Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Committing all experimental AR features etc

  • Loading branch information...
commit 62cb0cbeaf58f3f5ff9e13b405e810ca48edb1ba 1 parent ca0a44d
@samluescher samluescher authored
View
37 g-speak/oblessd
@@ -22,6 +22,7 @@ fps = 80.0
open_sockets = []
socket_options = {}
+min_dist = 0.025
if ARGV.length > 0:
@@ -36,11 +37,26 @@ if ARGV.length > 0:
end
end
+def vector_dist(v1, v2)
+ xd = v1[0] - v2[0]
+ yd = v1[1] - v2[1]
+ zd = v1[2] - v2[2]
+ return Math.sqrt(xd*xd + yd*yd + zd*zd)
+end
+def tag_different(tag1, tag2, min_dist)
+ for cmp in ['loc', 'norm', 'over']:
+ if vector_dist(tag1[cmp], tag2[cmp]) > min_dist:
+ return true
+ end
+ end
+ return false;
+end
Thread.abort_on_exception = true
ingests_tags = {}
ingests_tracks = {}
+sent_tags = {}
for pool in pools:
Thread.new do
@@ -67,15 +83,21 @@ Thread.new do
loop {
next_time = Time.now
timeDelta = next_time - prev_time
- if timeDelta >= targetTimeDelta
+ if min_dist != 0 or timeDelta >= targetTimeDelta
for socket in open_sockets
send_tags = nil
send_tracks = nil
if ingests_tags:
send_tags = {}
for tag in watch_tags[socket]
- if ingests_tags[tag]
- send_tags[tag] = ingests_tags[tag]
+ if ingests_tags[tag]:
+ if (not sent_tags[socket].include?(tag)) or tag_different(sent_tags[socket][tag], ingests_tags[tag], min_dist):
+ if sent_tags[socket].include?(tag):
+ #puts 'is different: '+(tag_different(sent_tags[socket][tag], ingests_tags[tag])
+ end
+ send_tags[tag] = ingests_tags[tag]
+ sent_tags[socket][tag] = ingests_tags[tag]
+ end
end
end
end
@@ -83,13 +105,15 @@ Thread.new do
send_tracks = ingests_tracks
end
send_data = {}
- if send_tags:
+ if send_tags and send_tags.length > 0:
send_data['tags'] = send_tags
end
- if send_tracks:
+ if send_tracks and send_tracks.length > 0:
send_data['tracks'] = send_tracks
end
- socket.send(send_data.to_json)
+ if send_data.length > 0:
+ socket.send(send_data.to_json)
+ end
end
prev_time = next_time
end
@@ -104,6 +128,7 @@ EventMachine::WebSocket.start(:host => host, :port => port, :debug => debug) do
puts "WebSocket connected"
open_sockets.push(ws)
watch_tags[ws] = []
+ sent_tags[ws] = {}
}
ws.onclose {
EM.cancel_timer(timer)
View
11 g-speak/test-oblessd-tags.html
@@ -15,6 +15,11 @@
var receivedMilli = [];
var sampleSize = 50;
+ var getMilli = function() {
+ d = new Date();
+ return Date.UTC(d.getYear(), d.getMonth(), d.getDay(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
+ };
+
var connect = function()
{
ws = new Socket("ws://18.85.58.62:8080/");
@@ -33,9 +38,7 @@
console.log('broadcast message received '+evt.data);
return;
}
- d = new Date();
- var ms = Date.UTC(d.getYear(), d.getMonth(), d.getDay(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
- receivedMilli.push(ms);
+ receivedMilli.push(getMilli());
if (receivedMilli.length > sampleSize) {
receivedMilli.shift();
}
@@ -64,7 +67,7 @@
connect();
var displayFps = function() {
if (receivedMilli.length > 1 ) {
- fps = receivedMilli.length / ((receivedMilli[receivedMilli.length - 1] - receivedMilli[0]) / 1000.0);
+ fps = receivedMilli.length / ((getMilli() - receivedMilli[0]) / 1000.0);
document.getElementById("fps").innerHTML = Math.round(fps) + ' fps';
}
setTimeout(displayFps, 500);
View
14 ios/UIWebViewWebGL/UIWebViewWebGL/ARViewController.m
@@ -9,7 +9,6 @@
#import "ARViewController.h"
#import "WGLAppDelegate.h"
-
@interface ARViewController ()
@end
@@ -32,7 +31,7 @@ - (void)viewDidLoad
[super viewDidLoad];
webView.backgroundColor = [UIColor clearColor];
webView.opaque = NO;
- webView.scrollView.scrollEnabled = NO;
+ //webView.scrollView.scrollEnabled = NO;
webView.scrollView.bounces = NO;
id webDocumentView = [webView performSelector:@selector(_browserView)];
@@ -49,7 +48,9 @@ - (void)viewDidLoad
[backingWebView _setWebGLEnabled:YES];
- [self loadUrl:@"http://18.85.59.187:8124/hY0VgozaeoY4NQp/globe/?lens_tag=1" addToHistory:YES];
+ [self loadUrl:@"http://18.189.26.244:8124/Oeq6NkK0FTmkPRG/globe/?lens_tag=1" addToHistory:YES];
+ [self loadUrl:@"http://18.189.26.244:8124/Oeq6NkK0FTmkPRG/globe/?lens_tag=1&loupe=1" addToHistory:YES];
+ [self loadUrl:@"http://18.189.26.244:8124/" addToHistory:YES];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showUrlBar:)];
[tapRecognizer setNumberOfTapsRequired:3];
@@ -71,7 +72,6 @@ - (void)loadUrl:(NSString *)url addToHistory:(bool)addToHistory {
urlField.text = url;
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:10];
- webView.hidden = YES;
[webView loadRequest:request];
NSLog(@"load url %@", url);
@@ -90,7 +90,8 @@ - (IBAction)urlEntered {
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
- [hud show:YES];
+ //[hud show:YES];
+ //webView.hidden = YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
@@ -132,6 +133,9 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
+ if ([error code] == NSURLErrorCancelled) {
+ return;
+ }
[hud hide:YES];
NSString *message = [error.userInfo objectForKey:@"NSLocalizedDescription"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
View
518 node_modules/.bin/nodemon
@@ -0,0 +1,518 @@
+#!/usr/bin/env node
+var fs = require('fs'),
+ util = require('util'),
+ childProcess = require('child_process'),
+ dirs = [],
+ path = require('path'),
+ exists = fs.exists || path.exists, // yay, exists moved from path to fs in 0.7.x ... :-\
+ existsSync = fs.existsSync || path.existsSync,
+ spawn = childProcess.spawn,
+ meta = JSON.parse(fs.readFileSync(__dirname + '/package.json')),
+ exec = childProcess.exec,
+ flag = './.monitor',
+ program = getNodemonArgs(),
+ child = null,
+ monitor = null,
+ ignoreFilePath = './.nodemonignore',
+ oldIgnoreFilePath = './nodemon-ignore',
+ ignoreFiles = [],
+ reIgnoreFiles = null,
+ timeout = 1000, // check every 1 second
+ restartDelay = 0, // controlled through arg --delay 10 (for 10 seconds)
+ restartTimer = null,
+ lastStarted = +new Date,
+ statOffset = 0, // stupid fix for https://github.com/joyent/node/issues/2705
+ platform = process.platform,
+ isWindows = platform === 'win32',
+ noWatch = (platform !== 'win32') || !fs.watch, // && platform !== 'linux' - removed linux fs.watch usage #72
+ watchFile = platform === 'darwin' ? fs.watchFile : fs.watch, // lame :(
+ // create once, reuse as needed
+ reEscComments = /\\#/g,
+ reUnescapeComments = /\^\^/g, // note that '^^' is used in place of escaped comments
+ reComments = /#.*$/,
+ reTrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
+ reEscapeChars = /[.|\-[\]()\\]/g,
+ reAsterisk = /\*/g;
+
+// test to see if the version of find being run supports searching by seconds (-mtime -1s -print)
+if (noWatch) {
+ exec('find -L /dev/null -type f -mtime -1s -print', function(error, stdout, stderr) {
+ if (error) {
+ if (!fs.watch) {
+ util.error('\x1B[1;31mThe version of node you are using combined with the version of find being used does not support watching files. Upgrade to a newer version of node, or install a version of find that supports search by seconds.\x1B[0m');
+ process.exit(1);
+ } else {
+ noWatch = false;
+ }
+ }
+ });
+}
+
+function startNode() {
+ util.log('\x1B[32m[nodemon] starting `' + program.options.exec + ' ' + program.args.join(' ') + '`\x1B[0m');
+
+ child = spawn(program.options.exec, program.args);
+
+ lastStarted = +new Date;
+
+ child.stdout.on('data', function (data) {
+ util.print(data);
+ });
+
+ child.stderr.on('data', function (data) {
+ process.stderr.write(data);
+ });
+
+ child.on('exit', function (code, signal) {
+ // this is nasty, but it gives it windows support
+ if (isWindows && signal == 'SIGTERM') signal = 'SIGUSR2';
+ // exit the monitor, but do it gracefully
+ if (signal == 'SIGUSR2') {
+ // restart
+ startNode();
+ } else if (code === 0) { // clean exit - wait until file change to restart
+ util.log('\x1B[32m[nodemon] clean exit - waiting for changes before restart\x1B[0m');
+ child = null;
+ } else if (program.options.exitcrash) {
+ util.log('\x1B[1;31m[nodemon] app crashed\x1B[0m');
+ process.exit(0);
+ } else {
+ util.log('\x1B[1;31m[nodemon] app crashed - waiting for file changes before starting...\x1B[0m');
+ child = null;
+ }
+ });
+
+ // pinched from https://github.com/DTrejo/run.js - pipes stdin to the child process - cheers DTrejo ;-)
+ if (program.options.stdin) {
+ process.stdin.resume();
+ process.stdin.setEncoding('utf8');
+ process.stdin.pipe(child.stdin);
+ }
+
+ setTimeout(startMonitor, timeout);
+}
+
+function startMonitor() {
+ var changeFunction;
+
+ if (noWatch) {
+ // if native fs.watch doesn't work the way we want, we keep polling find command (mac only oddly)
+ changeFunction = function (lastStarted, callback) {
+ var cmds = [],
+ changed = [];
+
+ dirs.forEach(function(dir) {
+ cmds.push('find -L "' + dir + '" -type f -mtime -' + ((+new Date - lastStarted)/1000|0) + 's -print');
+ });
+
+ exec(cmds.join(';'), function (error, stdout, stderr) {
+ var files = stdout.split(/\n/);
+ files.pop(); // remove blank line ending and split
+ callback(files);
+ });
+ }
+ } else {
+ changeFunction = function (lastStarted, callback) {
+ // recursive watch - watch each directory and it's subdirectories, etc, etc
+ var watch = function (err, dir) {
+ try {
+ fs.watch(dir, { persistent: false }, function (event, filename) {
+ var filepath = path.join(dir, filename);
+ callback([filepath]);
+ });
+
+ fs.readdir(dir, function (err, files) {
+ if (!err) {
+ files.forEach(function (file) {
+ var filename = path.join(dir, file);
+ fs.stat(filename, function (err, stat) {
+ if (!err && stat) {
+ if (stat.isDirectory()) {
+ fs.realpath(filename, watch);
+ }
+ }
+ });
+ });
+ }
+ });
+ } catch (e) {
+ // ignoring this directory, likely it's "My Music"
+ // or some such windows fangled stuff
+ }
+ }
+ dirs.forEach(function (dir) {
+ fs.realpath(dir, watch);
+ });
+ }
+ }
+
+ var isWindows = process.platform === 'win32';
+ changeFunction(lastStarted, function (files) {
+ if (files.length) {
+ // filter ignored files
+ if (ignoreFiles.length) {
+ files = files.filter(function(file) {
+ // If we are in a Windows machine
+ if (isWindows) {
+ // Break up the file by slashes
+ var fileParts = file.split(/\\/g);
+
+ // Remove the first piece (C:)
+ fileParts.shift();
+
+ // Join the parts together with Unix slashes
+ file = '/' + fileParts.join('/');
+ }
+ return !reIgnoreFiles.test(file);
+ });
+ }
+
+
+ if (files.length) {
+ if (restartTimer !== null) clearTimeout(restartTimer);
+ restartTimer = setTimeout(function () {
+ if (program.options.verbose) util.log('[nodemon] restarting due to changes...');
+ files.forEach(function (file) {
+ if (program.options.verbose) util.log('[nodemon] ' + file);
+ });
+ if (program.options.verbose) util.print('\n\n');
+
+ if (child !== null) {
+ child.kill(isWindows ? '' : 'SIGUSR2');
+ } else {
+ startNode();
+ }
+ }, restartDelay);
+ return;
+ }
+ }
+
+ if (noWatch) setTimeout(startMonitor, timeout);
+ });
+}
+
+function addIgnoreRule(line, noEscape) {
+ // remove comments and trim lines
+ // this mess of replace methods is escaping "\#" to allow for emacs temp files
+ if (!noEscape) {
+ if (line = line.replace(reEscComments, '^^').replace(reComments, '').replace(reUnescapeComments, '#').replace(reTrim, '')) {
+ ignoreFiles.push(line.replace(reEscapeChars, '\\$&').replace(reAsterisk, '.*'));
+ }
+ } else if (line = line.replace(reTrim, '')) {
+ ignoreFiles.push(line);
+ }
+ reIgnoreFiles = new RegExp(ignoreFiles.join('|'));
+}
+
+function readIgnoreFile(curr, prev) {
+ // unless the ignore file was actually modified, do no re-read it
+ if(curr && prev && curr.mtime.valueOf() === prev.mtime.valueOf()) return;
+
+ if (platform === 'darwin') fs.unwatchFile(ignoreFilePath);
+
+ // Check if ignore file still exists. Vim tends to delete it before replacing with changed file
+ exists(ignoreFilePath, function(exists) {
+ if (program.options.verbose) util.log('[nodemon] reading ignore list');
+
+ // ignoreFiles = ignoreFiles.concat([flag, ignoreFilePath]);
+ // addIgnoreRule(flag);
+ addIgnoreRule(ignoreFilePath.substring(2)); // ignore the ./ part of the filename
+ fs.readFileSync(ignoreFilePath).toString().split(/\n/).forEach(function (rule, i) {
+ addIgnoreRule(rule);
+ });
+
+ watchFile(ignoreFilePath, { persistent: false }, readIgnoreFile);
+ });
+}
+
+// attempt to shutdown the wrapped node instance and remove
+// the monitor file as nodemon exists
+function cleanup() {
+ child && child.kill();
+ // fs.unlink(flag);
+}
+
+function getNodemonArgs() {
+ var args = process.argv,
+ len = args.length,
+ i = 2,
+ dir = process.cwd(),
+ indexOfApp = -1,
+ app = null;
+
+ for (; i < len; i++) {
+ if (existsSync(path.resolve(dir, args[i]))) {
+ // double check we didn't use the --watch or -w opt before this arg
+ if (args[i-1] && (args[i-1] == '-w' || args[i-1] == '--watch')) {
+ // ignore
+ } else {
+ indexOfApp = i;
+ break;
+ }
+ }
+ }
+
+ if (indexOfApp !== -1) {
+ // not found, so assume we're reading the package.json and thus swallow up all the args
+ // indexOfApp = len;
+ app = process.argv[i];
+ indexOfApp++;
+ } else {
+ indexOfApp = len;
+ }
+
+ var appargs = [], //process.argv.slice(indexOfApp),
+ // app = appargs[0],
+ nodemonargs = process.argv.slice(2, indexOfApp - (app ? 1 : 0)),
+ arg,
+ options = {
+ delay: 1,
+ watch: [],
+ exec: 'node',
+ verbose: true,
+ js: false, // becomes the default anyway...
+ includeHidden: false,
+ exitcrash: false,
+ stdin: true
+ // args: []
+ };
+
+ // process nodemon args
+ args.splice(0, 2);
+ while (arg = args.shift()) {
+ if (arg === '--help' || arg === '-h' || arg === '-?') {
+ return help(); // exits program
+ } else if (arg === '--version' || arg == '-v') {
+ return version(); // also exits
+ } else if (arg == '--js') {
+ options.js = true;
+ } else if (arg == '--quiet' || arg == '-q') {
+ options.verbose = false;
+ } else if (arg == '--hidden') {
+ options.includeHidden = true;
+ } else if (arg === '--watch' || arg === '-w') {
+ options.watch.push(args.shift());
+ } else if (arg === '--exitcrash') {
+ options.exitcrash = true;
+ } else if (arg === '--delay' || arg === '-d') {
+ options.delay = parseInt(args.shift());
+ } else if (arg === '--exec' || arg === '-x') {
+ options.exec = args.shift();
+ } else if (arg === '--no-stdin' || arg === '-I') {
+ options.stdin = false;
+ } else { //if (arg === "--") {
+ // Remaining args are node arguments
+ appargs.push(arg);
+ }
+ }
+
+ var program = { options: options, args: appargs, app: app };
+
+ getAppScript(program);
+
+ return program;
+}
+
+function getAppScript(program) {
+ var hokeycokey = false;
+ if (!program.args.length || program.app === null) {
+ // try to get the app from the package.json
+ // doing a try/catch because we can't use the path.exist callback pattern
+ // or we could, but the code would get messy, so this will do exactly
+ // what we're after - if the file doesn't exist, it'll throw.
+ try {
+ // note: this isn't nodemon's package, it's the user's cwd package
+ program.app = JSON.parse(fs.readFileSync('./package.json').toString()).main;
+ if (program.app === undefined) {
+ // no app found to run - so give them a tip and get the feck out
+ help();
+ }
+ program.args.unshift(program.app);
+ hokeycokey = true;
+ } catch (e) {}
+ }
+
+ if (!program.app) {
+ program.app = program.args[0];
+ }
+
+ program.app = path.basename(program.app);
+ program.ext = path.extname(program.app);
+
+ if (program.options.exec.indexOf(' ') !== -1) {
+ var execOptions = program.options.exec.split(' ');
+ program.options.exec = execOptions.splice(0, 1)[0];
+ program.args = execOptions.concat(program.args);
+ }
+
+ if (program.options.exec === 'node' && program.ext == '.coffee') {
+ program.options.exec = 'coffee';
+ }
+
+ if (program.options.exec === 'coffee') {
+ if (hokeycokey) {
+ program.args.push(program.args.shift());
+ }
+ //coffeescript requires --nodejs --debug
+ var debugIndex = program.args.indexOf('--debug');
+ if (debugIndex === -1) debugIndex = program.args.indexOf('--debug-brk');
+ if (debugIndex !== -1 && program.args.indexOf('--nodejs') === -1) {
+ program.args.splice(debugIndex, 0, '--nodejs');
+ }
+ // monitor both types - TODO possibly make this an option?
+ program.ext = '.coffee|.js';
+ if (!program.options.exec || program.options.exec == 'node') program.options.exec = 'coffee';
+
+ // because windows can't find 'coffee', it needs the real file 'coffee.cmd'
+ if (isWindows) program.options.exec += '.cmd';
+ }
+}
+
+function findStatOffset() {
+ var filename = './.stat-test';
+ fs.writeFile(filename, function (err) {
+ if (err) return;
+ fs.stat(filename, function (err, stat) {
+ if (err) return;
+
+ statOffset = stat.mtime.getTime() - new Date().getTime();
+ fs.unlink(filename);
+ });
+ });
+}
+
+function version() {
+ console.log(meta.version);
+ process.exit(0);
+}
+
+function help() {
+ util.print([
+ '',
+ ' Usage: nodemon [options] [script.js] [args]',
+ '',
+ ' Options:',
+ '',
+ ' -d, --delay n throttle restart for "n" seconds',
+ ' -w, --watch dir watch directory "dir". use once for each',
+ ' directory to watch',
+ ' -x, --exec app execute script with "app", ie. -x "python -v"',
+ ' -I, --no-stdin don\'t try to read from stdin',
+ ' -q, --quiet minimise nodemon messages to start/stop only',
+ ' --exitcrash exit on crash, allows use of nodemon with',
+ ' daemon tools like forever.js',
+ ' -v, --version current nodemon version',
+ ' -h, --help you\'re looking at it',
+ '',
+ ' Note: if the script is omitted, nodemon will try to ',
+ ' read "main" from package.json and without a .nodemonignore,',
+ ' nodemon will monitor .js and .coffee by default.',
+ '',
+ ' Examples:',
+ '',
+ ' $ nodemon server.js',
+ ' $ nodemon -w ../foo server.js apparg1 apparg2',
+ ' $ PORT=8000 nodemon --debug-brk server.js',
+ ' $ nodemon --exec python app.py',
+ '',
+ ' For more details see http://github.com/remy/nodemon/',
+ ''
+ ].join('\n') + '\n');
+ process.exit(0);
+}
+
+// this little bit of hoop jumping is because sometimes the file can't be
+// touched properly, and it send nodemon in to a loop of restarting.
+// this way, the .monitor file is removed entirely, and recreated with
+// permissions that anyone can remove it later (i.e. if you run as root
+// by accident and then try again later).
+// if (path.existsSync(flag)) fs.unlinkSync(flag);
+// fs.writeFileSync(flag, '.'); // requires some content https://github.com/remy/nodemon/issues/36
+// fs.chmodSync(flag, '666');
+
+// remove the flag file on exit
+process.on('exit', function (code) {
+ if (program.options.verbose) util.log('[nodemon] exiting');
+ cleanup();
+});
+
+if (!isWindows) { // because windows borks when listening for the SIG* events
+ // usual suspect: ctrl+c exit
+ process.on('SIGINT', function () {
+ child && child.kill('SIGINT');
+ cleanup();
+ process.exit(0);
+ });
+
+ process.on('SIGTERM', function () {
+ cleanup();
+ process.exit(0);
+ });
+}
+
+// TODO on a clean exit, we could continue to monitor the directory and reboot the service
+
+// on exception *inside* nodemon, shutdown wrapped node app
+process.on('uncaughtException', function (err) {
+ util.log('[nodemon] exception in nodemon killing node');
+ util.error(err.stack);
+ cleanup();
+});
+
+
+if (program.options.delay) {
+ restartDelay = program.options.delay * 1000;
+}
+
+// this is the default - why am I making it a cmd line opt?
+if (program.options.js) {
+ addIgnoreRule('^((?!\.js|\.coffee$).)*$', true); // ignores everything except JS
+}
+
+if (program.options.watch && program.options.watch.length > 0) {
+ program.options.watch.forEach(function (dir) {
+ dirs.push(path.resolve(dir));
+ });
+} else {
+ dirs.unshift(process.cwd());
+}
+
+if (!program.app) {
+ help();
+}
+
+if (program.options.verbose) util.log('[nodemon] v' + meta.version);
+
+// this was causing problems for a lot of people, so now not moving to the subdirectory
+// process.chdir(path.dirname(app));
+dirs.forEach(function(dir) {
+ if (program.options.verbose) util.log('\x1B[32m[nodemon] watching: ' + dir + '\x1B[0m');
+});
+
+// findStatOffset();
+
+startNode();
+
+exists(ignoreFilePath, function (exist) {
+ // watch it: "exist" not to be confused with "exists"....
+ if (!exist) {
+ // try the old format
+ exists(oldIgnoreFilePath, function (exist) {
+ if (exist) {
+ if (program.options.verbose) util.log('[nodemon] detected old style .nodemonignore');
+ ignoreFilePath = oldIgnoreFilePath;
+ } else {
+ // don't create the ignorefile, just ignore the flag & JS
+ // addIgnoreRule(flag);
+ var ext = program.ext.replace(/\./g, '\\.');
+ if (ext) {
+ addIgnoreRule('^((?!' + ext + '$).)*$', true);
+ } else {
+ addIgnoreRule('^((?!\.js|\.coffee$).)*$', true); // ignores everything except JS
+ }
+ }
+ });
+ } else {
+ readIgnoreFile();
+ }
+});
View
120 node_modules/nodemon/README.md
@@ -0,0 +1,120 @@
+# nodemon
+
+For use during development of a node.js based application.
+
+nodemon will watch the files in the directory that nodemon was started, and if they change, it will automatically restart your node application.
+
+nodemon does **not** require *any* changes to your code or method of development. nodemon simply wraps your node application and keeps an eye on any files that have changed. Remember that nodemon is a replacement wrapper for `node`, think of it as replacing the word "node" on the command line when you run your script.
+
+# Installation
+
+Either through forking or by using [npm](http://npmjs.org) (the recommended way):
+
+ npm install nodemon -g
+
+And nodemon will be installed in to your bin path. Note that as of npm v1, you must explicitly tell npm to install globally as nodemon is a command line utility.
+
+# Usage
+
+nodemon wraps your application, so you can pass all the arguments you would normally pass to your app:
+
+ nodemon [your node app]
+
+For example, if my application accepted a host and port as the arguments, I would start it as so:
+
+ nodemon ./server.js localhost 8080
+
+Any output from this script is prefixed with `[nodemon]`, otherwise all output from your application, errors included, will be echoed out as expected.
+
+nodemon also supports running and monitoring [coffee-script](http://jashkenas.github.com/coffee-script/) apps:
+
+ nodemon server.coffee
+
+If no script is given, nodemon will test for a `package.json` file and if found, will run the file associated with the *main* property ([ref](https://github.com/remy/nodemon/issues/14)).
+
+You can also pass the debug flag to node through the command line as you would normally:
+
+ nodemon --debug ./server.js 80
+
+If you have a `package.json` file for your app, you can omit the main script entirely and nodemon will read the `package.json` for the `main` property and use that value as the app.
+
+# Automatic re-running
+
+nodemon was original written to restart hanging processes such as web servers, but now supports apps that cleanly exit. If your script exits cleanly, nodemon will continue to monitor the directory (or directories) and restart the script if there are any changes.
+
+# Running non-node scripts
+
+nodemon can also be used to execute and monitor other programs. nodemon will read the file extension of the script being run and monitor that extension instead of .js if there's no .nodemonignore:
+
+ nodemon --exec "python -v" ./app.py
+
+Now nodemon will run `app.py` with python in verbose mode (note that if you're not passing args to the exec program, you don't need the quotes), and look for new or modified files with the `.py` extension.
+
+# Monitoring multiple directories
+
+By default nodemon monitors the current working directory. If you want to take control of that option, use the `--watch` option to add specific paths:
+
+ nodemon --watch app --watch libs app/server.js
+
+Now nodemon will only restart if there are changes in the `./app` or `./libs` directory. By default nodemon will traverse sub-directories, so there's no need in explicitly including sub-directories.
+
+# Delaying restarting
+
+In some situations, you may want to wait until a number of files have changed. The timeout before checking for new file changes is 1 second. If you're uploading a number of files and it's taking some number of seconds, this could cause your app to restart multiple time unnecessarily.
+
+To add an extra throttle, or delay restarting, use the `--delay` command:
+
+ nodemon --delay 10 server.js
+
+The delay figure is number of seconds to delay before restarting. So nodemon will only restart your app the given number of seconds after the *last* file change.
+
+# Ignoring files
+
+By default, if nodemon will only restart when a `.js` JavaScript file changes. In some cases you will want to ignore some specific files, directories or file patterns, to prevent nodemon from prematurely restarting your application.
+
+You can use the [example ignore file](http://github.com/remy/nodemon/blob/master/nodemonignore.example) (note that this example file is not hidden - you must rename it to `.nodemonignore`) as a basis for your nodemon, but it's very simple to create your own:
+
+ # this is my ignore file with a nice comment at the top
+
+ /vendor/* # ignore all external submodules
+ /public/* # static files
+ ./README.md # a specific file
+ *.css # ignore any CSS files too
+
+The ignore file accepts:
+
+* Comments starting with a `#` symbol
+* Blank lines
+* Specific files
+* File patterns (this is converted to a regex, so you have full control of the pattern)
+
+# Controlling shutdown of your script
+
+nodemon sends a kill signal to your application when it sees a file update. If you need to clean up on shutdown inside your script you can capture the kill signal and handle it yourself.
+
+The following example will listen once for the `SIGUSR2` signal (used by nodemon to restart), run the clean up process and then kill itself for nodemon to continue control:
+
+ process.once('SIGUSR2', function () {
+ gracefulShutdown(function () {
+ process.kill(process.pid, 'SIGUSR2');
+ })
+ });
+
+Note that the `process.kill` is *only* called once your shutdown jobs are complete. Hat tip to [Benjie Gillam](http://www.benjiegillam.com/2011/08/node-js-clean-restart-and-faster-development-with-nodemon/) for writing technique this up.
+
+
+# Using nodemon with forever
+
+If you're using nodemon with [forever](https://github.com/nodejitsu/forever) (perhaps in a production environment) you can combine the two together. This way if the script crashes, forever restarts the script, and if there are file changes, nodemon restarts your script. For more detail, see [issue 30](https://github.com/remy/nodemon/issues/30).
+
+To acheive this you need to include the `--exitcrash` flag to ensure nodemon exits if the script crashes (or exits unexpectedly):
+
+ forever nodemon --exitcrash server.js
+
+To test this, you can kill the server.js process and forever will restart it. If you `touch server.js` nodemon will restart it.
+
+Note that I *would not* recommend using nodemon in a production environment - but that's because I wouldn't want it restart without my explicit instruction.
+
+# License
+
+MIT [http://rem.mit-license.org](http://rem.mit-license.org)
View
518 node_modules/nodemon/nodemon.js
@@ -0,0 +1,518 @@
+#!/usr/bin/env node
+var fs = require('fs'),
+ util = require('util'),
+ childProcess = require('child_process'),
+ dirs = [],
+ path = require('path'),
+ exists = fs.exists || path.exists, // yay, exists moved from path to fs in 0.7.x ... :-\
+ existsSync = fs.existsSync || path.existsSync,
+ spawn = childProcess.spawn,
+ meta = JSON.parse(fs.readFileSync(__dirname + '/package.json')),
+ exec = childProcess.exec,
+ flag = './.monitor',
+ program = getNodemonArgs(),
+ child = null,
+ monitor = null,
+ ignoreFilePath = './.nodemonignore',
+ oldIgnoreFilePath = './nodemon-ignore',
+ ignoreFiles = [],
+ reIgnoreFiles = null,
+ timeout = 1000, // check every 1 second
+ restartDelay = 0, // controlled through arg --delay 10 (for 10 seconds)
+ restartTimer = null,
+ lastStarted = +new Date,
+ statOffset = 0, // stupid fix for https://github.com/joyent/node/issues/2705
+ platform = process.platform,
+ isWindows = platform === 'win32',
+ noWatch = (platform !== 'win32') || !fs.watch, // && platform !== 'linux' - removed linux fs.watch usage #72
+ watchFile = platform === 'darwin' ? fs.watchFile : fs.watch, // lame :(
+ // create once, reuse as needed
+ reEscComments = /\\#/g,
+ reUnescapeComments = /\^\^/g, // note that '^^' is used in place of escaped comments
+ reComments = /#.*$/,
+ reTrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
+ reEscapeChars = /[.|\-[\]()\\]/g,
+ reAsterisk = /\*/g;
+
+// test to see if the version of find being run supports searching by seconds (-mtime -1s -print)
+if (noWatch) {
+ exec('find -L /dev/null -type f -mtime -1s -print', function(error, stdout, stderr) {
+ if (error) {
+ if (!fs.watch) {
+ util.error('\x1B[1;31mThe version of node you are using combined with the version of find being used does not support watching files. Upgrade to a newer version of node, or install a version of find that supports search by seconds.\x1B[0m');
+ process.exit(1);
+ } else {
+ noWatch = false;
+ }
+ }
+ });
+}
+
+function startNode() {
+ util.log('\x1B[32m[nodemon] starting `' + program.options.exec + ' ' + program.args.join(' ') + '`\x1B[0m');
+
+ child = spawn(program.options.exec, program.args);
+
+ lastStarted = +new Date;
+
+ child.stdout.on('data', function (data) {
+ util.print(data);
+ });
+
+ child.stderr.on('data', function (data) {
+ process.stderr.write(data);
+ });
+
+ child.on('exit', function (code, signal) {
+ // this is nasty, but it gives it windows support
+ if (isWindows && signal == 'SIGTERM') signal = 'SIGUSR2';
+ // exit the monitor, but do it gracefully
+ if (signal == 'SIGUSR2') {
+ // restart
+ startNode();
+ } else if (code === 0) { // clean exit - wait until file change to restart
+ util.log('\x1B[32m[nodemon] clean exit - waiting for changes before restart\x1B[0m');
+ child = null;
+ } else if (program.options.exitcrash) {
+ util.log('\x1B[1;31m[nodemon] app crashed\x1B[0m');
+ process.exit(0);
+ } else {
+ util.log('\x1B[1;31m[nodemon] app crashed - waiting for file changes before starting...\x1B[0m');
+ child = null;
+ }
+ });
+
+ // pinched from https://github.com/DTrejo/run.js - pipes stdin to the child process - cheers DTrejo ;-)
+ if (program.options.stdin) {
+ process.stdin.resume();
+ process.stdin.setEncoding('utf8');
+ process.stdin.pipe(child.stdin);
+ }
+
+ setTimeout(startMonitor, timeout);
+}
+
+function startMonitor() {
+ var changeFunction;
+
+ if (noWatch) {
+ // if native fs.watch doesn't work the way we want, we keep polling find command (mac only oddly)
+ changeFunction = function (lastStarted, callback) {
+ var cmds = [],
+ changed = [];
+
+ dirs.forEach(function(dir) {
+ cmds.push('find -L "' + dir + '" -type f -mtime -' + ((+new Date - lastStarted)/1000|0) + 's -print');
+ });
+
+ exec(cmds.join(';'), function (error, stdout, stderr) {
+ var files = stdout.split(/\n/);
+ files.pop(); // remove blank line ending and split
+ callback(files);
+ });
+ }
+ } else {
+ changeFunction = function (lastStarted, callback) {
+ // recursive watch - watch each directory and it's subdirectories, etc, etc
+ var watch = function (err, dir) {
+ try {
+ fs.watch(dir, { persistent: false }, function (event, filename) {
+ var filepath = path.join(dir, filename);
+ callback([filepath]);
+ });
+
+ fs.readdir(dir, function (err, files) {
+ if (!err) {
+ files.forEach(function (file) {
+ var filename = path.join(dir, file);
+ fs.stat(filename, function (err, stat) {
+ if (!err && stat) {
+ if (stat.isDirectory()) {
+ fs.realpath(filename, watch);
+ }
+ }
+ });
+ });
+ }
+ });
+ } catch (e) {
+ // ignoring this directory, likely it's "My Music"
+ // or some such windows fangled stuff
+ }
+ }
+ dirs.forEach(function (dir) {
+ fs.realpath(dir, watch);
+ });
+ }
+ }
+
+ var isWindows = process.platform === 'win32';
+ changeFunction(lastStarted, function (files) {
+ if (files.length) {
+ // filter ignored files
+ if (ignoreFiles.length) {
+ files = files.filter(function(file) {
+ // If we are in a Windows machine
+ if (isWindows) {
+ // Break up the file by slashes
+ var fileParts = file.split(/\\/g);
+
+ // Remove the first piece (C:)
+ fileParts.shift();
+
+ // Join the parts together with Unix slashes
+ file = '/' + fileParts.join('/');
+ }
+ return !reIgnoreFiles.test(file);
+ });
+ }
+
+
+ if (files.length) {
+ if (restartTimer !== null) clearTimeout(restartTimer);
+ restartTimer = setTimeout(function () {
+ if (program.options.verbose) util.log('[nodemon] restarting due to changes...');
+ files.forEach(function (file) {
+ if (program.options.verbose) util.log('[nodemon] ' + file);
+ });
+ if (program.options.verbose) util.print('\n\n');
+
+ if (child !== null) {
+ child.kill(isWindows ? '' : 'SIGUSR2');
+ } else {
+ startNode();
+ }
+ }, restartDelay);
+ return;
+ }
+ }
+
+ if (noWatch) setTimeout(startMonitor, timeout);
+ });
+}
+
+function addIgnoreRule(line, noEscape) {
+ // remove comments and trim lines
+ // this mess of replace methods is escaping "\#" to allow for emacs temp files
+ if (!noEscape) {
+ if (line = line.replace(reEscComments, '^^').replace(reComments, '').replace(reUnescapeComments, '#').replace(reTrim, '')) {
+ ignoreFiles.push(line.replace(reEscapeChars, '\\$&').replace(reAsterisk, '.*'));
+ }
+ } else if (line = line.replace(reTrim, '')) {
+ ignoreFiles.push(line);
+ }
+ reIgnoreFiles = new RegExp(ignoreFiles.join('|'));
+}
+
+function readIgnoreFile(curr, prev) {
+ // unless the ignore file was actually modified, do no re-read it
+ if(curr && prev && curr.mtime.valueOf() === prev.mtime.valueOf()) return;
+
+ if (platform === 'darwin') fs.unwatchFile(ignoreFilePath);
+
+ // Check if ignore file still exists. Vim tends to delete it before replacing with changed file
+ exists(ignoreFilePath, function(exists) {
+ if (program.options.verbose) util.log('[nodemon] reading ignore list');
+
+ // ignoreFiles = ignoreFiles.concat([flag, ignoreFilePath]);
+ // addIgnoreRule(flag);
+ addIgnoreRule(ignoreFilePath.substring(2)); // ignore the ./ part of the filename
+ fs.readFileSync(ignoreFilePath).toString().split(/\n/).forEach(function (rule, i) {
+ addIgnoreRule(rule);
+ });
+
+ watchFile(ignoreFilePath, { persistent: false }, readIgnoreFile);
+ });
+}
+
+// attempt to shutdown the wrapped node instance and remove
+// the monitor file as nodemon exists
+function cleanup() {
+ child && child.kill();
+ // fs.unlink(flag);
+}
+
+function getNodemonArgs() {
+ var args = process.argv,
+ len = args.length,
+ i = 2,
+ dir = process.cwd(),
+ indexOfApp = -1,
+ app = null;
+
+ for (; i < len; i++) {
+ if (existsSync(path.resolve(dir, args[i]))) {
+ // double check we didn't use the --watch or -w opt before this arg
+ if (args[i-1] && (args[i-1] == '-w' || args[i-1] == '--watch')) {
+ // ignore
+ } else {
+ indexOfApp = i;
+ break;
+ }
+ }
+ }
+
+ if (indexOfApp !== -1) {
+ // not found, so assume we're reading the package.json and thus swallow up all the args
+ // indexOfApp = len;
+ app = process.argv[i];
+ indexOfApp++;
+ } else {
+ indexOfApp = len;
+ }
+
+ var appargs = [], //process.argv.slice(indexOfApp),
+ // app = appargs[0],
+ nodemonargs = process.argv.slice(2, indexOfApp - (app ? 1 : 0)),
+ arg,
+ options = {
+ delay: 1,
+ watch: [],
+ exec: 'node',
+ verbose: true,
+ js: false, // becomes the default anyway...
+ includeHidden: false,
+ exitcrash: false,
+ stdin: true
+ // args: []
+ };
+
+ // process nodemon args
+ args.splice(0, 2);
+ while (arg = args.shift()) {
+ if (arg === '--help' || arg === '-h' || arg === '-?') {
+ return help(); // exits program
+ } else if (arg === '--version' || arg == '-v') {
+ return version(); // also exits
+ } else if (arg == '--js') {
+ options.js = true;
+ } else if (arg == '--quiet' || arg == '-q') {
+ options.verbose = false;
+ } else if (arg == '--hidden') {
+ options.includeHidden = true;
+ } else if (arg === '--watch' || arg === '-w') {
+ options.watch.push(args.shift());
+ } else if (arg === '--exitcrash') {
+ options.exitcrash = true;
+ } else if (arg === '--delay' || arg === '-d') {
+ options.delay = parseInt(args.shift());
+ } else if (arg === '--exec' || arg === '-x') {
+ options.exec = args.shift();
+ } else if (arg === '--no-stdin' || arg === '-I') {
+ options.stdin = false;
+ } else { //if (arg === "--") {
+ // Remaining args are node arguments
+ appargs.push(arg);
+ }
+ }
+
+ var program = { options: options, args: appargs, app: app };
+
+ getAppScript(program);
+
+ return program;
+}
+
+function getAppScript(program) {
+ var hokeycokey = false;
+ if (!program.args.length || program.app === null) {
+ // try to get the app from the package.json
+ // doing a try/catch because we can't use the path.exist callback pattern
+ // or we could, but the code would get messy, so this will do exactly
+ // what we're after - if the file doesn't exist, it'll throw.
+ try {
+ // note: this isn't nodemon's package, it's the user's cwd package
+ program.app = JSON.parse(fs.readFileSync('./package.json').toString()).main;
+ if (program.app === undefined) {
+ // no app found to run - so give them a tip and get the feck out
+ help();
+ }
+ program.args.unshift(program.app);
+ hokeycokey = true;
+ } catch (e) {}
+ }
+
+ if (!program.app) {
+ program.app = program.args[0];
+ }
+
+ program.app = path.basename(program.app);
+ program.ext = path.extname(program.app);
+
+ if (program.options.exec.indexOf(' ') !== -1) {
+ var execOptions = program.options.exec.split(' ');
+ program.options.exec = execOptions.splice(0, 1)[0];
+ program.args = execOptions.concat(program.args);
+ }
+
+ if (program.options.exec === 'node' && program.ext == '.coffee') {
+ program.options.exec = 'coffee';
+ }
+
+ if (program.options.exec === 'coffee') {
+ if (hokeycokey) {
+ program.args.push(program.args.shift());
+ }
+ //coffeescript requires --nodejs --debug
+ var debugIndex = program.args.indexOf('--debug');
+ if (debugIndex === -1) debugIndex = program.args.indexOf('--debug-brk');
+ if (debugIndex !== -1 && program.args.indexOf('--nodejs') === -1) {
+ program.args.splice(debugIndex, 0, '--nodejs');
+ }
+ // monitor both types - TODO possibly make this an option?
+ program.ext = '.coffee|.js';
+ if (!program.options.exec || program.options.exec == 'node') program.options.exec = 'coffee';
+
+ // because windows can't find 'coffee', it needs the real file 'coffee.cmd'
+ if (isWindows) program.options.exec += '.cmd';
+ }
+}
+
+function findStatOffset() {
+ var filename = './.stat-test';
+ fs.writeFile(filename, function (err) {
+ if (err) return;
+ fs.stat(filename, function (err, stat) {
+ if (err) return;
+
+ statOffset = stat.mtime.getTime() - new Date().getTime();
+ fs.unlink(filename);
+ });
+ });
+}
+
+function version() {
+ console.log(meta.version);
+ process.exit(0);
+}
+
+function help() {
+ util.print([
+ '',
+ ' Usage: nodemon [options] [script.js] [args]',
+ '',
+ ' Options:',
+ '',
+ ' -d, --delay n throttle restart for "n" seconds',
+ ' -w, --watch dir watch directory "dir". use once for each',
+ ' directory to watch',
+ ' -x, --exec app execute script with "app", ie. -x "python -v"',
+ ' -I, --no-stdin don\'t try to read from stdin',
+ ' -q, --quiet minimise nodemon messages to start/stop only',
+ ' --exitcrash exit on crash, allows use of nodemon with',
+ ' daemon tools like forever.js',
+ ' -v, --version current nodemon version',
+ ' -h, --help you\'re looking at it',
+ '',
+ ' Note: if the script is omitted, nodemon will try to ',
+ ' read "main" from package.json and without a .nodemonignore,',
+ ' nodemon will monitor .js and .coffee by default.',
+ '',
+ ' Examples:',
+ '',
+ ' $ nodemon server.js',
+ ' $ nodemon -w ../foo server.js apparg1 apparg2',
+ ' $ PORT=8000 nodemon --debug-brk server.js',
+ ' $ nodemon --exec python app.py',
+ '',
+ ' For more details see http://github.com/remy/nodemon/',
+ ''
+ ].join('\n') + '\n');
+ process.exit(0);
+}
+
+// this little bit of hoop jumping is because sometimes the file can't be
+// touched properly, and it send nodemon in to a loop of restarting.
+// this way, the .monitor file is removed entirely, and recreated with
+// permissions that anyone can remove it later (i.e. if you run as root
+// by accident and then try again later).
+// if (path.existsSync(flag)) fs.unlinkSync(flag);
+// fs.writeFileSync(flag, '.'); // requires some content https://github.com/remy/nodemon/issues/36
+// fs.chmodSync(flag, '666');
+
+// remove the flag file on exit
+process.on('exit', function (code) {
+ if (program.options.verbose) util.log('[nodemon] exiting');
+ cleanup();
+});
+
+if (!isWindows) { // because windows borks when listening for the SIG* events
+ // usual suspect: ctrl+c exit
+ process.on('SIGINT', function () {
+ child && child.kill('SIGINT');
+ cleanup();
+ process.exit(0);
+ });
+
+ process.on('SIGTERM', function () {
+ cleanup();
+ process.exit(0);
+ });
+}
+
+// TODO on a clean exit, we could continue to monitor the directory and reboot the service
+
+// on exception *inside* nodemon, shutdown wrapped node app
+process.on('uncaughtException', function (err) {
+ util.log('[nodemon] exception in nodemon killing node');
+ util.error(err.stack);
+ cleanup();
+});
+
+
+if (program.options.delay) {
+ restartDelay = program.options.delay * 1000;
+}
+
+// this is the default - why am I making it a cmd line opt?
+if (program.options.js) {
+ addIgnoreRule('^((?!\.js|\.coffee$).)*$', true); // ignores everything except JS
+}
+
+if (program.options.watch && program.options.watch.length > 0) {
+ program.options.watch.forEach(function (dir) {
+ dirs.push(path.resolve(dir));
+ });
+} else {
+ dirs.unshift(process.cwd());
+}
+
+if (!program.app) {
+ help();
+}
+
+if (program.options.verbose) util.log('[nodemon] v' + meta.version);
+
+// this was causing problems for a lot of people, so now not moving to the subdirectory
+// process.chdir(path.dirname(app));
+dirs.forEach(function(dir) {
+ if (program.options.verbose) util.log('\x1B[32m[nodemon] watching: ' + dir + '\x1B[0m');
+});
+
+// findStatOffset();
+
+startNode();
+
+exists(ignoreFilePath, function (exist) {
+ // watch it: "exist" not to be confused with "exists"....
+ if (!exist) {
+ // try the old format
+ exists(oldIgnoreFilePath, function (exist) {
+ if (exist) {
+ if (program.options.verbose) util.log('[nodemon] detected old style .nodemonignore');
+ ignoreFilePath = oldIgnoreFilePath;
+ } else {
+ // don't create the ignorefile, just ignore the flag & JS
+ // addIgnoreRule(flag);
+ var ext = program.ext.replace(/\./g, '\\.');
+ if (ext) {
+ addIgnoreRule('^((?!' + ext + '$).)*$', true);
+ } else {
+ addIgnoreRule('^((?!\.js|\.coffee$).)*$', true); // ignores everything except JS
+ }
+ }
+ });
+ } else {
+ readIgnoreFile();
+ }
+});
View
11 node_modules/nodemon/nodemonignore.example
@@ -0,0 +1,11 @@
+# .nodemonignore file
+#
+# This is an example of a comment
+#
+# You can list individual files in the ignore list, or they can be
+# file patterns. For example:
+#
+# /vendor/*
+# /public/css/styles.css
+# ./server.js
+# ./.git/*
View
48 node_modules/nodemon/package.json
@@ -0,0 +1,48 @@
+{
+ "name": "nodemon",
+ "homepage": "http://github.com/remy/nodemon",
+ "author": {
+ "name": "Remy Sharp",
+ "url": "http://github.com/remy"
+ },
+ "bin": {
+ "nodemon": "./nodemon.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/remy/nodemon.git"
+ },
+ "description": "Simple monitor script for use during development of a node.js app.",
+ "keywords": [
+ "monitor",
+ "development",
+ "restart",
+ "autoload",
+ "reload",
+ "terminal"
+ ],
+ "version": "0.6.18",
+ "preferGlobal": "true",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://rem.mit-license.org"
+ }
+ ],
+ "main": "./nodemon",
+ "_id": "nodemon@0.6.18",
+ "dependencies": {},
+ "devDependencies": {},
+ "optionalDependencies": {},
+ "engines": {
+ "node": "*"
+ },
+ "_engineSupported": true,
+ "_npmVersion": "1.1.19",
+ "_nodeVersion": "v0.6.15",
+ "_defaultsLoaded": true,
+ "dist": {
+ "shasum": "915b74f49a985ae5702b07a26f61b4a8245fdbcf"
+ },
+ "_from": "nodemon"
+}
View
BIN  public/assets/textures/.DS_Store
Binary file not shown
View
BIN  public/assets/textures/planets/earth_white_debug_red.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
32 public/config.js
@@ -1,7 +1,7 @@
/* Config
*/
-var DEBUG = false;
+var DEBUG = 1;
if (!DEBUG || typeof console == "undefined" || typeof console.log == "undefined") var console = { log: function() {} };
function getURLParameter(paramName) {
@@ -38,7 +38,6 @@ function genQueryString(params, name) {
//APP GLOBALS
var tilt = 0.41;
-var IS_REMOTE_CONTROLLED = getURLParameter('rc');
var IS_IPAD = navigator.userAgent.match(/iPad/i) != null;
var VIRTUAL_PHYSICAL_FACTOR = 1;
var CAMERA_FOV = 50;
@@ -51,24 +50,36 @@ var COLOR_RANGE = 2;
var lensTag = getURLParameter('lens_tag') || false;
var globeTag = getURLParameter('globe_tag') || false;
-var IS_AR = IS_IPAD || lensTag != false;
+//var IS_AR = IS_IPAD || lensTag != false;
var IS_TAGGED_GLOBE = globeTag != false;
+var IS_LOUPE = getURLParameter('loupe') || false;
+var IS_AR = (!IS_LOUPE ? (!IS_IPAD && !getURLParameter('ar') ? false : lensTag != false) : false);
+var IS_GESTURAL = getURLParameter('gestural') || false;
+
+var IS_TOP_DOWN = getURLParameter('top_down') || false;
+
+var radius = 145; //6371
+
+
+var LOUPE_FOCAL_DISTANCE = radius * 2.5;
+var LOUPE_STRENGTH = CAMERA_FOV * .95;
+
var WORLD_ROT_X = Math.PI / 2;
var WORLD_ROT_Y = -Math.PI / 2;
var WORLD_ROT_Z = tilt;
-
-if (IS_AR) {
- var WORLD_ROT_Z = 0;
-}
+var WORLD_FIXED_DIST = !IS_TOP_DOWN ? radius * 3 : 640;
+if (IS_AR || IS_TOP_DOWN || IS_TAGGED_GLOBE) {
+ WORLD_ROT_Z = 0;
+}
var taggedObjects = [
new TaggedObject('globe', [
- new ObjectTag((globeTag && globeTag != 1 ? globeTag : 'Right-Hand-2'), [0, 0, 0])
+ new ObjectTag((globeTag && globeTag != 1 ? globeTag : 'Left-Hand-3'), [0, 0, 0])
]),
new TaggedObject('lens', [
- new ObjectTag((lensTag && lensTag != 1 ? lensTag : 'Left-Hand-2'), [0, 0, 0]),
+ new ObjectTag((lensTag && lensTag != 1 ? lensTag : 'Left-Hand-1'), [0, 0, 0]),
//new ObjectTag('Object-03', [-150, 100, 0]),
//new ObjectTag('Object-06', [150, -100, 0])
])
@@ -102,8 +113,7 @@ var _chatVisible = false;
//VARIABLES FOR THREE.JS
-var radius = 145, //6371,
-rotationSpeed = 0.1,
+var rotationSpeed = 0.1,
cloudsScale = 1.005,
moonScale = 0.23,
height = window.innerHeight,
View
48 public/libs/globe.js
@@ -70,8 +70,8 @@ DAT.Globe = function(container, colorFn) {
}
};
- var camera, scene, scenePoints, world, sceneAtmosphere, renderer, w, h, meshDebug;
- var meshPlanet, atmosphere, point;
+ var camera, scene, scenePoints, world, sceneAtmosphere, renderer, w, h, meshDebug, roiMarker;
+ var meshPlanet, planetExtents, atmosphere, point;
var controls;
var overRenderer;
@@ -96,7 +96,7 @@ DAT.Globe = function(container, colorFn) {
function init() {
var shader, uniforms, material;
- if (IS_AR && IS_IPAD) {
+ if ((IS_AR || IS_LOUPE) && IS_IPAD) {
w = 768;
h = 1024;
} else {
@@ -197,8 +197,13 @@ DAT.Globe = function(container, colorFn) {
//world.rotation.z = tilt;
world.add( meshPlanet );
+ geometry = new THREE.SphereGeometry( radius, 1, 1 );
+ planetExtents = new THREE.Mesh(geometry);
+ planetExtents.visible = false;
+ world.add(planetExtents);
- if (DEBUG && (IS_AR || IS_TAGGED_GLOBE)) {
+
+ if (DEBUG && (IS_AR || IS_LOUPE || IS_TAGGED_GLOBE)) {
var plane = new THREE.Mesh(new THREE.CubeGeometry(150, 10, 250, 2, 2), new THREE.MeshBasicMaterial({
color: 0x0000ff,
opacity: .5
@@ -215,6 +220,23 @@ DAT.Globe = function(container, colorFn) {
}
+ geometry = new THREE.CubeGeometry(aspectRatio, 1, 1, 2, 2);
+ geometry = new THREE.CylinderGeometry(1, 1, 1, 30, 1, false);
+
+ var roiWidget = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
+ color: 0xffff88,
+ opacity: (IS_TOP_DOWN ? .8 : .4),
+ transparent: true,
+ }));
+
+ roiWidget.overdraw = true;
+ roiMarker = new THREE.Object3D();
+ roiMarker.add(roiWidget);
+ roiWidget.rotation.x = Math.PI / 2;
+ //roiMarker.visible = false;
+
+ scene.add(roiMarker);
+
scene.add(new THREE.AmbientLight( 0xffffff ));
@@ -275,7 +297,7 @@ DAT.Globe = function(container, colorFn) {
}, false);
- if (!IS_AR && !IS_TAGGED_GLOBE) {
+ if (!IS_AR && !IS_TAGGED_GLOBE && !IS_LOUPE) {
controls = new THREE.TrackballControls( camera, renderer.domElement );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
@@ -356,7 +378,11 @@ DAT.Globe = function(container, colorFn) {
lng = data[i + 1];
color = colorFnWrapper(data,i);
//console.log(data);
- size = data[i + 2] * barHeight;
+ if (!IS_TOP_DOWN) {
+ size = data[i + 2] * barHeight;
+ } else {
+ size = barHeight * .05;
+ }
addPoint(lat, lng, size, color, subgeo);
}
if (opts.animated) {
@@ -374,7 +400,8 @@ DAT.Globe = function(container, colorFn) {
color: 0xffffff,
vertexColors: THREE.FaceColors,
morphTargets: false,
- transparent: true, blending: THREE.AdditiveBlending
+ transparent: true, blending: THREE.AdditiveBlending,
+ opacity: .75
}));
} else {
if (this._baseGeometry.morphTargets.length < 8) {
@@ -390,7 +417,9 @@ DAT.Globe = function(container, colorFn) {
color: 0xffffff,
vertexColors: THREE.FaceColors,
morphTargets: true,
- //transparent: true, blending: THREE.AdditiveBlending
+ transparent: true, blending: THREE.AdditiveBlending,
+ opacity: .75
+
}));
}
world.add(this.points);
@@ -490,8 +519,11 @@ DAT.Globe = function(container, colorFn) {
this.animate = animate;
this.render = render;
this.world = world;
+ this.planet = meshPlanet;
+ this.planetExtents = planetExtents;
CAMERA = this.camera = camera;
this.debugMarker = meshDebug;
+ this.roiMarker = roiMarker;
this.__defineGetter__('time', function() {
return this._time || 0;
View
2  public/styles/map-gl.css
@@ -24,6 +24,6 @@
/*
AR version
*/
-body.ipad, .ipad .map-gl-view {
+body.ar-lens, .ar-lens .map-gl-view {
background: transparent !important;
}
View
112 public/template/sidebar-experimental.html
@@ -0,0 +1,112 @@
+<div class="settings-tab" id="settingsTab"><h4 id="settingsTabText">HIDE</h4></div>
+<div class="data-container" id="dataContainer">
+ <div class="accordion" id="accordion">
+ <div class="accordion-group">
+ <div class="accordion-heading">
+ <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" href="#collapseOne">Comments</a>
+ </div>
+ <div id="collapseOne" class="accordion-body collapse">
+ <div class="accordion-inner">
+ <div class="data-toggle">
+ <div class="btn-group" data-toggle="buttons-radio">
+ <button class="btn active" id="toggleCommentsVisible">Visible</button>
+ <button class="btn" id="toggleCommentsHidden">Hidden</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="toggle-group" id="dataManager">
+ <div class="data-toggle double">
+ <button class="btn data first" id="addData">Add New Data</button>
+ <button class="btn data second" id="addDataLibrary">Browse Data Library</button>
+ </div>
+ </div>
+ <div class="divider large"></div>
+ <div class="toggle-group">
+ <div class="data-toggle">
+ <p>Display</p>
+ <div class="btn-group" data-toggle="buttons-radio">
+ <button id="display2D" class="btn">Flat map</button>
+ <button id="display3D" class="btn">3D Globe</button>
+ </div>
+ </div>
+ </div>
+ <div class="toggle-group" id="themeToggleGroup">
+ <div class="data-toggle">
+ <p>Theme</p>
+ <div class="btn-group" data-toggle="buttons-radio">
+ <button id="dark_theme" class="btn active">Dark</button>
+ <button id="light_theme" class="btn ">Light</button>
+ <button id="standard_theme" class="btn">Standard</button>
+ </div>
+ </div>
+ </div>
+ <div class="toggle-group" id="scaleToggleGroup">
+ <div class="data-toggle">
+ <p>Scale</p>
+ <div class="btn-group" data-toggle="buttons-radio">
+ <button id="scale_linear" class="btn">Linear</button>
+ <button id="scale_log" class="btn">Logarhitmic</button>
+ </div>
+ </div>
+ </div>
+ <div class="toggle-group" id="couplingToggleGroup">
+ <div class="data-toggle">
+ <p>Coupling</p>
+ <div class="btn-group" data-toggle="buttons-radio">
+ <button id="couple_lens" class="btn">AR Lens</button>
+ <button id="couple_mag" class="btn">Loupe</button>
+ <button id="couple_rc" class="btn">RC</button>
+ </div>
+ </div>
+ </div>
+ <div class="toggle-group clearfix" id="arToggleGroup">
+ <div class="data-toggle">
+ <div class="slider" id="world-fixed-dist"></div>
+ <p>WORLD_FIXED_DIST: <span id="world-fixed-dist-val"></span></p>
+ </div>
+ <div class="data-toggle">
+ <div class="slider" id="world-rot-x"></div>
+ <p>WORLD_ROT_X: <span id="world-rot-x-val"></span></p>
+ </div>
+ <div class="data-toggle">
+ <div class="slider" id="world-rot-y"></div>
+ <p>WORLD_ROT_Y: <span id="world-rot-y-val"></span></p>
+ </div>
+ <div class="data-toggle">
+ <div class="slider" id="world-rot-z"></div>
+ <p>WORLD_ROT_Z: <span id="world-rot-z-val"></span></p>
+ </div>
+ <div class="data-toggle">
+ <div class="slider" id="camera-fov"></div>
+ <p>CAMERA_FOV: <span id="camera-fov-val"></span></p>
+ </div>
+ <div class="data-toggle" style="margin-top: 1em">
+ <div class="slider" id="virtual-physical-factor"></div>
+ <p>VIRTUAL_PHYSICAL_FACTOR: <span id="virtual-physical-factor-val"></span></p>
+ </div>
+ <div class="data-toggle" style="margin-top: 1em">
+ <div class="slider" id="lens-ofs-x"></div>
+ <p>LENS OFFSET X: <span id="lens-ofs-x-val"></span></p>
+ </div>
+ <div class="data-toggle" style="margin-top: 1em">
+ <div class="slider" id="lens-ofs-y"></div>
+ <p>LENS OFFSET Y: <span id="lens-ofs-y-val"></span></p>
+ </div>
+ <div class="data-toggle" style="margin-top: 1em">
+ <div class="slider" id="lens-ofs-z"></div>
+ <p>LENS OFFSET Z: <span id="lens-ofs-z-val"></span></p>
+ </div>
+ <div class="" id="tracking-info" style="margin-top: 2em; background: white !important; font-family: courier; border: 1px #888 solid; height: 50px">
+ no tracking info received
+ </div>
+ </div>
+
+
+ <!--
+ <button id="tweetButton">Get Tweets</button>
+ -->
+
+</div>
View
43 public/template/sidebar.html
@@ -43,6 +43,8 @@
</div>
</div>
</div>
+
+ <!--
<div class="toggle-group" id="scaleToggleGroup">
<div class="data-toggle">
<p>Scale</p>
@@ -52,46 +54,7 @@
</div>
</div>
</div>
- <div class="toggle-group clearfix" id="arToggleGroup">
- <div class="data-toggle">
- <div class="slider" id="world-rot-x"></div>
- <p>WORLD_ROT_X: <span id="world-rot-x-val"></span></p>
- </div>
- <div class="data-toggle">
- <div class="slider" id="world-rot-y"></div>
- <p>WORLD_ROT_Y: <span id="world-rot-y-val"></span></p>
- </div>
- <div class="data-toggle">
- <div class="slider" id="world-rot-z"></div>
- <p>WORLD_ROT_Z: <span id="world-rot-z-val"></span></p>
- </div>
- <!--
- -->
- <div class="data-toggle">
- <div class="slider" id="camera-fov"></div>
- <p>CAMERA_FOV: <span id="camera-fov-val"></span></p>
- </div>
- <div class="data-toggle" style="margin-top: 1em">
- <div class="slider" id="virtual-physical-factor"></div>
- <p>VIRTUAL_PHYSICAL_FACTOR: <span id="virtual-physical-factor-val"></span></p>
- </div>
- <div class="data-toggle" style="margin-top: 1em">
- <div class="slider" id="lens-ofs-x"></div>
- <p>LENS OFFSET X: <span id="lens-ofs-x-val"></span></p>
- </div>
- <div class="data-toggle" style="margin-top: 1em">
- <div class="slider" id="lens-ofs-y"></div>
- <p>LENS OFFSET Y: <span id="lens-ofs-y-val"></span></p>
- </div>
- <div class="data-toggle" style="margin-top: 1em">
- <div class="slider" id="lens-ofs-z"></div>
- <p>LENS OFFSET Z: <span id="lens-ofs-z-val"></span></p>
- </div>
- <div class="" id="tracking-info" style="margin-top: 2em; background: white !important; font-family: courier; border: 1px #888 solid; height: 50px">
- no tracking info received
- </div>
- </div>
-
+ -->
<!--
<button id="tweetButton">Get Tweets</button>
View
104 public/views/map-gl-view.js
@@ -31,24 +31,29 @@ window.MapGLView = window.MapViewBase.extend({
};
},
+ getMatrixFromTag: function(obj, switchON) {
+ var norm = !switchON ? obj.norm.normalize() : obj.over.normalize();
+ var over = !switchON ? obj.over.normalize() : obj.norm.normalize();
+ var up = new THREE.Vector3().cross(obj.norm, obj.over).normalize();
+ var matrix = new THREE.Matrix4(
+ over.x, up.x, norm.x, 0,
+ over.y, up.y, norm.y, 0,
+ over.z, up.z, norm.z, 0,
+ 0, 0, 0, 1
+ );
+ return matrix;
+ },
+
updateTaggedObject: function(obj)
{
if (!this.globe) return;
+ //console.log(obj.name);
switch (obj.name) {
case 'globe':
- if (!IS_AR && !IS_TAGGED_GLOBE) break;
+ if (!IS_AR && !IS_TAGGED_GLOBE && !IS_LOUPE) break;
// get tag matrix
- var norm = obj.norm.normalize();
- var over = obj.over.normalize();
- var up = new THREE.Vector3().cross(obj.norm, obj.over).normalize();
- var matrix = new THREE.Matrix4(
- over.x, up.x, norm.x, 0,
- over.y, up.y, norm.y, 0,
- over.z, up.z, norm.z, 0,
- 0, 0, 0, 1
- );
-
+ var matrix = this.getMatrixFromTag(obj);
var rotMatrix = new THREE.Matrix4();
rotMatrix.setRotationX(WORLD_ROT_X);
matrix.multiplySelf(rotMatrix);
@@ -72,25 +77,77 @@ window.MapGLView = window.MapViewBase.extend({
this.globe.debugMarker.position = new THREE.Vector3().copy(obj.loc);
}
- if (!IS_AR) {
+ if (IS_TAGGED_GLOBE && !IS_AR && (!IS_LOUPE || IS_TOP_DOWN)) {
this.globe.camera.position = new THREE.Vector3().copy(this.globe.world.position);
- this.globe.camera.position.z += radius * 2;
- this.globe.world.position.y -= radius * 2.5;
+ this.globe.camera.position.y += WORLD_FIXED_DIST;
+ if (!IS_TOP_DOWN) {
+ this.globe.camera.position.z += WORLD_FIXED_DIST * .4;
+ } else {
+ var rotMatrix = new THREE.Matrix4();
+ this.globe.camera.up.getRotationFromMatrix(rotMatrix);
+ }
this.globe.camera.lookAt(this.globe.world.position);
}
break;
case 'lens':
- if (!IS_AR) break;
- // set camera position and up vectors to respective lens tag vectors
- this.globe.camera.position = new THREE.Vector3().copy(obj.loc);
- this.globe.camera.up = new THREE.Vector3().cross(obj.norm, obj.over);
- this.globe.camera.updateProjectionMatrix();
+ if (!IS_AR && !IS_LOUPE) break;
// look at camera position plus inverse norm vector of lens tag
- var invNorm = new THREE.Vector3().copy(obj.norm).multiplyScalar(-1);
- var newLookAt = new THREE.Vector3().add(obj.loc, invNorm);
- this.globe.camera.lookAt(newLookAt);
+ var invNorm;
+ if (!IS_GESTURAL) {
+ invNorm = new THREE.Vector3().copy(obj.norm).multiplyScalar(-1);
+ } else {
+ invNorm = new THREE.Vector3().copy(obj.over);
+ }
+
+ if (!IS_TOP_DOWN) {
+ // set camera position and up vectors to respective lens tag vectors
+ this.globe.camera.position = new THREE.Vector3().copy(obj.loc);
+ this.globe.camera.up = new THREE.Vector3().cross(obj.norm, obj.over);
+ var newLookAt = new THREE.Vector3().add(obj.loc, invNorm);
+ this.globe.camera.lookAt(newLookAt);
+ }
+
+ if (IS_LOUPE) {
+
+ var ray = new THREE.Ray(obj.loc, invNorm);
+ var intersect = ray.intersectObject(this.globe.planetExtents);
+
+ if (intersect && intersect.length) {
+ console.log('intersect');
+ var i = intersect[0];
+ var loupeDist = Math.min(Math.abs(i.distance - LOUPE_FOCAL_DISTANCE), LOUPE_FOCAL_DISTANCE);
+ var zoom = (LOUPE_FOCAL_DISTANCE - loupeDist) / LOUPE_FOCAL_DISTANCE;
+
+ var newFov = CAMERA_FOV - LOUPE_STRENGTH * zoom;
+
+ if (!IS_TOP_DOWN && this.globe.camera.fov != newFov) {
+ this.globe.camera.fov = newFov;
+ this.globe.camera.updateProjectionMatrix();
+ }
+
+ if (IS_TOP_DOWN) {
+ this.globe.roiMarker.position = new THREE.Vector3().copy(this.globe.world.position);
+ this.globe.roiMarker.rotation = new THREE.Vector3().getRotationFromMatrix(this.getMatrixFromTag(obj, IS_GESTURAL));
+ this.globe.roiMarker.position.addSelf(invNorm.multiplyScalar(-radius));
+ var roiScale = 1 - zoom;
+ var roiSize = 300;
+ this.globe.roiMarker.scale.x = roiScale * roiSize;
+ this.globe.roiMarker.scale.y = roiScale * roiSize;
+ this.globe.roiMarker.visible = true;
+ }
+ } else {
+ this.globe.roiMarker.visible = false;
+ }
+
+ /*
+ var loupePos = invNorm.multiplyScalar(300);
+ console.log(loupePos.length());
+ console.log('loupe');
+ this.globe.camera.position.x += 100;
+ */
+ }
break;
}
@@ -160,6 +217,9 @@ window.MapGLView = window.MapViewBase.extend({
if (IS_IPAD) {
$('body').addClass('ipad');
}
+ if (IS_AR) {
+ $('body').addClass('ar-lens');
+ }
if (DEBUG) {
this.stats = new Stats();
View
16 public/views/sidebar-view.js
@@ -26,7 +26,7 @@ window.SideBarView = Backbone.View.extend({
_.bindAll(this, "setToggleStates");
options.vent.bind("setToggleStates", this.setToggleStates);
- if (IS_AR || IS_TAGGED_GLOBE || IS_REMOTE_CONTROLLED) {
+ if (IS_AR || IS_LOUPE || IS_TAGGED_GLOBE) {
new OblessdClient({vent: options.vent, taggedObjects: taggedObjects, track: true});
}
},
@@ -47,9 +47,21 @@ window.SideBarView = Backbone.View.extend({
}
this.$('#scale_linear').addClass('active');
- if (!DEBUG || !IS_AR && !IS_TAGGED_GLOBE) {
+ if (!DEBUG || (!IS_AR && !IS_LOUPE && !IS_TAGGED_GLOBE)) {
this.$('#arToggleGroup').remove();
} else {
+ this.$("#world-fixed-dist-val").text(WORLD_FIXED_DIST);
+ this.$('#world-fixed-dist').slider({
+ value: WORLD_FIXED_DIST,
+ min: 0,
+ max: radius * 10,
+ step: 1,
+ range: 'max',
+ slide: function( event, ui ) {
+ self.$("#world-fixed-dist-val").text(ui.value);
+ WORLD_FIXED_DIST = ui.value;
+ },
+ });
this.$("#world-rot-x-val").text(WORLD_ROT_X);
this.$('#world-rot-x').slider({
value: WORLD_ROT_X,
View
7 scripts/reduce-points.js
@@ -130,7 +130,7 @@ var ReductionKey = {
var loc = [grid_lng * grid_size, grid_lat * grid_size];
return [
grid_lng + ',' + grid_lat + ',' + grid_size,
- [grid_lng * grid_size, grid_lat * grid_size]
+ [grid_lng * grid_size + grid_size / 2, grid_lat * grid_size + grid_size / 2]
];
};
this.name = 'loc-'+this.grid_size;
@@ -297,7 +297,7 @@ var reducePoints = function(reduction_keys, opts) {
}, opts);
};
-use geo;
+
var cur = db.pointcollections.find({});
cur.forEach(function(collection) {
print('*** collection = '+collection.title+' ('+collection._id+') ***');
@@ -311,11 +311,12 @@ cur.forEach(function(collection) {
loc: new ReductionKey.LocGrid(grid_size)
}, opts);
- reducePoints({
+ /*reducePoints({
collectionid: ReductionKey.copy,
loc: new ReductionKey.LocGrid(grid_size),
datetime: new ReductionKey.Weekly()
}, opts);
+*/
/*
reducePoints({
collectionid: ReductionKey.copy,
Please sign in to comment.
Something went wrong with that request. Please try again.