<commit_before>var fs = require('fs');
var path = require('path');
var Q = require('q');
var debounce = require('debounce');
var utils = require('./utils');
var child_process = require('child_process');

// Returns a promise.
module.exports = exports = function push(target, watch) {
  var argsAsJson = JSON.stringify([].slice.call(arguments));
  return checkFileHandleLimit()
  .then(function(fileHandleLimit) {
    if (fileHandleLimit < 10000 && watch) {
      return relaunchWithBiggerUlimit(argsAsJson);
    }
    target = !target || Array.isArray(target) ? target : [target];
    var ret = Q.when(target);
    if (!target) {
      ret = extractTargets();
    }
    return ret.then(function(targets) {
      return createSession(targets);
    }).then(function(session) {
      return pushAll(session.clientInfos)
      .then(function() {
        if (watch) {
          return watchFiles(session);
        }
      });
    });
  });
};

function extractTargets() {
  // TODO: Use adbkit for smarter auto-detection.
  return Q.when(['localhost:2424']);
}

function createSession(targets) {
  var PushClient = require('chrome-app-developer-tool-client');

  var i = 0;
  var ret = {
    platforms: [],
    clientInfos: [],
    appType: null
  };

  var chromeAppPushRoot = null;
  if (fs.existsSync('www/manifest.json')) {
    chromeAppPushRoot = 'www';
  } else if (fs.existsSync('manifest.json')) {
    chromeAppPushRoot = '.';
  }
  ret.appType = chromeAppPushRoot ? 'chrome' : 'cordova';

  var platformMap = Object.create(null);
  function createClient() {
    var target = targets[i++];
    if (!target) {
      ret.platforms = Object.keys(platformMap);
      return ret;
    }
    var newClient = new PushClient(target);
    return newClient.info()
    .then(function(response) {
      var infoJson = response.body;
      var platform = infoJson['platform'];
      platformMap[platform] = true;
      if (ret.appType == 'chrome') {
        ret.clientInfos.push({
            platform: platform,
            client: newClient,
            pushSession: newClient.createPushSession(chromeAppPushRoot),
            watchDir: chromeAppPushRoot
        });
      } else {
        // Vanilla Cordova project.
        var wwwDir = utils.assetDirForPlatform(platform);
        ret.clientInfos.push({
            platform: platform,
            client: newClient,
            pushSession: newClient.createPushSession('.'), // push client figures out to use platforms/
            watchDir: wwwDir
        });
      }
      return createClient();
    }, function(error) {
      if (error.code === 'ECONNREFUSED') {
        console.warn();
        console.warn('Could not connect to device at ' + target);
        console.warn('For a USB connected Android device, try running: adb forward tcp:2424 tcp:2424');
        console.warn('For a networked device, use --target=DEVICE_IP_ADDRESS');
      } else if (error.code === 'ECONNRESET') {
        console.warn('\nPlease make sure that the Chrome App Developer Tool for Mobile is running on your device.');
      } else {
        console.error(error);
      }
      process.exit();
    });
  }
  return createClient();
}

var pushInProgress = false;
var pushAgainWhenDone = false;
function pushAll(clientInfos) {
  if (pushInProgress) {
    pushAgainWhenDone = true;
    return Q();
  }
  pushInProgress = true;
  var allPromises = clientInfos.map(function(clientInfo) {
    try {
      return clientInfo.pushSession.push();
    } catch (e) {
      if (/Not a Cordova project/.test(e)) {
        console.warn('Please navigate to a Chrome App or Cordova project, and then try pushing again.');
      } else {
        console.error(e);
      }
      process.exit();
    }
  });
  return Q.all(allPromises)
  .then(function() {
    if (pushAgainWhenDone) {
      return process.nextTick(function() {
        pushAll(clientInfos).done();
      });
    }
    pushInProgress = false;
    pushAgainWhenDone = false;
  }, function(e) {
    pushInProgress = false;
    pushAgainWhenDone = false;
    throw e;
  });
}

var debouncedPushAll = debounce(function(clientInfos) {
  pushAll(clientInfos).done();
}, 50, false);

function watchFiles(session) {
  var gaze = require('gaze');
  var deferred = Q.defer();

  // TODO: This doesn't work for vanilla cordova apps + multiple platforms.
  var watchDir = session.clientInfos[0].watchDir;
  // Note: gaze doesn't work well with ./ prefix nor with absolute paths
  // (https://github.com/gruntjs/grunt-contrib-watch/issues/166)
  // path.join() is smart enough to remove the ./ prefix.
  gaze(path.join(watchDir, '**', '*'), function(err, watcher) {
    console.log('Watching for changes.');
    watcher.on('all', function(event, filepath) {
      debouncedPushAll(session.clientInfos);
    });
  });
  // TODO: Capture Ctrl-C and resolve the promise.
  return deferred.promise;
}

function checkFileHandleLimit() {
  var deferred = Q.defer();
  if (process.platform != 'win32' && process.env['SHELL']) {
    // gaze opens a lot of file handles, and the default on OS X is too small (EMFILE exceptions).
    // NOTE: This is fixed in node 0.11, and is a problem only on node 0.10.
    child_process.exec('ulimit -n', function(error, stdout, stderr) {
      var curLimit = !error && +stdout;
      deferred.resolve(curLimit || Infinity);
    });
  } else {
    deferred.resolve(Infinity);
  }
  return deferred.promise;
}

function relaunchWithBiggerUlimit(argsAsJson) {
  // re-run with the new ulimit
  var deferred = Q.defer();
  var args = ['-c', 'ulimit -n 10240; exec "' + process.argv[0] + '" "' + __filename + '" "' + argsAsJson.replace(/"/g, '@') + '"'];
  var child = child_process.spawn(process.env['SHELL'], args, { stdio: 'inherit' });
  child.on('close', function(code) {
    if (code) {
      deferred.reject(code);
    } else {
      deferred.resolve(code);
    }
  });
  return deferred.promise;
}

// Gets called from EMFILE re-launch
if (require.main === module) {
  exports.apply(this, JSON.parse(process.argv[2].replace(/@/g, '"'))).done();
}
<commit_msg>Fix push --watch not noticing changes that occurred during a push (fixes #264)<commit_after>var fs = require('fs');
var path = require('path');
var Q = require('q');
var debounce = require('debounce');
var utils = require('./utils');
var child_process = require('child_process');

// Returns a promise.
module.exports = exports = function push(target, watch) {
  var argsAsJson = JSON.stringify([].slice.call(arguments));
  return checkFileHandleLimit()
  .then(function(fileHandleLimit) {
    if (fileHandleLimit < 10000 && watch) {
      return relaunchWithBiggerUlimit(argsAsJson);
    }
    target = !target || Array.isArray(target) ? target : [target];
    var ret = Q.when(target);
    if (!target) {
      ret = extractTargets();
    }
    return ret.then(function(targets) {
      return createSession(targets);
    }).then(function(session) {
      return pushAll(session.clientInfos)
      .then(function() {
        if (watch) {
          return watchFiles(session);
        }
      });
    });
  });
};

function extractTargets() {
  // TODO: Use adbkit for smarter auto-detection.
  return Q.when(['localhost:2424']);
}

function createSession(targets) {
  var PushClient = require('chrome-app-developer-tool-client');

  var i = 0;
  var ret = {
    platforms: [],
    clientInfos: [],
    appType: null
  };

  var chromeAppPushRoot = null;
  if (fs.existsSync('www/manifest.json')) {
    chromeAppPushRoot = 'www';
  } else if (fs.existsSync('manifest.json')) {
    chromeAppPushRoot = '.';
  }
  ret.appType = chromeAppPushRoot ? 'chrome' : 'cordova';

  var platformMap = Object.create(null);
  function createClient() {
    var target = targets[i++];
    if (!target) {
      ret.platforms = Object.keys(platformMap);
      return ret;
    }
    var newClient = new PushClient(target);
    return newClient.info()
    .then(function(response) {
      var infoJson = response.body;
      var platform = infoJson['platform'];
      platformMap[platform] = true;
      if (ret.appType == 'chrome') {
        ret.clientInfos.push({
            platform: platform,
            client: newClient,
            pushSession: newClient.createPushSession(chromeAppPushRoot),
            watchDir: chromeAppPushRoot
        });
      } else {
        // Vanilla Cordova project.
        var wwwDir = utils.assetDirForPlatform(platform);
        ret.clientInfos.push({
            platform: platform,
            client: newClient,
            pushSession: newClient.createPushSession('.'), // push client figures out to use platforms/
            watchDir: wwwDir
        });
      }
      return createClient();
    }, function(error) {
      if (error.code === 'ECONNREFUSED') {
        console.warn();
        console.warn('Could not connect to device at ' + target);
        console.warn('For a USB connected Android device, try running: adb forward tcp:2424 tcp:2424');
        console.warn('For a networked device, use --target=DEVICE_IP_ADDRESS');
      } else if (error.code === 'ECONNRESET') {
        console.warn('\nPlease make sure that the Chrome App Developer Tool for Mobile is running on your device.');
      } else {
        console.error(error);
      }
      process.exit();
    });
  }
  return createClient();
}

var pushInProgress = false;
var pushAgainWhenDone = false;
function pushAll(clientInfos) {
  if (pushInProgress) {
    pushAgainWhenDone = true;
    return Q();
  }
  pushInProgress = true;
  var allPromises = clientInfos.map(function(clientInfo) {
    try {
      return clientInfo.pushSession.push();
    } catch (e) {
      if (/Not a Cordova project/.test(e)) {
        console.warn('Please navigate to a Chrome App or Cordova project, and then try pushing again.');
      } else {
        console.error(e);
      }
      process.exit();
    }
  });
  return Q.all(allPromises)
  .then(function() {
    if (pushAgainWhenDone) {
      process.nextTick(function() {
        pushAll(clientInfos).done();
      });
    }
    pushInProgress = false;
    pushAgainWhenDone = false;
  }, function(e) {
    pushInProgress = false;
    pushAgainWhenDone = false;
    throw e;
  });
}

var debouncedPushAll = debounce(function(clientInfos) {
  pushAll(clientInfos).done();
}, 50, false);

function watchFiles(session) {
  var gaze = require('gaze');
  var deferred = Q.defer();

  // TODO: This doesn't work for vanilla cordova apps + multiple platforms.
  var watchDir = session.clientInfos[0].watchDir;
  // Note: gaze doesn't work well with ./ prefix nor with absolute paths
  // (https://github.com/gruntjs/grunt-contrib-watch/issues/166)
  // path.join() is smart enough to remove the ./ prefix.
  gaze(path.join(watchDir, '**', '*'), function(err, watcher) {
    console.log('Watching for changes.');
    watcher.on('all', function(event, filepath) {
      debouncedPushAll(session.clientInfos);
    });
  });
  // TODO: Capture Ctrl-C and resolve the promise.
  return deferred.promise;
}

function checkFileHandleLimit() {
  var deferred = Q.defer();
  if (process.platform != 'win32' && process.env['SHELL']) {
    // gaze opens a lot of file handles, and the default on OS X is too small (EMFILE exceptions).
    // NOTE: This is fixed in node 0.11, and is a problem only on node 0.10.
    child_process.exec('ulimit -n', function(error, stdout, stderr) {
      var curLimit = !error && +stdout;
      deferred.resolve(curLimit || Infinity);
    });
  } else {
    deferred.resolve(Infinity);
  }
  return deferred.promise;
}

function relaunchWithBiggerUlimit(argsAsJson) {
  // re-run with the new ulimit
  var deferred = Q.defer();
  var args = ['-c', 'ulimit -n 10240; exec "' + process.argv[0] + '" "' + __filename + '" "' + argsAsJson.replace(/"/g, '@') + '"'];
  var child = child_process.spawn(process.env['SHELL'], args, { stdio: 'inherit' });
  child.on('close', function(code) {
    if (code) {
      deferred.reject(code);
    } else {
      deferred.resolve(code);
    }
  });
  return deferred.promise;
}

// Gets called from EMFILE re-launch
if (require.main === module) {
  exports.apply(this, JSON.parse(process.argv[2].replace(/@/g, '"'))).done();
}
<|endoftext|>


<commit_before>if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(["require","../deep", "../deep-unit"], function (require, deep, Unit) {

    //_______________________________________________________________ GENERIC STORE TEST CASES

    var unit = {
        title:"deep/units/compositions",
        stopOnError:false,
        setup:function(){
            return {
                a : {
                    func1:function(){
                        //console.log("func1");
                        this.res.push("func1");
                    },
                    func2:deep.compose.before(function(){
                        //console.log("func2");
                        this.res.push("func2");
                    }),
                    func3:deep.compose.after(function(){
                        //console.log("func3");
                        this.res.push("func3");
                    })
                },
                b : {
                    res:[],
                    func1:deep.compose.after(function(){
                        // console.log("func1_1");
                        this.res.push("func1_1");
                    }),
                    func2:deep.compose.after(function(){
                        // console.log("func2_2");
                        this.res.push("func2_2");
                    }),
                    func3:deep.compose.after(function(){
                        // console.log("func3_3");
                        this.res.push("func3_3");
                    })
                },
                c : {
                    res:[],
                    func1:deep.compose.after(function(){
                        // console.log("func1_1_c");
                        this.res.push("func1_1_c");
                    }),
                    func2:deep.compose.after(function(){
                        // console.log("func2_2_c");
                        this.res.push("func2_2_c");
                    }),
                    func3:deep.compose.after(function(){
                        // console.log("func3_3_c");
                        this.res.push("func3_3_c");
                    })
                }
            };
        },
        tests : {
            a:function(){
                return deep(this.b)
                .bottom(this.a)
                .query("/(func.*)")
                .run()
                .query("/res")
                .equal(["func1","func1_1", "func2","func2_2","func3","func3_3"]);

            },
            b:function(){
                return deep(this.c)
                .bottom(this.a)
                .query("./(func.*)")
                .run()
                .query("/res")
                .equal(["func1","func1_1_c", "func2","func2_2_c","func3","func3_3_c"]);
            },
            after:function(){
                var closure = { test:"" };
                var a = {
                    test:function(){
                        closure.test += "hello test";
                        return closure.test;
                    }
                };
                var b = {
                    test:deep.compose.after(function(){
                        closure.test += " : after";
                        return closure.test;
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal("hello test : after");
            },
            delayed_after:function(){
                var closure = { test:"" };
                var a = {
                    test:function(){
                        closure.test += "hello test";
                        return deep.when(closure.test).delay(2);
                    }
                };
                var b = {
                    test:deep.compose.after(function(){
                        closure.test += " : after";
                        return deep.when(closure.test).delay(1);
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal( "hello test : after" );
            },
            around:function(){
                var a = {
                    test:function(){
                        return "test";
                    }
                };
                var b = {
                    test:deep.compose.around(function(old){
                        return function(){
                            return "hello "+ old.apply(this) + " around";
                        };
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal("hello test around");
            },
            before:function(){
                var closure = { test:"" };
                var a = {
                    test:function(){
                        closure.test += "hello test";
                    }
                };
                var b = {
                    test:deep.compose.before(function(){
                        closure.test += "before : ";
                    })
                };
                deep.utils.up(b,a);
                a.test();
                return deep(closure.test)
                .equal("before : hello test");
            },
            delayed_before:function(){
                var closure = { test:"" };
                var a = {
                    test:function(){
                        closure.test += "hello test";
                        return deep(closure.test).delay(1);
                    }
                };
                var b = {
                    test:deep.compose.before(function(){
                        closure.test += "before : ";
                        return deep(closure.test).delay(1);
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal("before : hello test");
            },
            parallele:function(){
                var a = {
                    test:function(){
                        return "hello test";
                    }
                };
                var b = {
                    test:deep.compose.parallele(function(){
                        return "hello parallele";
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal(["hello parallele", "hello test"]);
            },
            delayed_parallele:function(){
                var a = {
                    test:function(){
                        return deep.when("hello test").delay(2);
                    }
                };
                var b = {
                    test:deep.compose.parallele(function(){
                        return deep.when("hello parallele").delay(3);
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal(["hello parallele", "hello test"]);
            },
            before_alone:function(){
                var a = {
                    test:deep.compose.before(function(){
                        return "hello";
                    })
                };
                return deep.when(a.test()).equal("hello");
            },
            before_alone_with_arg:function(){
                var a = {
                    test:deep.compose.before(function(arg){
                        return "hello";
                    })
                };
                return deep.when(a.test()).equal("hello");
            },
            before_replace_arg:function(){
                var b = {
                    test:function(arg){
                       return "hello : "+arg;
                    }
                };
                var a = {
                    test:deep.compose.before(function(arg){
                       return "weee";
                    })
                };

                deep.utils.bottom(b, a);

                return deep.when(a.test("bloup")).equal("hello : weee");
            },
             before_dont_replace_arg:function(){
                var b = {
                    test:function(arg){
                       return "hello : "+arg;
                    }
                };
                var a = {
                    test:deep.compose.before(function(arg){
                       // return nothing so arg are forwarded
                    })
                };

                deep.utils.bottom(b, a);

                return deep.when(a.test("bloup")).equal("hello : bloup");
            },
            after_forward_result : function(){
                var b = {
                    test:function(arg){
                       return "hello : "+arg;
                    }
                };
                var a = {
                    test:deep.compose.after(function(arg){
                       // return nothing so result are not changed
                    })
                };

                deep.utils.bottom(b, a);

                return deep.when(a.test("bloup")).equal("hello : bloup");
            },
            after_receive_forwarded_arg:function(){
                var b = {
                    test:function(arg){
                       return "hello : "+arg;
                    }
                };
                var a = {
                    test:deep.compose.after(function(arg){
                        return arg+"!";
                    })
                };

                deep.utils.bottom(b, a);

                return deep.when(a.test("bloup")).equal("hello : bloup!");
            }

        }
    };

    return new Unit(unit);
});
<commit_msg>add testcases<commit_after>if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(["require","../deep", "../deep-unit"], function (require, deep, Unit) {

    //_______________________________________________________________ GENERIC STORE TEST CASES

    var unit = {
        title:"deep/units/compositions",
        stopOnError:false,
        setup:function(){
            return {
                a : {
                    func1:function(){
                        //console.log("func1");
                        this.res.push("func1");
                    },
                    func2:deep.compose.before(function(){
                        //console.log("func2");
                        this.res.push("func2");
                    }),
                    func3:deep.compose.after(function(){
                        //console.log("func3");
                        this.res.push("func3");
                    })
                },
                b : {
                    res:[],
                    func1:deep.compose.after(function(){
                        // console.log("func1_1");
                        this.res.push("func1_1");
                    }),
                    func2:deep.compose.after(function(){
                        // console.log("func2_2");
                        this.res.push("func2_2");
                    }),
                    func3:deep.compose.after(function(){
                        // console.log("func3_3");
                        this.res.push("func3_3");
                    })
                },
                c : {
                    res:[],
                    func1:deep.compose.after(function(){
                        // console.log("func1_1_c");
                        this.res.push("func1_1_c");
                    }),
                    func2:deep.compose.after(function(){
                        // console.log("func2_2_c");
                        this.res.push("func2_2_c");
                    }),
                    func3:deep.compose.after(function(){
                        // console.log("func3_3_c");
                        this.res.push("func3_3_c");
                    })
                }
            };
        },
        tests : {
            a:function(){
                return deep(this.b)
                .bottom(this.a)
                .query("/(func.*)")
                .run()
                .query("/res")
                .equal(["func1","func1_1", "func2","func2_2","func3","func3_3"]);

            },
            b:function(){
                return deep(this.c)
                .bottom(this.a)
                .query("./(func.*)")
                .run()
                .query("/res")
                .equal(["func1","func1_1_c", "func2","func2_2_c","func3","func3_3_c"]);
            },
            after:function(){
                var closure = { test:"" };
                var a = {
                    test:function(){
                        closure.test += "hello test";
                        return closure.test;
                    }
                };
                var b = {
                    test:deep.compose.after(function(){
                        closure.test += " : after";
                        return closure.test;
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal("hello test : after");
            },
            delayed_after:function(){
                var closure = { test:"" };
                var a = {
                    test:function(){
                        closure.test += "hello test";
                        return deep.when(closure.test).delay(2);
                    }
                };
                var b = {
                    test:deep.compose.after(function(){
                        closure.test += " : after";
                        return deep.when(closure.test).delay(1);
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal( "hello test : after" );
            },
            around:function(){
                var a = {
                    test:function(){
                        return "test";
                    }
                };
                var b = {
                    test:deep.compose.around(function(old){
                        return function(){
                            return "hello "+ old.apply(this) + " around";
                        };
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal("hello test around");
            },
            before:function(){
                var closure = { test:"" };
                var a = {
                    test:function(){
                        closure.test += "hello test";
                    }
                };
                var b = {
                    test:deep.compose.before(function(){
                        closure.test += "before : ";
                    })
                };
                deep.utils.up(b,a);
                a.test();
                return deep(closure.test)
                .equal("before : hello test");
            },
            delayed_before:function(){
                var closure = { test:"" };
                var a = {
                    test:function(){
                        closure.test += "hello test";
                        return deep(closure.test).delay(1);
                    }
                };
                var b = {
                    test:deep.compose.before(function(){
                        closure.test += "before : ";
                        return deep(closure.test).delay(1);
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal("before : hello test");
            },
            parallele:function(){
                var a = {
                    test:function(){
                        return "hello test";
                    }
                };
                var b = {
                    test:deep.compose.parallele(function(){
                        return "hello parallele";
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal(["hello parallele", "hello test"]);
            },
            delayed_parallele:function(){
                var a = {
                    test:function(){
                        return deep.when("hello test").delay(2);
                    }
                };
                var b = {
                    test:deep.compose.parallele(function(){
                        return deep.when("hello parallele").delay(3);
                    })
                };
                deep.utils.up(b,a);
                return deep.when(a.test())
                .equal(["hello parallele", "hello test"]);
            },
            before_alone:function(){
                var a = {
                    test:deep.compose.before(function(){
                        return "hello";
                    })
                };
                return deep.when(a.test()).equal("hello");
            },
            before_alone_with_arg:function(){
                var a = {
                    test:deep.compose.before(function(arg){
                        return "hello";
                    })
                };
                return deep.when(a.test()).equal("hello");
            },
            before_replace_arg:function(){
                var b = {
                    test:function(arg){
                       return "hello : "+arg;
                    }
                };
                var a = {
                    test:deep.compose.before(function(arg){
                       return "weee";
                    })
                };

                deep.utils.bottom(b, a);

                return deep.when(a.test("bloup")).equal("hello : weee");
            },
             before_dont_replace_arg:function(){
                var b = {
                    test:function(arg){
                       return "hello : "+arg;
                    }
                };
                var a = {
                    test:deep.compose.before(function(arg){
                       // return nothing so arg are forwarded
                    })
                };

                deep.utils.bottom(b, a);

                return deep.when(a.test("bloup")).equal("hello : bloup");
            },
            after_forward_result : function(){
                var b = {
                    test:function(arg){
                       return "hello : "+arg;
                    }
                };
                var a = {
                    test:deep.compose.after(function(arg){
                       // return nothing so result are not changed
                    })
                };

                deep.utils.bottom(b, a);

                return deep.when(a.test("bloup")).equal("hello : bloup");
            },
            after_receive_forwarded_arg:function(){
                var b = {
                    test:function(arg){
                       return "hello : "+arg;
                    }
                };
                var a = {
                    test:deep.compose.after(function(arg){
                        return arg+"!";
                    })
                };

                deep.utils.bottom(b, a);
                return deep.when(a.test("bloup"))
                .equal("hello : bloup!");
            },
            argsBefore:function(){
                var checker = {};
                var a = {
                    test:function(arg1, arg2){
                        checker.fromA = [arg1, arg2];
                        return ["A1:"+arg1, "A2:"+arg2];
                    }
                };
                var b = {
                    test:deep.compose.before(function(arg1, arg2){
                        checker.fromB = [arg1, arg2];
                        return ["B1:"+arg1, "B2:"+arg2];
                    })
                };
                deep.utils.up(b,a);
                return deep(a.test("hello","world"))
                .equal(["A1:B1:hello", "A2:B2:world"])
                .deep(checker)
                .equal({
                    fromB:["hello","world"],
                    fromA:["B1:hello", "B2:world"]
                });
            },
            argsAfter:function(){
                var checker = {};
                var a = {
                    test:function(arg1, arg2){
                        checker.fromA = [arg1, arg2];
                        return ["A1:"+arg1, "A2:"+arg2];
                    }
                };
                var b = {
                    test:deep.compose.after(function(arg1, arg2){
                        checker.fromB = [arg1, arg2];
                        return ["B1:"+arg1, "B2:"+arg2];
                    })
                };
                deep.utils.up(b,a);
                return deep(a.test("hello","world"))
                .equal(["B1:A1:hello", "B2:A2:world"])
                .deep(checker)
                .equal({
                    fromA:["hello","world"],
                    fromB:["A1:hello", "A2:world"]
                });
            },
            argsAfterUndefined:function(){
                var checker = {};
                var a = {
                    test:function(arg1, arg2){
                        checker.fromA = [arg1, arg2];
                        //return ["A1:"+arg1, "A2:"+arg2];
                    }
                };
                var b = {
                    test:deep.compose.after(function(arg1, arg2){
                        checker.fromB = [arg1, arg2];
                        return ["B1:"+arg1, "B2:"+arg2];
                    })
                };
                deep.utils.up(b,a);
                return deep(a.test("hello","world"))
                .equal(["B1:hello", "B2:world"])
                .deep(checker)
                .equal({
                    fromA:["hello","world"],
                    fromB:["hello", "world"]
                });
            }

        }
    };

    return new Unit(unit);
});
<|endoftext|>


<commit_before>/* eslint-disable lodash/prefer-lodash-method */
import React from 'react';
import PropTypes from 'prop-types';
import {Text, View} from 'react-native';
import {filter, toNumber, isNil, sum, isNaN} from 'lodash';

import QuestionText from './QuestionText';
import styles from './styles';

const getTotal = (section, question, callback) => {
    const addends = filter(
        question.fieldsToAdd,
        field => !isNil(section[field])
    ).map(
        field => toNumber(section[field])
    );
    let total = sum(addends);
    if (isNaN(total)) {
        total = 0;
    }
    callback({[question.name]: total});
    return total;
};

const Total = ({section, question, onChange}) => (
    <View style={styles.rowContainer}>
        <QuestionText question={question}/>
        <Text>{getTotal(section, question, onChange)}</Text>
    </View>
);

Total.propTypes = {
    section: PropTypes.shape({}).isRequired,
    question: PropTypes.shape({}).isRequired,
    onChange: PropTypes.func.isRequired
};

export default Total;
<commit_msg>fix(total): fix total callback (#19)<commit_after>/* eslint-disable lodash/prefer-lodash-method */
import React from 'react';
import PropTypes from 'prop-types';
import {Text, View} from 'react-native';
import {filter, toNumber, isNil, sum, isNaN} from 'lodash';

import QuestionText from './QuestionText';
import styles from './styles';

const getTotal = (section, question, callback) => {
    const addends = filter(
        question.fieldsToAdd,
        field => !isNil(section[field])
    ).map(
        field => toNumber(section[field])
    );
    let total = sum(addends);
    if (isNaN(total)) {
        total = 0;
    }

    if (section[question.name] === total) {
        return total;
    }

    callback({[question.name]: total});
    return total;
};

const Total = ({section, question, onChange}) => (
    <View style={styles.rowContainer}>
        <QuestionText question={question}/>
        <Text>{getTotal(section, question, onChange)}</Text>
    </View>
);

Total.propTypes = {
    section: PropTypes.shape({}).isRequired,
    question: PropTypes.shape({}).isRequired,
    onChange: PropTypes.func.isRequired
};

export default Total;
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import SVGIcon from './SVGIcon.js';

class Accordion extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      open: props.defaultExpanded
    };
  }
  render() {
    const { children, title, className, dataTestId } = this.props;
    const { open } = this.state;
    return (
      <div className={`sdc-accordion ${className}`}>

 this.setState({ open: !open })} className='sdc-accordion-header'>
          <SVGIcon name='chevronUp' iconClassName={open ? 'down' : ''} />

{title}

        <div className={`sdc-accordion-body ${open ? 'open' : ''}`}>{children}

    );
  }
}

Accordion.propTypes = {
  title: PropTypes.string,
  children: PropTypes.node,
  expandByDefault: PropTypes.bool,
  dataTestId: PropTypes.string
};

Accordion.defaultProps = {
  title: '',
  className: '',
  defaultExpanded: false
};

export default Accordion;
<commit_msg>accordion icon aligment<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import SVGIcon from './SVGIcon.js';

class Accordion extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      open: props.defaultExpanded
    };
  }
  render() {
    const { children, title, className, dataTestId } = this.props;
    const { open } = this.state;
    return (
      <div className={`sdc-accordion ${className}`}>

 this.setState({ open: !open })} className='sdc-accordion-header'>
          <SVGIcon name='chevronDown' iconClassName={open ? 'down' : ''} />

{title}

        <div className={`sdc-accordion-body ${open ? 'open' : ''}`}>{children}

    );
  }
}

Accordion.propTypes = {
  title: PropTypes.string,
  children: PropTypes.node,
  expandByDefault: PropTypes.bool,
  dataTestId: PropTypes.string
};

Accordion.defaultProps = {
  title: '',
  className: '',
  defaultExpanded: false
};

export default Accordion;
<|endoftext|>


<commit_before>import * as utils from 'base/utils';
import Reader from 'base/reader';

const cached = {};

const CSVReader = Reader.extend({

  QUERY_FROM_CONCEPTS: 'concepts',
  QUERY_FROM_DATAPOINTS: 'datapoints',
  QUERY_FROM_ENTITIES: 'entities',

  CONDITION_CALLBACKS: {
    $gt: (configValue, rowValue) => rowValue > configValue,
    $gte: (configValue, rowValue) => rowValue >= configValue,
    $lt: (configValue, rowValue) => rowValue < configValue,
    $lte: (configValue, rowValue) => rowValue <= configValue,
    $in: (configValue, rowValue) => configValue.includes(rowValue)
  },

  _name: 'csv',

  /**
   * Initializes the reader.
   * @param {Object} readerInfo Information about the reader
   */
  init(readerInfo) {
    this._data = [];
    this._basepath = readerInfo.path;
    this.delimiter = readerInfo.delimiter;
    this.keySize = readerInfo.keySize || 1;

    if (!this._basepath) {
      utils.error('Missing base path for csv reader');
    }
  },

  /**
   * Reads from source
   * @param {Object} query to be performed
   * @param parsers
   * @returns {Promise} a promise that will be resolved when data is read
   */
  read(query, parsers = {}) {
    query = this._normalizeQuery(query, parsers);

    const {
      select,
      from,
      order_by = []
    } = query;

    const [orderBy] = order_by;

    return this.load()
      .then((data) => {
        switch (true) {
          case from === this.QUERY_FROM_CONCEPTS:
            return this._getConcepts(data);

          case this._isDataQuery(from) && select.key.length > 0:
            return data
              .reduce(this._applyQuery(query, parsers), [])
              .sort((prev, next) => prev[orderBy] - next[orderBy]);

          default:
            return [];
        }
      });
  },

  /**
   * This function returns info about the dataset
   * in case of CSV reader it's just the name of the file
   * @returns {object} object of info about the dataset
   */
  getDatasetInfo() {
    return { name: this._basepath.split('/').pop() };
  },

  load() {
    const { _basepath: path, delimiter } = this;

    return new Promise((resolve, reject) => {
      const cachedData = cached[path];

      if (cachedData) {
        resolve(cachedData);

      } else {
        d3.csv(path).get((error, result) => {
          if (!result) {
            return reject(`No permissions or empty file: ${path}. ${error}`);
          }

          if (error) {
            return reject(`Error happened while loading csv file: ${path}. ${error}`);
          }

          const [firstRow] = result;
          const isNotUndefined = (v) => v !== undefined;
          const itemsInLine1 = utils.keys(firstRow).filter(isNotUndefined).length;
          const itemsInLine2 = utils.values(firstRow).filter(isNotUndefined).length;

          const semicolonsInLine1 = utils.keys(firstRow).join('').split(';').length;
          const semicolonsInLine2 = utils.values(firstRow).join('').split(';').length;

          if (itemsInLine1 === itemsInLine2
            && itemsInLine1 > 1 && itemsInLine2 > 1
            && itemsInLine1 > semicolonsInLine1 && itemsInLine1 > semicolonsInLine2
            && (!delimiter || delimiter === ',')
          ) {

            //comma is indeed the delimiter!
            cached[path] = result;
            resolve(result);

          } else {

            //something else is a delimiter. assume semicolon
            d3.dsv(delimiter || ';', 'text/plain')(path).get((error, result) => {
              cached[path] = result;
              resolve(result);
            });

          }
        });
      }
    });
  },

  _normalizeQuery(_query, parsers) {
    const query = Object.assign({}, _query);
    const { where, join } = query;

    if (where.$and) {
      where.$and = where.$and.reduce((whereResult, condition) => {
        Object.keys(condition).forEach(rowKey => {
          const conditionValue = condition[rowKey];

          if (typeof conditionValue === 'string' && conditionValue.startsWith('$')) {
            const joinWhere = join[conditionValue].where;

            Object.keys(joinWhere)
              .forEach(joinRowKey => {
                const value = joinWhere[joinRowKey];
                const parser = parsers[joinRowKey];

                whereResult[joinRowKey] = parser ?
                  typeof value === 'object' ?
                    Object.keys(value).reduce((callbackConditions, callbackKey) => {
                      callbackConditions[callbackKey] = parser(value[callbackKey]);
                      return callbackConditions;
                    }, {}) :
                    parser(value)
                  : value;
              });
          } else {
            const parser = parsers[rowKey];
            whereResult[rowKey] = parser ? parser(conditionValue) : conditionValue;
          }
        });

        return whereResult;
      }, {});
    }

    return query;
  },

  _isDataQuery(from) {
    return [
      this.QUERY_FROM_DATAPOINTS,
      this.QUERY_FROM_ENTITIES
    ].includes(from);
  },

  _getRowMapper(query, parsers) {
    const { select } = query;

    return (row) => {
      let correct = true;

      const result = Object.keys(row).reduce((result, key) => {
        if (correct) {
          const value = row[key];
          const parser = parsers[key];
          let resultValue;

          if (parser) {
            resultValue = parser(value);
          } else {
            const numeric = parseFloat(value);
            resultValue = !isNaN(numeric) && isFinite(numeric) ? parseFloat(value.replace(',', '.')) : value;
          }

          if (!resultValue && resultValue !== 0) {
            if (parser && resultValue === null) {
              throw new Error(`Data format is wrong. Can't read "${key}" column.`);
            } else if (select.key.includes(key)) {
              correct = false;
            }
          } else {
            result[key] = resultValue;
          }
        }

        const resultValue = result[key];
        if (!resultValue && resultValue !== 0) {
          throw new Error(`Data format is wrong. Can't read "${key}" column.`);
        }

        return result;
      }, {});

      return correct && result;
    };
  },

  _getConcepts(data) {
    const [firstRow] = data;

    return Object.keys(firstRow).map((concept, index) => {
      const result = { concept };
      // TODO: is the order of first/last elements stable?
      // first columns are expected to have keys
      if (index < this.keySize) {
        result.concept_type = 'entity_domain';
      } else if (index === this.keySize) {
        //the column after is expected to have time
        result.concept_type = 'time';
      } else {
        result.concept_type = 'measure';

        for (let i = data.length - 1; i >= 0; i--) {
          if (utils.isString(data[i][concept]) && data[i][concept] !== '') {
            result.concept_type = 'entity_set';
            result.domain = Object.keys(firstRow)[0];
            break;
          }
        }
      }

      return result;
    })
  },

  _applyQuery(query, parsers) {
    const {
      select,
      from
    } = query;

    const [uniqueKey] = select.key;
    const uniqueValues = [];
    const mapRow = this._getRowMapper(query, parsers);

    return (result, row) => {
      row = mapRow(row);

      if (row) {
        const unique = row[uniqueKey];
        const isUnique = from !== this.QUERY_FROM_ENTITIES || !uniqueValues.includes(unique);
        const isSuitable = this._isSuitableRow(query, row);

        if (isSuitable && isUnique) {
          if (from === this.QUERY_FROM_ENTITIES) {
            uniqueValues.push(unique);
          }

          const rowFilteredByKeys = Object.keys(row)
            .reduce((resultRow, rowKey) => {
              if (select.key.includes(rowKey) || select.value.includes(rowKey)) {
                resultRow[rowKey] = row[rowKey];
              }

              return resultRow;
            }, {});

          result.push(rowFilteredByKeys);
        }
      }

      return result;
    };
  },

  _isSuitableRow(query, row) {
    const { where } = query;

    return !where.$and ||
      Object.keys(where.$and).every(conditionKey => {
        const condition = where.$and[conditionKey];
        const rowValue = row[conditionKey];

        // if the column is missing, then don't apply filter
        return rowValue === undefined
          || (typeof condition !== 'object' ?
            (rowValue === condition
              //resolve booleans via strings
              || condition === true && utils.isString(rowValue) && rowValue.toLowerCase().trim() === 'true'
              || condition === false && utils.isString(rowValue) && rowValue.toLowerCase().trim() === 'false'
            ) :
            Object.keys(condition).every(callbackKey =>
              this.CONDITION_CALLBACKS[callbackKey](condition[callbackKey], rowValue)
            ));
      });
  }

});

export default CSVReader;
<commit_msg>Fix the incorrect previous merge<commit_after>import * as utils from 'base/utils';
import Reader from 'base/reader';

const cached = {};

const CSVReader = Reader.extend({

  QUERY_FROM_CONCEPTS: 'concepts',
  QUERY_FROM_DATAPOINTS: 'datapoints',
  QUERY_FROM_ENTITIES: 'entities',

  CONDITION_CALLBACKS: {
    $gt: (configValue, rowValue) => rowValue > configValue,
    $gte: (configValue, rowValue) => rowValue >= configValue,
    $lt: (configValue, rowValue) => rowValue < configValue,
    $lte: (configValue, rowValue) => rowValue <= configValue,
    $in: (configValue, rowValue) => configValue.includes(rowValue)
  },

  _name: 'csv',

  /**
   * Initializes the reader.
   * @param {Object} readerInfo Information about the reader
   */
  init(readerInfo) {
    this._data = [];
    this._basepath = readerInfo.path;
    this.delimiter = readerInfo.delimiter;
    this.keySize = readerInfo.keySize || 1;

    if (!this._basepath) {
      utils.error('Missing base path for csv reader');
    }
  },

  /**
   * Reads from source
   * @param {Object} query to be performed
   * @param parsers
   * @returns {Promise} a promise that will be resolved when data is read
   */
  read(query, parsers = {}) {
    query = this._normalizeQuery(query, parsers);

    const {
      select,
      from,
      order_by = []
    } = query;

    const [orderBy] = order_by;

    return this.load()
      .then((data) => {
        switch (true) {
          case from === this.QUERY_FROM_CONCEPTS:
            return this._getConcepts(data);

          case this._isDataQuery(from) && select.key.length > 0:
            return data
              .reduce(this._applyQuery(query, parsers), [])
              .sort((prev, next) => prev[orderBy] - next[orderBy]);

          default:
            return [];
        }
      });
  },

  /**
   * This function returns info about the dataset
   * in case of CSV reader it's just the name of the file
   * @returns {object} object of info about the dataset
   */
  getDatasetInfo() {
    return { name: this._basepath.split('/').pop() };
  },

  load() {
    const { _basepath: path, delimiter } = this;

    return new Promise((resolve, reject) => {
      const cachedData = cached[path];

      if (cachedData) {
        resolve(cachedData);

      } else {
        d3.csv(path).get((error, result) => {
          if (!result) {
            return reject(`No permissions or empty file: ${path}. ${error}`);
          }

          if (error) {
            return reject(`Error happened while loading csv file: ${path}. ${error}`);
          }

          const [firstRow] = result;
          const isNotUndefined = (v) => v !== undefined;
          const itemsInLine1 = utils.keys(firstRow).filter(isNotUndefined).length;
          const itemsInLine2 = utils.values(firstRow).filter(isNotUndefined).length;

          const semicolonsInLine1 = utils.keys(firstRow).join('').split(';').length;
          const semicolonsInLine2 = utils.values(firstRow).join('').split(';').length;

          if (itemsInLine1 === itemsInLine2
            && itemsInLine1 > 1 && itemsInLine2 > 1
            && itemsInLine1 > semicolonsInLine1 && itemsInLine1 > semicolonsInLine2
            && (!delimiter || delimiter === ',')
          ) {

            //comma is indeed the delimiter!
            cached[path] = result;
            resolve(result);

          } else {

            //something else is a delimiter. assume semicolon
            d3.dsv(delimiter || ';', 'text/plain')(path).get((error, result) => {
              cached[path] = result;
              resolve(result);
            });

          }
        });
      }
    });
  },

  _normalizeQuery(_query, parsers) {
    const query = Object.assign({}, _query);
    const { where, join } = query;

    if (where.$and) {
      where.$and = where.$and.reduce((whereResult, condition) => {
        Object.keys(condition).forEach(rowKey => {
          const conditionValue = condition[rowKey];

          if (typeof conditionValue === 'string' && conditionValue.startsWith('$')) {
            const joinWhere = join[conditionValue].where;

            Object.keys(joinWhere)
              .forEach(joinRowKey => {
                const value = joinWhere[joinRowKey];
                const parser = parsers[joinRowKey];

                whereResult[joinRowKey] = parser ?
                  typeof value === 'object' ?
                    Object.keys(value).reduce((callbackConditions, callbackKey) => {
                      callbackConditions[callbackKey] = parser(value[callbackKey]);
                      return callbackConditions;
                    }, {}) :
                    parser(value)
                  : value;
              });
          } else {
            const parser = parsers[rowKey];
            whereResult[rowKey] = parser ? parser(conditionValue) : conditionValue;
          }
        });

        return whereResult;
      }, {});
    }

    return query;
  },

  _isDataQuery(from) {
    return [
      this.QUERY_FROM_DATAPOINTS,
      this.QUERY_FROM_ENTITIES
    ].includes(from);
  },

  _getRowMapper(query, parsers) {
    const { select } = query;

    return (row) => {
      let correct = true;

      const result = Object.keys(row).reduce((result, key) => {
        if (correct) {
          const value = row[key];
          const parser = parsers[key];
          let resultValue;

          if (parser) {
            resultValue = parser(value);
          } else {
            const numeric = parseFloat(value);
            resultValue = !isNaN(numeric) && isFinite(numeric) ? parseFloat(value.replace(',', '.')) : value;
          }

          if (!resultValue && resultValue !== 0) {
            if (parser && resultValue === null) {
              throw new Error(`Data format is wrong. Can't read "${key}" column.`);
            } else if (select.key.includes(key)) {
              correct = false;
            }
          } else {
            result[key] = resultValue;
          }
        }

        return result;
      }, {});

      return correct && result;
    };
  },

  _getConcepts(data) {
    const [firstRow] = data;

    return Object.keys(firstRow).map((concept, index) => {
      const result = { concept };
      // TODO: is the order of first/last elements stable?
      // first columns are expected to have keys
      if (index < this.keySize) {
        result.concept_type = 'entity_domain';
      } else if (index === this.keySize) {
        //the column after is expected to have time
        result.concept_type = 'time';
      } else {
        result.concept_type = 'measure';

        for (let i = data.length - 1; i >= 0; i--) {
          if (utils.isString(data[i][concept]) && data[i][concept] !== '') {
            result.concept_type = 'entity_set';
            result.domain = Object.keys(firstRow)[0];
            break;
          }
        }
      }

      return result;
    })
  },

  _applyQuery(query, parsers) {
    const {
      select,
      from
    } = query;

    const [uniqueKey] = select.key;
    const uniqueValues = [];
    const mapRow = this._getRowMapper(query, parsers);

    return (result, row) => {
      row = mapRow(row);

      if (row) {
        const unique = row[uniqueKey];
        const isUnique = from !== this.QUERY_FROM_ENTITIES || !uniqueValues.includes(unique);
        const isSuitable = this._isSuitableRow(query, row);

        if (isSuitable && isUnique) {
          if (from === this.QUERY_FROM_ENTITIES) {
            uniqueValues.push(unique);
          }

          const rowFilteredByKeys = Object.keys(row)
            .reduce((resultRow, rowKey) => {
              if (select.key.includes(rowKey) || select.value.includes(rowKey)) {
                resultRow[rowKey] = row[rowKey];
              }

              return resultRow;
            }, {});

          result.push(rowFilteredByKeys);
        }
      }

      return result;
    };
  },

  _isSuitableRow(query, row) {
    const { where } = query;

    return !where.$and ||
      Object.keys(where.$and).every(conditionKey => {
        const condition = where.$and[conditionKey];
        const rowValue = row[conditionKey];

        // if the column is missing, then don't apply filter
        return rowValue === undefined
          || (typeof condition !== 'object' ?
            (rowValue === condition
              //resolve booleans via strings
              || condition === true && utils.isString(rowValue) && rowValue.toLowerCase().trim() === 'true'
              || condition === false && utils.isString(rowValue) && rowValue.toLowerCase().trim() === 'false'
            ) :
            Object.keys(condition).every(callbackKey =>
              this.CONDITION_CALLBACKS[callbackKey](condition[callbackKey], rowValue)
            ));
      });
  }

});

export default CSVReader;
<|endoftext|>


<commit_before>/*
 * @requires renderers/canvas
 */
/**
 * class DebugRenderer < Renderer
 *
 * Physics.renderer('debug')
 *
 * Extends canvas renderer with special debugging functionality.
 *
 * Additional config options:
 *
 * - debug: Draw debug shapes and bounding boxes. (default: `false`)
 * - metaEl: HTMLElement to write meta information like FPS and IPF into. (default: autogenerated)
 * - offset: Offset the shapes by this amount. (default: `{ x: 0, y: 0 }`)
 * - styles: Styles to use to draw the shapes. (see below)
 *
 * The styles property should contain _default_ styles for each shape you want to draw.
 *
 * Example:
 *
 * ```javascript
 * styles: {
 *
 *    'circle' : {
 *        strokeStyle: '#542437',
 *        lineWidth: 1,
 *        fillStyle: '#542437',
 *        angleIndicator: 'white'
 *    },
 *
 *    'convex-polygon' : {
 *        strokeStyle: '#542437',
 *        lineWidth: 1,
 *        fillStyle: '#542437',
 *        angleIndicator: 'white'
 *    }
 * }
 * ```
 **/
Physics.renderer('debug', 'canvas', function( parent, proto ){

    if ( !document ){
        // must be in node environment
        return {};
    }

    var defaults = {

        // the element to place meta data into
        metaEl: null,
        offset: { x: 0, y: 0 },
        // draw aabbs of bodies for debugging
        drawAABB: true,
        drawRealPosition: false,
        drawIntervals: false,
        drawContacts: false,

        // *** colors
        // color of the aabbs
        aabbColor: 'rgba(0, 0, 255, 0.1)',
        // styles used to draw the image of the body at its true non-interpolated position
        realBodyStyle: 'rgba(255, 0, 0, 0.5)',
        // colors for intervals
        intervalMinColor: 'rgba( 0, 0, 200, 0.1 )',
        intervalMaxColor: 'rgba( 200, 0, 0, 0.1 )',

        // contacts
        mtvColor: '#c00',
        contactColor: '#0c0'
    };

    return {

        // extended
        init: function( options ){

            var self = this;

            // call parent init
            parent.init.call(this, options);

            this.options.defaults( defaults, true );
            this.options( options, true );

            // init debug layer
            this.addLayer('debug', null, {
                zIndex: 2
            });

            this.layer('debug').render = function(){

                var intr, i, l, cols, xy;

                this.el.width = this.el.width;

                if ( self.options.drawIntervals && self._intervals ){
                    for ( xy = 0; xy < 2; xy++ ){
                        intr = self._intervals[ xy ];
                        for ( i = 0, l = intr.length; i < l; i++ ){

                            self.drawInterval( intr[ i ], this.ctx );
                        }
                    }
                }

                if ( self.options.drawContacts && self._collisions ){
                    cols = self._collisions;
                    for ( i = 0, l = cols.length; i < l; i++ ){
                        self.drawContact( cols[ i ], this.ctx );
                    }
                }
            };

            if ( window.dat ){
                this.initGui();
            }
        },

        connect: function( world ){

            world.on('sweep-prune:intervals', this.storeIntervals, this );
            world.on('collisions:detected', this.storeCollisions, this );
            world.on('render', this.reset, this);

            this.updateGui();
        },

        disconnect: function( world ){

            world.off('sweep-prune:intervals', this.storeIntervals, this );
            world.off('collisions:detected', this.storeCollisions, this );
            world.off('render', this.reset, this);
        },

        storeIntervals: function( intervals ){

            this._intervals = intervals;
        },

        storeCollisions: function( data ){

            this._collisions = data.collisions;
        },

        reset: function(){

            this._intervals = false;
            this._collisions = false;
        },

        drawInterval: function( intr, ctx ){

            var scratch = Physics.scratchpad()
                ,opts = this.options
                ,from = scratch.vector().set( intr.val.x, 0 )
                ,to = scratch.vector().set( intr.val.x, intr.tracker.body.state.pos.y )
                ;

            this.drawLine( from, to, opts[ intr.type ? 'intervalMaxColor' : 'intervalMinColor' ], ctx );
            this.drawCircle( from.x, from.y, 4, opts[ intr.type ? 'intervalMaxColor' : 'intervalMinColor' ], ctx );

            from.set( 0, intr.val.y );
            to.set( intr.tracker.body.state.pos.x, intr.val.y );

            this.drawLine( from, to, opts[ intr.type ? 'intervalMaxColor' : 'intervalMinColor' ], ctx );
            this.drawCircle( from.x, from.y, 4, opts[ intr.type ? 'intervalMaxColor' : 'intervalMinColor' ], ctx );

            scratch.done();
        },

        drawContact: function( c, ctx ){

            var scratch = Physics.scratchpad()
                ,from = scratch.vector().clone( c.pos ).vadd( c.bodyA.state.pos )
                ,to = scratch.vector().clone( from ).vsub( scratch.vector().clone( c.mtv ) )
                ,opts = this.options
                ;

            this.drawLine( from, to, opts.mtvColor, ctx );
            this.drawCircle( from.x, from.y, 2, opts.contactColor, ctx );

            scratch.done();
        },

        initGui: function(){

            var self = this
                ,gui = this.gui = new window.dat.GUI({ autoPlace: false })
                ,el = document.getElementById('my-gui-container')
                ,op = this.options
                ,getset
                ,f
                ;

            getset = {
                get timestep(){
                    return self._world ? self._world.timestep() : 6;
                }
                ,set timestep( dt ){
                    if ( self._world ) {
                        self._world.timestep( dt );
                    }
                }
                ,get maxIPF(){
                    return self._world ? self._world.options.maxIPF : 16;
                }
                ,set maxIPF( m ){
                    if ( self._world ){
                        self._world.options({ maxIPF: m });
                    }
                }
            };

            function pauseWorld(){
                if ( self._world ){
                    if ( self._world.isPaused() ){
                        self._world.unpause();
                    } else {
                        self._world.pause();
                    }
                }
            }

            f = gui.addFolder( 'General' );
            f.add( getset, 'timestep', 1, 20).step( 1 );
            f.add( getset, 'maxIPF', 1, 100).step( 1 );
            f.add( { pause: pauseWorld }, 'pause');
            f.open();

            f = gui.addFolder( 'Draw Options' );
            f.add( op, 'drawAABB' );
            f.add( op, 'drawRealPosition' );
            f.add( op, 'drawIntervals' );
            f.add( op, 'drawContacts' );
            f.open();

            f = gui.addFolder( 'Colors' );
            f.addColor( op, 'aabbColor' );
            f.addColor( op, 'realBodyStyle' );
            f.addColor( op, 'intervalMinColor' );
            f.addColor( op, 'intervalMaxColor' );
            f.addColor( op, 'mtvColor' );
            f.addColor( op, 'contactColor' );

            gui.domElement.style.zIndex = '100';
            gui.domElement.style.position = 'absolute';
            gui.domElement.style.top = '0';
            gui.domElement.style.left = '0';
            this.el.parentNode.appendChild( gui.domElement );
        },

        updateGui: function(){
            var gui = this.gui;
            // Iterate over all controllers
            for (var i in gui.__controllers) {
                gui.__controllers[i].updateDisplay();
            }
        },

        drawBody: function( body, view, ctx, offset ){

            var pos = body.state.pos
                ,v = body.state.vel
                ,t = this._interpolateTime || 0
                ,x
                ,y
                ,ang
                ,aabb
                ;

            offset = offset || this.options.offset;
            ctx = ctx || this.ctx;

            // interpolate positions
            x = pos.x + offset.x + v.x * t;
            y = pos.y + offset.y + v.y * t;
            ang = body.state.angular.pos + body.state.angular.vel * t;

            ctx.save();
            ctx.translate( x, y );
            ctx.rotate( ang );
            ctx.drawImage(view, -view.width/2, -view.height/2, view.width, view.height);
            ctx.restore();

            if ( this.options.drawAABB ){
                aabb = body.aabb();
                // draw bounding boxes
                this.drawRect( aabb.x, aabb.y, 2 * aabb.hw, 2 * aabb.hh, this.options.aabbColor );
            }

            if ( this.options.drawRealPosition ){
                // draw the non-interpolated body position
                body._debugView = body._debugView || this.createView(body.geometry, this.options.realBodyStyle);
                ctx.save();
                ctx.translate(pos.x + offset.x, pos.y + offset.y);
                ctx.rotate(body.state.angular.pos);
                ctx.drawImage(body._debugView, -body._debugView.width * 0.5, -body._debugView.height * 0.5);
                ctx.restore();
            }
        }
    };
});
<commit_msg>added docs to debug renderer<commit_after>/*
 * @requires renderers/canvas
 */
/**
 * class DebugRenderer < Renderer
 *
 * Physics.renderer('debug')
 *
 * Extends canvas renderer with special debugging functionality.
 *
 * Additional config options:
 *
 * - metaEl: HTMLElement to write meta information like FPS and IPF into. (default: autogenerated)
 * - offset: Offset the shapes by this amount. (default: `{ x: 0, y: 0 }`)
 * - styles: Styles to use to draw the shapes. (see below)
 * - drawAABB: whether or not to draw bounding boxes. (default: `true`)
 * - drawRealPosition: whether or not to draw the non-interpolated position of bodies. (default: `false`)
 * - drawIntervals: whether or not to draw the broadphase (sweep-prune) intervals. (default: `false`)
 * - drawContacts: whether or not to draw contact points. (default: `false`)
 * - aabbColor: the color of AABBs
 * - realBodyStyle: styles used to draw the image of the body at its true non-interpolated position
 * - intervalMinColor: color of interval minima
 * - intervalMaxColor: color of interval maxima
 * - mtvColor: color of minimum transit vector for contacts (overlaps)
 * - contactColor: color of contact points
 *
 * The styles property should contain _default_ styles for each shape you want to draw.
 *
 * Example:
 *
 * ```javascript
 * styles: {
 *
 *    'circle' : {
 *        strokeStyle: '#542437',
 *        lineWidth: 1,
 *        fillStyle: '#542437',
 *        angleIndicator: 'white'
 *    },
 *
 *    'convex-polygon' : {
 *        strokeStyle: '#542437',
 *        lineWidth: 1,
 *        fillStyle: '#542437',
 *        angleIndicator: 'white'
 *    }
 * }
 * ```
 **/
Physics.renderer('debug', 'canvas', function( parent, proto ){

    if ( !document ){
        // must be in node environment
        return {};
    }

    var defaults = {

        // the element to place meta data into
        metaEl: null,
        offset: { x: 0, y: 0 },
        // draw aabbs of bodies for debugging
        drawAABB: true,
        drawRealPosition: false,
        drawIntervals: false,
        drawContacts: false,

        // *** colors
        // color of the aabbs
        aabbColor: 'rgba(0, 0, 255, 0.1)',
        // styles used to draw the image of the body at its true non-interpolated position
        realBodyStyle: 'rgba(255, 0, 0, 0.5)',
        // colors for intervals
        intervalMinColor: 'rgba( 0, 0, 200, 0.1 )',
        intervalMaxColor: 'rgba( 200, 0, 0, 0.1 )',

        // contacts
        mtvColor: '#c00',
        contactColor: '#0c0'
    };

    return {

        // extended
        init: function( options ){

            var self = this;

            // call parent init
            parent.init.call(this, options);

            this.options.defaults( defaults, true );
            this.options( options, true );

            // init debug layer
            this.addLayer('debug', null, {
                zIndex: 2
            });

            this.layer('debug').render = function(){

                var intr, i, l, cols, xy;

                this.el.width = this.el.width;

                if ( self.options.drawIntervals && self._intervals ){
                    for ( xy = 0; xy < 2; xy++ ){
                        intr = self._intervals[ xy ];
                        for ( i = 0, l = intr.length; i < l; i++ ){

                            self.drawInterval( intr[ i ], this.ctx );
                        }
                    }
                }

                if ( self.options.drawContacts && self._collisions ){
                    cols = self._collisions;
                    for ( i = 0, l = cols.length; i < l; i++ ){
                        self.drawContact( cols[ i ], this.ctx );
                    }
                }
            };

            if ( window.dat ){
                this.initGui();
            }
        },

        connect: function( world ){

            world.on('sweep-prune:intervals', this.storeIntervals, this );
            world.on('collisions:detected', this.storeCollisions, this );
            world.on('render', this.reset, this);

            this.updateGui();
        },

        disconnect: function( world ){

            world.off('sweep-prune:intervals', this.storeIntervals, this );
            world.off('collisions:detected', this.storeCollisions, this );
            world.off('render', this.reset, this);
        },

        storeIntervals: function( intervals ){

            this._intervals = intervals;
        },

        storeCollisions: function( data ){

            this._collisions = data.collisions;
        },

        reset: function(){

            this._intervals = false;
            this._collisions = false;
        },

        drawInterval: function( intr, ctx ){

            var scratch = Physics.scratchpad()
                ,opts = this.options
                ,from = scratch.vector().set( intr.val.x, 0 )
                ,to = scratch.vector().set( intr.val.x, intr.tracker.body.state.pos.y )
                ;

            this.drawLine( from, to, opts[ intr.type ? 'intervalMaxColor' : 'intervalMinColor' ], ctx );
            this.drawCircle( from.x, from.y, 4, opts[ intr.type ? 'intervalMaxColor' : 'intervalMinColor' ], ctx );

            from.set( 0, intr.val.y );
            to.set( intr.tracker.body.state.pos.x, intr.val.y );

            this.drawLine( from, to, opts[ intr.type ? 'intervalMaxColor' : 'intervalMinColor' ], ctx );
            this.drawCircle( from.x, from.y, 4, opts[ intr.type ? 'intervalMaxColor' : 'intervalMinColor' ], ctx );

            scratch.done();
        },

        drawContact: function( c, ctx ){

            var scratch = Physics.scratchpad()
                ,from = scratch.vector().clone( c.pos ).vadd( c.bodyA.state.pos )
                ,to = scratch.vector().clone( from ).vsub( scratch.vector().clone( c.mtv ) )
                ,opts = this.options
                ;

            this.drawLine( from, to, opts.mtvColor, ctx );
            this.drawCircle( from.x, from.y, 2, opts.contactColor, ctx );

            scratch.done();
        },

        initGui: function(){

            var self = this
                ,gui = this.gui = new window.dat.GUI({ autoPlace: false })
                ,el = document.getElementById('my-gui-container')
                ,op = this.options
                ,getset
                ,f
                ;

            getset = {
                get timestep(){
                    return self._world ? self._world.timestep() : 6;
                }
                ,set timestep( dt ){
                    if ( self._world ) {
                        self._world.timestep( dt );
                    }
                }
                ,get maxIPF(){
                    return self._world ? self._world.options.maxIPF : 16;
                }
                ,set maxIPF( m ){
                    if ( self._world ){
                        self._world.options({ maxIPF: m });
                    }
                }
            };

            function pauseWorld(){
                if ( self._world ){
                    if ( self._world.isPaused() ){
                        self._world.unpause();
                    } else {
                        self._world.pause();
                    }
                }
            }

            f = gui.addFolder( 'General' );
            f.add( getset, 'timestep', 1, 20).step( 1 );
            f.add( getset, 'maxIPF', 1, 100).step( 1 );
            f.add( { pause: pauseWorld }, 'pause');
            f.open();

            f = gui.addFolder( 'Draw Options' );
            f.add( op, 'drawAABB' );
            f.add( op, 'drawRealPosition' );
            f.add( op, 'drawIntervals' );
            f.add( op, 'drawContacts' );
            f.open();

            f = gui.addFolder( 'Colors' );
            f.addColor( op, 'aabbColor' );
            f.addColor( op, 'realBodyStyle' );
            f.addColor( op, 'intervalMinColor' );
            f.addColor( op, 'intervalMaxColor' );
            f.addColor( op, 'mtvColor' );
            f.addColor( op, 'contactColor' );

            gui.domElement.style.zIndex = '100';
            gui.domElement.style.position = 'absolute';
            gui.domElement.style.top = '0';
            gui.domElement.style.left = '0';
            this.el.parentNode.appendChild( gui.domElement );
        },

        updateGui: function(){
            var gui = this.gui;
            // Iterate over all controllers
            for (var i in gui.__controllers) {
                gui.__controllers[i].updateDisplay();
            }
        },

        drawBody: function( body, view, ctx, offset ){

            var pos = body.state.pos
                ,v = body.state.vel
                ,t = this._interpolateTime || 0
                ,x
                ,y
                ,ang
                ,aabb
                ;

            offset = offset || this.options.offset;
            ctx = ctx || this.ctx;

            // interpolate positions
            x = pos.x + offset.x + v.x * t;
            y = pos.y + offset.y + v.y * t;
            ang = body.state.angular.pos + body.state.angular.vel * t;

            ctx.save();
            ctx.translate( x, y );
            ctx.rotate( ang );
            ctx.drawImage(view, -view.width/2, -view.height/2, view.width, view.height);
            ctx.restore();

            if ( this.options.drawAABB ){
                aabb = body.aabb();
                // draw bounding boxes
                this.drawRect( aabb.x, aabb.y, 2 * aabb.hw, 2 * aabb.hh, this.options.aabbColor );
            }

            if ( this.options.drawRealPosition ){
                // draw the non-interpolated body position
                body._debugView = body._debugView || this.createView(body.geometry, this.options.realBodyStyle);
                ctx.save();
                ctx.translate(pos.x + offset.x, pos.y + offset.y);
                ctx.rotate(body.state.angular.pos);
                ctx.drawImage(body._debugView, -body._debugView.width * 0.5, -body._debugView.height * 0.5);
                ctx.restore();
            }
        }
    };
});
<|endoftext|>


<commit_before>import { Router } from 'express'
import fs from 'fs'

const router = Router()

router.get('/', (req, res) => {
  fs.readFile('./data', 'utf8', (err, data) => {
    if (err) {
      res.status(500).send(err || { message: 'Internal Error' })
    } else {
      res.status(200).send(JSON.parse(data))
    }
  })
})

router.post('/', (req, res) => {
  const { lat, long } = req.body
  console.log(`body: ${JSON.parse(req.body)}`)
  console.log(`lat: ${lat}, long: ${long}`)
  const data = { lat, long }
  fs.writeFile('./data', JSON.stringify(data), (err) => {
    if (err) {
      res.status(500).send(err || { message: 'Internal Error' })
    } else {
      res.status(200).send({ message: 'Success' })
    }
  })
})

export default router
<commit_msg>change json.parse to json.stringify<commit_after>import { Router } from 'express'
import fs from 'fs'

const router = Router()

router.get('/', (req, res) => {
  fs.readFile('./data', 'utf8', (err, data) => {
    if (err) {
      res.status(500).send(err || { message: 'Internal Error' })
    } else {
      res.status(200).send(JSON.parse(data))
    }
  })
})

router.post('/', (req, res) => {
  const { lat, long } = req.body
  console.log(`body: ${JSON.stringify(req.body)}`)
  console.log(`lat: ${lat}, long: ${long}`)
  const data = { lat, long }
  fs.writeFile('./data', JSON.stringify(data), (err) => {
    if (err) {
      res.status(500).send(err || { message: 'Internal Error' })
    } else {
      res.status(200).send({ message: 'Success' })
    }
  })
})

export default router
<|endoftext|>


<commit_before>'use strict';

var express = require('express'),
    router = express.Router(),
    User = require('../models/user'),
    Category = require('../models/category'),
    Notepad = require('../models/notepad'),
    moment = require('moment');

//TODO: put this in FacebookAuth
//TODO: or make it connected somehow
//TODO: with the Facebook authentication process
function checkAuth(req, res, next) {
    if (!req.isAuthenticated()) {
        console.log('API cats: checkAuth(), not authenticated!');
        res.status(403).send('Unauthorized');
    } else {
        next();
    }
}

//protect this resource to the logged in user
//TODO: checkAuth() should be shared between all handlers/controllers
//TODO: put it in the FacebookAuth file
//TODO: or create a separate auth module
router.use(checkAuth);

//base: /notepads

// ?insidecats=1
//TODO: only next() may be needed, not next(route)
//TODO: check with/out the ?param
//TODO: and create a second route without the query string instead of the else
function insidecatsFromQueryString() {
    return function (req, res, next) {
        var insidecats = String(req.query.insidecats);
        if (insidecats && "1" === insidecats) {
            req.params.insidecats = insidecats;
            next();
        } else {
            next('route');
        }
    };
}

//validation
//router.param('range', /^(\w+)\.\.(\w+)?$/);

// GET /notepads(?insidecats=1)?
//TODO: make it with less DB calls!!!
router.get('/', insidecatsFromQueryString(), function (req, res) {
    //if (req.params.insidecats) {
            //get the categories
            Category.getByUserId(req.user._id, function (err, cats) {
                var categories = [];
                //TODO: if err / cats === null , empty
                cats.forEach(function (cat) {
                    categories.push({
                        _id: cat._id,
                        name: cat.name,
                        notepadsCount: cat.notepadsCount,
                        notepads: []
                    });
                });

                //TODO: catsCache[] instead:
                var catsCache = {};
                function findCat(cats, id) {
                    if ("undefined" !== typeof catsCache[id]) {
                        return catsCache[id];
                    }
                    cats.forEach(function (cat) {
                        if (cat._id.toString() === id.toString()) {
                            catsCache[id] = cat;
                            return false;
                        }
                    });
                    return catsCache[id];
                }

                Notepad.getByUserId(req.user._id, function (err, notepads) {
                    //TODO: if err / notepads === null
                    var curCat;
                    notepads.forEach(function (notepad) {
                        curCat = findCat(categories, notepad.category);
                        curCat.notepads.push({
                            _id: notepad._id,
                            title: notepad.title,
                            text: notepad.text,
                            created: moment(
                                notepad._id.getTimestamp())
                                    .format('YYYY/MM/DD HH:mm:ss')
                        });
                    });
                    return res.json(categories);
                });
        });
    /*} else {
        //get user's notepads' ObjectIds
        User.findOne({ _id: req.user.id }, 'notepads', function (err, user) {
            //TODO: if err
            //get the notepads data by these ObjectIds
            Notepad.find({ _id: { $in: user.notepads }}, function (err, notepads) {
                //TODO: if err
                //TODO: empty status
                res.json(notepads);
            });
        });
    }*/
});

// GET /notepads/1
router.get('/:id', function (req, res) {
    //TODO: check id validity, belongs to the current user, etc.
    Notepad.getByIdForUser(req.params.id, req.user.id, function (err, notepad) {
        if (err) {
            return res.status(500).json(err);
        }
        if (!notepad) {
            //TODO: return empty set status
            return res.status(500).json({});
        }
        return res.json(notepad);
    });
});

// POST /notepads
router.post('/', function (req, res) {
    //TODO: check for empty or invalid values
    //TODO: create a common checkCatBelongsTo() function to use everywhere here
    Category.getByIdForUser(req.body.category, req.user.id, function (err, category) {

        if (err) {
            return res.status(500).json(err);
        }
        if (!category) {
            return res.status(500).json('Unknown category!');
        }

        var notepad = new Notepad({
            category: req.body.category,
            title: req.body.title,
            text: req.body.text,
            user: req.user.id
        });

        notepad.save(function (err, notepad) {
            User.addNotepad(req.user.id, notepad, function (err, user) {
                if (err) {
                    return res.status(500).json(err);
                }
                if (!user) {
                    return res.status(500).json('Error assigning the notepad to the user!');
                }
                Category.increaseNotepadsCountById(notepad.category, function (err, category) {
                    //TODO: if err / category === null
                    return res.json(notepad);
                });
            });
        });

    });
});

// PUT /notepads/1
router.put('/:id', function (req, res) {
    console.log('updating a Notepad');
    //TODO: check id and notepad props validity, belongs to user, etc.
    //TODO: check if cat belongs to the user: create a common function for that
    Notepad.getByIdForUser(req.params.id, req.user.id, function (err, notepad) {
        //TODO: check err/null notepad and return empty res status
        console.log('found a notepad for user', req.params.id, notepad, req.user.id);
        var oldCat = notepad.category;
        var newCat = req.body.category;
        Notepad.findOneAndUpdate(
            { _id: notepad._id },
            {$set: {
                title: req.body.title,
                text: req.body.text,
                category: req.body.category
            }},
            {},
            function (err, notepadNew) {
                //TODO: check err / notepad === null
                console.log('Notepad updated', notepadNew);
                if (String(newCat) !== String(oldCat)) {
                    Category.decreaseNotepadsCountById(oldCat, function (err, catOld) {
                        //TODO: if err / category === null
                        console.log('category Notepads count decreased', catOld);
                        Category.increaseNotepadsCountById(newCat, function (err, catNew) {
                            //TODO: if err / category === null
                            console.log('category Notepads count increased', catNew);
                            return res.status(200).json(notepadNew);
                        });
                    });
                } else {
                    return res.status(200).json(notepadNew);
                }
            }
        );
    });
});

// DELETE /notepads/1
router.delete('/:id', function (req, res) {
    //TODO: check for valid id: exists/is ObjectID/etc.
    Notepad.getByIdForUser(req.params.id, req.user.id, function (err, notepad) {

        if (err) {
            return res.status(500).json(err);
        }
        if (!notepad) {
            //TODO: return the proper status code
            return res.status(500).json('Notepad not found!');
        }

        Notepad.findByIdAndRemove(req.params.id, function (err, notepad) {
            //TODO: check if err / notepad === null - missing
            console.log('notepad removed:', notepad);
            //finds user by notepad._id looking into user.notepads[]
            User.findOneAndUpdate({ notepads: req.params.id }, {
                $pull: { notepads: req.params.id }
            }, function (err, user) {
                console.log('notepad removed from the user');
                //TODO: check if err / user === null, etc.
                Category.decreaseNotepadsCountById(notepad.category, function (err, category) {
                    console.log('category notepadsCount decreased');
                    //TODO: check if err / category === null - not found?
                    return res.status(200).json(notepad);
                });
            });
        });

    });
});

module.exports = router;
<commit_msg>added and used https-status module, added missing error checks<commit_after>'use strict';

var express = require('express'),
    router = express.Router(),
    User = require('../models/user'),
    Category = require('../models/category'),
    Notepad = require('../models/notepad'),
    moment = require('moment'),
    HttpStatus = require('http-status');

//TODO: put this in FacebookAuth
//TODO: or make it connected somehow
//TODO: with the Facebook authentication process
function checkAuth(req, res, next) {
    if (!req.isAuthenticated()) {
        console.log('API cats: checkAuth(), not authenticated!');
        res.status(403).send('Unauthorized');
    } else {
        next();
    }
}

//protect this resource to the logged in user
//TODO: checkAuth() should be shared between all handlers/controllers
//TODO: put it in the FacebookAuth file
//TODO: or create a separate auth module
router.use(checkAuth);

//base: /notepads

// ?insidecats=1
//TODO: only next() may be needed, not next(route)
//TODO: check with/out the ?param
//TODO: and create a second route without the query string instead of the else
function insidecatsFromQueryString() {
    return function (req, res, next) {
        var insidecats = String(req.query.insidecats);
        if (insidecats && "1" === insidecats) {
            req.params.insidecats = insidecats;
            next();
        } else {
            next('route');
        }
    };
}

//validation
//router.param('range', /^(\w+)\.\.(\w+)?$/);

// GET /notepads(?insidecats=1)?
//TODO: make it with less DB calls!!!
router.get('/', insidecatsFromQueryString(), function (req, res) {
    //if (req.params.insidecats) {
            //get the categories
            Category.getByUserId(req.user._id, function (err, cats) {
                var categories = [];
                //TODO: if err / cats === null , empty
                cats.forEach(function (cat) {
                    categories.push({
                        _id: cat._id,
                        name: cat.name,
                        notepadsCount: cat.notepadsCount,
                        notepads: []
                    });
                });

                //TODO: catsCache[] instead:
                var catsCache = {};
                function findCat(cats, id) {
                    if ("undefined" !== typeof catsCache[id]) {
                        return catsCache[id];
                    }
                    cats.forEach(function (cat) {
                        if (cat._id.toString() === id.toString()) {
                            catsCache[id] = cat;
                            return false;
                        }
                    });
                    return catsCache[id];
                }

                Notepad.getByUserId(req.user._id, function (err, notepads) {
                    //TODO: if err / notepads === null
                    var curCat;
                    notepads.forEach(function (notepad) {
                        curCat = findCat(categories, notepad.category);
                        curCat.notepads.push({
                            _id: notepad._id,
                            title: notepad.title,
                            text: notepad.text,
                            created: moment(
                                notepad._id.getTimestamp())
                                    .format('YYYY/MM/DD HH:mm:ss')
                        });
                    });
                    return res.json(categories);
                });
        });
    /*} else {
        //get user's notepads' ObjectIds
        User.findOne({ _id: req.user.id }, 'notepads', function (err, user) {
            //TODO: if err
            //get the notepads data by these ObjectIds
            Notepad.find({ _id: { $in: user.notepads }}, function (err, notepads) {
                //TODO: if err
                //TODO: empty status
                res.json(notepads);
            });
        });
    }*/
});

// GET /notepads/1
router.get('/:id', function (req, res) {
    //TODO: check id validity, belongs to the current user, etc.
    Notepad.getByIdForUser(req.params.id, req.user.id, function (err, notepad) {
        if (err) {
            return res.status(500).json(err);
        }
        if (!notepad) {
            //TODO: return empty set status
            return res.status(500).json({});
        }
        return res.json(notepad);
    });
});

// POST /notepads
router.post('/', function (req, res) {
    //TODO: check for empty or invalid values
    //TODO: create a common checkCatBelongsTo() function to use everywhere here
    Category.getByIdForUser(req.body.category, req.user.id, function (err, category) {

        if (err) {
            return res.status(500).json(err);
        }
        if (!category) {
            return res.status(500).json('Unknown category!');
        }

        var notepad = new Notepad({
            category: req.body.category,
            title: req.body.title,
            text: req.body.text,
            user: req.user.id
        });

        notepad.save(function (err, notepad) {
            User.addNotepad(req.user.id, notepad, function (err, user) {
                if (err) {
                    return res.status(500).json(err);
                }
                if (!user) {
                    return res.status(500).json('Error assigning the notepad to the user!');
                }
                Category.increaseNotepadsCountById(notepad.category, function (err, category) {
                    if (err) {
                        return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({});
                    }
                    if (!category) {
                        return res.status(HttpStatus.NO_CONTENT).json({});
                    }
                    return res.json(notepad);
                });
            });
        });

    });
});

// PUT /notepads/1
router.put('/:id', function (req, res) {
    console.log('updating a Notepad');
    //TODO: check id and notepad props validity, belongs to user, etc.
    //TODO: check if cat belongs to the user: create a common function for that
    Notepad.getByIdForUser(req.params.id, req.user.id, function (err, notepad) {
        //TODO: check err/null notepad and return empty res status
        console.log('found a notepad for user', req.params.id, notepad, req.user.id);
        var oldCat = notepad.category;
        var newCat = req.body.category;
        Notepad.findOneAndUpdate(
            { _id: notepad._id },
            {$set: {
                title: req.body.title,
                text: req.body.text,
                category: req.body.category
            }},
            {},
            function (err, notepadNew) {
                //TODO: check err / notepad === null
                console.log('Notepad updated', notepadNew);
                if (String(newCat) !== String(oldCat)) {
                    Category.decreaseNotepadsCountById(oldCat, function (err, catOld) {
                        //TODO: if err / category === null
                        console.log('category Notepads count decreased', catOld);
                        Category.increaseNotepadsCountById(newCat, function (err, catNew) {
                            //TODO: if err / category === null
                            console.log('category Notepads count increased', catNew);
                            return res.status(200).json(notepadNew);
                        });
                    });
                } else {
                    return res.status(200).json(notepadNew);
                }
            }
        );
    });
});

// DELETE /notepads/1
router.delete('/:id', function (req, res) {
    //TODO: check for valid id: exists/is ObjectID/etc.
    Notepad.getByIdForUser(req.params.id, req.user.id, function (err, notepad) {

        if (err) {
            return res.status(500).json(err);
        }
        if (!notepad) {
            //TODO: return the proper status code
            return res.status(500).json('Notepad not found!');
        }

        Notepad.findByIdAndRemove(req.params.id, function (err, notepad) {
            //TODO: check if err / notepad === null - missing
            console.log('notepad removed:', notepad);
            //finds user by notepad._id looking into user.notepads[]
            User.findOneAndUpdate({ notepads: req.params.id }, {
                $pull: { notepads: req.params.id }
            }, function (err, user) {
                if (err) {
                    return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({});
                }
                if (!user) {
                    return res.status(HttpStatus.NO_CONTENT).json({});
                }
                console.info('notepad removed from the user');
                Category.decreaseNotepadsCountById(notepad.category, function (err, category) {
                    if (err) {
                        return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({});
                    }
                    if (!category) {
                        return res.status(HttpStatus.NO_CONTENT).json({});
                    }
                    console.log('category notepadsCount decreased');
                    return res.status(200).json(notepad);
                });
            });
        });

    });
});

module.exports = router;
<|endoftext|>