<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

var path_ = require('path');
var fs_   = require('fs');

process.on('unhandledRejection', function(e) {
  console.error("ERROR: Unhandled promise rejection ", e);
  process.exit(1);
});

// enable FOAM java support.
global.FOAM_FLAGS = { 'java': true, 'debug': true, 'js': false, 'swift': true };

// Enable FOAMLink mode but only if FOAMLINK_DATA is set in environment
var foamlinkMode = process.env.hasOwnProperty('FOAMLINK_DATA');
if ( foamlinkMode ) {
  global.FOAMLINK_DATA = process.env['FOAMLINK_DATA'];
}

var logger = {};
logger.debug = () => {};

// Store debug files but only if DEBUG_DATA_DIR is set in environment
var debugDataDir = null;
if ( process.env.hasOwnProperty('DEBUG_DATA_DIR') ) {
  debugDataDir = process.env['DEBUG_DATA_DIR'];
  logger.debug = (...args) => {
    fs_.appendFileSync(path_.join(debugDataDir, 'debugLog.jrl'),
      args.join('p({"type": "debug", "value": ' + JSON.stringify(args) + '})\n')
    );
  };
}

require('../src/foam.js');
require('../src/foam/nanos/nanos.js');
require('../src/foam/support/support.js');

var srcPath = __dirname + "/../src/";

if ( ! (
  process.argv.length == 4 ||
  process.argv.length == 5 ||
  process.argv.length == 6 ) ) {
  console.log("USAGE: genjava.js input-path output-path src-path(optional) files-to-update (optional)");
  process.exit(1);
}

if ( process.argv.length > 4 && process.argv[4] !== '--' ) {
  srcPath = process.argv[4];
  if ( ! srcPath.endsWith('/') ) {
    srcPath = srcPath + '/';
  }
}

var incrementalMeta = null;
if ( process.argv.length > 5  &&
     process.argv[5] !== '--' &&
     process.argv[5] != '' ) {
  incrementalMeta = JSON.parse(process.argv[5]);
  logger.debug('INCREMENTAL', process.argv[5]);
}

var indir = process.argv[2];
indir = path_.resolve(path_.normalize(indir));

var externalFile = require(indir);
var classes = externalFile.classes;
var abstractClasses = externalFile.abstractClasses;
var skeletons = externalFile.skeletons;
var proxies = externalFile.proxies;

var blacklist = {}
externalFile.blacklist.forEach(function(cls) {
  blacklist[cls] = true;
});

var fileWhitelist = null;
var classesNotFound = {};
var classesFound = {};
var debugFilesWritten = [];

// Set file whitelist from parsed argument, but only if foamlink is enabled
if ( incrementalMeta !== null && foamlinkMode ) {
  fileWhitelist = {}; // set
  for ( var i = 0; i < incrementalMeta.modified.length; i++ ) {
    let relativePath = path_.relative(process.cwd(), incrementalMeta.modified[i]);
    fileWhitelist[relativePath] = true;
  }
}
logger.debug('fileWhitelist', fileWhitelist);

[
  'FObject',
  'foam.core.AbstractEnum',
  'foam.core.AbstractInterface',
  'foam.core.Property',
  'foam.core.String',

  // These have hand written java impls so we don't want to clobber them.
  // TODO: Change gen.sh to prefer hand written java files over generated.
  'foam.dao.LimitedDAO',
  'foam.dao.OrderedDAO',
  'foam.dao.SkipDAO',

  // TODO: These models currently don't compile in java but could be updated to
  // compile properly.
  'foam.blob.BlobBlob',
  'foam.dao.CompoundDAODecorator',
  'foam.dao.DAOInterceptor',
  'foam.dao.FlowControl',
  'foam.dao.sync.SyncRecord',
  'foam.dao.sync.VersionedSyncRecord',
  'foam.mlang.Expressions',
  'foam.nanos.menu.MenuBar',

  'foam.box.Context',
//  'foam.box.HTTPBox',
//  'foam.box.SessionClientBox',
  'foam.box.SocketBox',
  'foam.box.WebSocketBox',
  'foam.box.TimeoutBox',
  'foam.box.RetryBox',
  'foam.dao.TTLCachingDAO',
  'foam.dao.CachingDAO',
  'foam.dao.CompoundDAODecorator',
  'foam.dao.InterceptedDAO',
  'foam.dao.DeDupDAO',
  'foam.dao.IDBDAO',
  'foam.dao.LoggingDAO',
  'foam.dao.MDAO',
  'foam.dao.RequestResponseClientDAO',
  'foam.dao.SyncDAO',
  'foam.dao.TimingDAO'
].forEach(function(cls) {
  blacklist[cls] = true;
});

var outdir = process.argv[3];
outdir = path_.resolve(path_.normalize(outdir));

function ensurePath(p) {
  var i = 1 ;
  var parts = p.split(path_.sep);
  var path = '/' + parts[0];

  while ( i < parts.length ) {
    try {
      var stat = fs_.statSync(path);
      if ( ! stat.isDirectory() ) throw path + 'is not a directory';
    } catch(e) {
      fs_.mkdirSync(path);
    }

    path += path_.sep + parts[i++];
  }
}

var loading = {};
function loadClass(c) {
  var path = srcPath;

  if ( foam.Array.isInstance(c) ) {
    path = path + c[0];
    c = c[1];
  }
  if ( loading[c] ) {
    console.log('******************************** call/loadClass:cls', c);
  }
  loading[c] = true;

  if ( ! foam.lookup(c, true) ) {
    console.warn("Using fallback model loading; " +
      "may cause errors for files with multiple definitions.");
    require(path + c.replace(/\./g, '/') + '.js');
  }
  cls = foam.lookup(c);
  return cls;
}

function generateClass(cls) {
  logger.debug('call/generateClass:cls', ''+cls);
  if ( foam.Array.isInstance(cls) ) {
    cls = cls[1];
  }
  if ( typeof cls === 'string' )
    cls = foam.lookup(cls);

  logger.debug('call/generateClass:cls.id', cls.id);

  if ( fileWhitelist !== null ) {
    let src = cls.model_.source;
    logger.debug('call/generateClass:src', cls.id, src);
    if ( ! src ) {
      classesNotFound[cls.id] = true;
    } else {
      delete classesNotFound[cls.id];
      classesFound[cls.id] = true;
      if ( ! fileWhitelist[src] ) {
        return;
      }
    }
  }
  logger.debug('call/generateClass:cls.id,build?', cls.id, 'true');

  var outfile = outdir + path_.sep +
    cls.id.replace(/\./g, path_.sep) + '.java';

  ensurePath(outfile);

  writeFileIfUpdated(outfile, cls.buildJavaClass().toJavaSource());
}

function generateAbstractClass(cls) {
  if ( foam.Array.isInstance(cls) ) {
    cls = cls[1];
  }
  cls = foam.lookup(cls);

  var outfile = outdir + path_.sep +
    cls.id.replace(/\./g, path_.sep) + '.java';

  ensurePath(outfile);

  var javaclass = cls.buildJavaClass(foam.java.Class.create({ abstract: true }));

  writeFileIfUpdated(outfile, javaclass.toJavaSource());
}

function generateSkeleton(cls) {
  if ( foam.Array.isInstance(cls) ) {
    cls = cls[1];
  }
  cls = foam.lookup(cls);

  var outfile = outdir + path_.sep +
    cls.package.replace(/\./g, path_.sep) +
    path_.sep + cls.name + 'Skeleton.java';

  ensurePath(outfile);

  writeFileIfUpdated(outfile, foam.java.Skeleton.create({ of: cls }).buildJavaClass().toJavaSource());
}

function generateProxy(intf) {
  if ( foam.Array.isInstance(intf) ) {
    intf = intf[1];
  }
  intf = foam.lookup(intf);

  var existing = foam.lookup(intf.package + '.Proxy' + intf.name, true);

  if ( existing ) {
    generateClass(existing.id);
    return;
  }

  var proxy = foam.core.Model.create({
    package: intf.package,
    name: 'Proxy' + intf.name,
    implements: [intf.id],
    properties: [
      {
        class: 'Proxy',
        of: intf.id,
        name: 'delegate'
      }
    ]
  });

  proxy.source = intf.model_.source;

  generateClass(proxy.buildClass());
}

function writeFileIfUpdated(outfile, buildJavaSource, opt_result) {
  if (! ( fs_.existsSync(outfile) && (fs_.readFileSync(outfile).toString() == buildJavaSource))) {
    debugFilesWritten.push(outfile);
    fs_.writeFileSync(outfile, buildJavaSource);
    if ( opt_result !== undefined) opt_result.push(outfile);
  }
}

var addDepsToClasses = function() {
  // Determine all of the paths that the modelDAO should search will use when
  // arequiring all of the classes.
  var paths = {};
  paths[srcPath] = true;
  classes.forEach(function(cls) {
    if ( foam.Array.isInstance(cls) ) paths[srcPath + cls[0]] = true;
  });

  // Remove the paths from the entries in classes because the modelDAO should be
  // able to find them now.
  classes = classes.map(function(cls) {
    return foam.Array.isInstance(cls) ? cls[1] : cls;
  });

  var flagFilter = foam.util.flagFilter(['java']);

  var classloader = foam.__context__.classloader;
  Object.keys(paths).forEach(function(p) {
    classloader.addClassPath(p);
  });

  return (function() {
    var loadClass = foam.cps.awrap(classloader.load.bind(classloader));

    function collectDeps() {
      var classMap = {};
      var classQueue = classes.slice(0);
      const PRINT_LIMIT = 25;
      let printCounter = 0;
      while ( classQueue.length ) {
        var cls = classQueue.pop();
        if ( ! classMap[cls] && ! blacklist[cls] ) {
          if ( printCounter < PRINT_LIMIT ) {
            console.log('generating', cls);
            printCounter = printCounter + 1;
          } else if ( printCounter == PRINT_LIMIT ) {
            console.log('generating ...');
            printCounter = printCounter + 1;
          }
          cls = foam.lookup(cls);
          if ( ! checkFlags(cls.model_) ) continue;
          classMap[cls.id] = true;
          cls.getAxiomsByClass(foam.core.Requires).filter(flagFilter).forEach(function(r) {
            r.javaPath && classQueue.push(r.javaPath);
          });
          cls.getAxiomsByClass(foam.core.Implements).filter(flagFilter).forEach(function(r) {
            classQueue.push(r.path);
          });
          if ( cls.model_.extends ) classQueue.push(cls.model_.extends);
        }
      }
      classes = Object.keys(classMap);
    }

    with ( foam.cps ) {
      return new Promise(
        sequence(
          compose(map(loadClass), value(classes)),
          wrap(collectDeps)));

    }
  })();
};

function checkFlags(model) {
  var parent = true;

  if ( model.extends &&
       ( model.extends != 'foam.core.FObject' && model.extends != 'FObject' ) ) {
    parent = checkFlags(foam.lookup(model.extends).model_);
  }

  if ( ! parent ) return false;

  if ( model.flags && model.flags.indexOf('java') == -1 ) {
    return false;
  }

  return true;
}

addDepsToClasses().then(function() {
  classes.forEach(loadClass);
  abstractClasses.forEach(loadClass);
  skeletons.forEach(loadClass);
  proxies.forEach(loadClass);

  classes.forEach(generateClass);
  abstractClasses.forEach(generateAbstractClass);
  skeletons.forEach(generateSkeleton);
  proxies.forEach(generateProxy);
}).then(function () {
  var notFound = Object.keys(classesNotFound).length;
  var found = Object.keys(classesFound).length;

  if ( notFound > 0 ) {
    var allKeys = {};
    for ( k in classesNotFound ) allKeys[k] = true;
    for ( k in classesFound ) allKeys[k] = true;
    console.log('${found}/${Object.keys(allKeys).length} sources found (${notFound} missing)');
    console.log(Object.keys(classesNotFound));
    if ( debugDataDir !== null ) {
      require('fs').writeFileSync(
        path_.join(debugDataDir, 'classesWithNoSources.json'),
        JSON.stringify(Object.keys(classesNotFound)));
    }
  }

  if ( debugDataDir !== null ) {
    if ( debugFilesWritten.length != 0 ) {
      require('fs').writeFileSync(
        path_.join(debugDataDir, 'filesWritten.json'),
        JSON.stringify(debugFilesWritten));
      }
  }
});
<commit_msg>Remove logging from genjava.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

var path_ = require('path');
var fs_   = require('fs');

process.on('unhandledRejection', function(e) {
  console.error("ERROR: Unhandled promise rejection ", e);
  process.exit(1);
});

// enable FOAM java support.
global.FOAM_FLAGS = { 'java': true, 'debug': true, 'js': false, 'swift': true };

// Enable FOAMLink mode but only if FOAMLINK_DATA is set in environment
var foamlinkMode = process.env.hasOwnProperty('FOAMLINK_DATA');
if ( foamlinkMode ) {
  global.FOAMLINK_DATA = process.env['FOAMLINK_DATA'];
}

var logger = {};
logger.debug = () => {};

// Store debug files but only if DEBUG_DATA_DIR is set in environment
var debugDataDir = null;
if ( process.env.hasOwnProperty('DEBUG_DATA_DIR') ) {
  debugDataDir = process.env['DEBUG_DATA_DIR'];
  logger.debug = (...args) => {
    fs_.appendFileSync(path_.join(debugDataDir, 'debugLog.jrl'),
      args.join('p({"type": "debug", "value": ' + JSON.stringify(args) + '})\n')
    );
  };
}

require('../src/foam.js');
require('../src/foam/nanos/nanos.js');
require('../src/foam/support/support.js');

var srcPath = __dirname + "/../src/";

if ( ! (
  process.argv.length == 4 ||
  process.argv.length == 5 ||
  process.argv.length == 6 ) ) {
  console.log("USAGE: genjava.js input-path output-path src-path(optional) files-to-update (optional)");
  process.exit(1);
}

if ( process.argv.length > 4 && process.argv[4] !== '--' ) {
  srcPath = process.argv[4];
  if ( ! srcPath.endsWith('/') ) {
    srcPath = srcPath + '/';
  }
}

var incrementalMeta = null;
if ( process.argv.length > 5  &&
     process.argv[5] !== '--' &&
     process.argv[5] != '' ) {
  incrementalMeta = JSON.parse(process.argv[5]);
  logger.debug('INCREMENTAL', process.argv[5]);
}

var indir = process.argv[2];
indir = path_.resolve(path_.normalize(indir));

var externalFile = require(indir);
var classes = externalFile.classes;
var abstractClasses = externalFile.abstractClasses;
var skeletons = externalFile.skeletons;
var proxies = externalFile.proxies;

var blacklist = {}
externalFile.blacklist.forEach(function(cls) {
  blacklist[cls] = true;
});

var fileWhitelist = null;
var classesNotFound = {};
var classesFound = {};
var debugFilesWritten = [];

// Set file whitelist from parsed argument, but only if foamlink is enabled
if ( incrementalMeta !== null && foamlinkMode ) {
  fileWhitelist = {}; // set
  for ( var i = 0; i < incrementalMeta.modified.length; i++ ) {
    let relativePath = path_.relative(process.cwd(), incrementalMeta.modified[i]);
    fileWhitelist[relativePath] = true;
  }
}
logger.debug('fileWhitelist', fileWhitelist);

[
  'FObject',
  'foam.core.AbstractEnum',
  'foam.core.AbstractInterface',
  'foam.core.Property',
  'foam.core.String',

  // These have hand written java impls so we don't want to clobber them.
  // TODO: Change gen.sh to prefer hand written java files over generated.
  'foam.dao.LimitedDAO',
  'foam.dao.OrderedDAO',
  'foam.dao.SkipDAO',

  // TODO: These models currently don't compile in java but could be updated to
  // compile properly.
  'foam.blob.BlobBlob',
  'foam.dao.CompoundDAODecorator',
  'foam.dao.DAOInterceptor',
  'foam.dao.FlowControl',
  'foam.dao.sync.SyncRecord',
  'foam.dao.sync.VersionedSyncRecord',
  'foam.mlang.Expressions',
  'foam.nanos.menu.MenuBar',

  'foam.box.Context',
//  'foam.box.HTTPBox',
//  'foam.box.SessionClientBox',
  'foam.box.SocketBox',
  'foam.box.WebSocketBox',
  'foam.box.TimeoutBox',
  'foam.box.RetryBox',
  'foam.dao.TTLCachingDAO',
  'foam.dao.CachingDAO',
  'foam.dao.CompoundDAODecorator',
  'foam.dao.InterceptedDAO',
  'foam.dao.DeDupDAO',
  'foam.dao.IDBDAO',
  'foam.dao.LoggingDAO',
  'foam.dao.MDAO',
  'foam.dao.RequestResponseClientDAO',
  'foam.dao.SyncDAO',
  'foam.dao.TimingDAO'
].forEach(function(cls) {
  blacklist[cls] = true;
});

var outdir = process.argv[3];
outdir = path_.resolve(path_.normalize(outdir));

function ensurePath(p) {
  var i = 1 ;
  var parts = p.split(path_.sep);
  var path = '/' + parts[0];

  while ( i < parts.length ) {
    try {
      var stat = fs_.statSync(path);
      if ( ! stat.isDirectory() ) throw path + 'is not a directory';
    } catch(e) {
      fs_.mkdirSync(path);
    }

    path += path_.sep + parts[i++];
  }
}

function loadClass(c) {
  var path = srcPath;

  if ( foam.Array.isInstance(c) ) {
    path = path + c[0];
    c = c[1];
  }

  if ( ! foam.lookup(c, true) ) {
    console.warn("Using fallback model loading; " +
      "may cause errors for files with multiple definitions.");
    require(path + c.replace(/\./g, '/') + '.js');
  }
  cls = foam.lookup(c);
  return cls;
}

function generateClass(cls) {
  logger.debug('call/generateClass:cls', ''+cls);
  if ( foam.Array.isInstance(cls) ) {
    cls = cls[1];
  }
  if ( typeof cls === 'string' )
    cls = foam.lookup(cls);

  logger.debug('call/generateClass:cls.id', cls.id);

  if ( fileWhitelist !== null ) {
    let src = cls.model_.source;
    logger.debug('call/generateClass:src', cls.id, src);
    if ( ! src ) {
      classesNotFound[cls.id] = true;
    } else {
      delete classesNotFound[cls.id];
      classesFound[cls.id] = true;
      if ( ! fileWhitelist[src] ) {
        return;
      }
    }
  }
  logger.debug('call/generateClass:cls.id,build?', cls.id, 'true');

  var outfile = outdir + path_.sep +
    cls.id.replace(/\./g, path_.sep) + '.java';

  ensurePath(outfile);

  writeFileIfUpdated(outfile, cls.buildJavaClass().toJavaSource());
}

function generateAbstractClass(cls) {
  if ( foam.Array.isInstance(cls) ) {
    cls = cls[1];
  }
  cls = foam.lookup(cls);

  var outfile = outdir + path_.sep +
    cls.id.replace(/\./g, path_.sep) + '.java';

  ensurePath(outfile);

  var javaclass = cls.buildJavaClass(foam.java.Class.create({ abstract: true }));

  writeFileIfUpdated(outfile, javaclass.toJavaSource());
}

function generateSkeleton(cls) {
  if ( foam.Array.isInstance(cls) ) {
    cls = cls[1];
  }
  cls = foam.lookup(cls);

  var outfile = outdir + path_.sep +
    cls.package.replace(/\./g, path_.sep) +
    path_.sep + cls.name + 'Skeleton.java';

  ensurePath(outfile);

  writeFileIfUpdated(outfile, foam.java.Skeleton.create({ of: cls }).buildJavaClass().toJavaSource());
}

function generateProxy(intf) {
  if ( foam.Array.isInstance(intf) ) {
    intf = intf[1];
  }
  intf = foam.lookup(intf);

  var existing = foam.lookup(intf.package + '.Proxy' + intf.name, true);

  if ( existing ) {
    generateClass(existing.id);
    return;
  }

  var proxy = foam.core.Model.create({
    package: intf.package,
    name: 'Proxy' + intf.name,
    implements: [intf.id],
    properties: [
      {
        class: 'Proxy',
        of: intf.id,
        name: 'delegate'
      }
    ]
  });

  proxy.source = intf.model_.source;

  generateClass(proxy.buildClass());
}

function writeFileIfUpdated(outfile, buildJavaSource, opt_result) {
  if (! ( fs_.existsSync(outfile) && (fs_.readFileSync(outfile).toString() == buildJavaSource))) {
    debugFilesWritten.push(outfile);
    fs_.writeFileSync(outfile, buildJavaSource);
    if ( opt_result !== undefined) opt_result.push(outfile);
  }
}

var addDepsToClasses = function() {
  // Determine all of the paths that the modelDAO should search will use when
  // arequiring all of the classes.
  var paths = {};
  paths[srcPath] = true;
  classes.forEach(function(cls) {
    if ( foam.Array.isInstance(cls) ) paths[srcPath + cls[0]] = true;
  });

  // Remove the paths from the entries in classes because the modelDAO should be
  // able to find them now.
  classes = classes.map(function(cls) {
    return foam.Array.isInstance(cls) ? cls[1] : cls;
  });

  var flagFilter = foam.util.flagFilter(['java']);

  var classloader = foam.__context__.classloader;
  Object.keys(paths).forEach(function(p) {
    classloader.addClassPath(p);
  });

  return (function() {
    var loadClass = foam.cps.awrap(classloader.load.bind(classloader));

    function collectDeps() {
      var classMap = {};
      var classQueue = classes.slice(0);
      const PRINT_LIMIT = 25;
      let printCounter = 0;
      while ( classQueue.length ) {
        var cls = classQueue.pop();
        if ( ! classMap[cls] && ! blacklist[cls] ) {
          if ( printCounter < PRINT_LIMIT ) {
            console.log('generating', cls);
            printCounter = printCounter + 1;
          } else if ( printCounter == PRINT_LIMIT ) {
            console.log('generating ...');
            printCounter = printCounter + 1;
          }
          cls = foam.lookup(cls);
          if ( ! checkFlags(cls.model_) ) continue;
          classMap[cls.id] = true;
          cls.getAxiomsByClass(foam.core.Requires).filter(flagFilter).forEach(function(r) {
            r.javaPath && classQueue.push(r.javaPath);
          });
          cls.getAxiomsByClass(foam.core.Implements).filter(flagFilter).forEach(function(r) {
            classQueue.push(r.path);
          });
          if ( cls.model_.extends ) classQueue.push(cls.model_.extends);
        }
      }
      classes = Object.keys(classMap);
    }

    with ( foam.cps ) {
      return new Promise(
        sequence(
          compose(map(loadClass), value(classes)),
          wrap(collectDeps)));

    }
  })();
};

function checkFlags(model) {
  var parent = true;

  if ( model.extends &&
       ( model.extends != 'foam.core.FObject' && model.extends != 'FObject' ) ) {
    parent = checkFlags(foam.lookup(model.extends).model_);
  }

  if ( ! parent ) return false;

  if ( model.flags && model.flags.indexOf('java') == -1 ) {
    return false;
  }

  return true;
}

addDepsToClasses().then(function() {
  classes.forEach(loadClass);
  abstractClasses.forEach(loadClass);
  skeletons.forEach(loadClass);
  proxies.forEach(loadClass);

  classes.forEach(generateClass);
  abstractClasses.forEach(generateAbstractClass);
  skeletons.forEach(generateSkeleton);
  proxies.forEach(generateProxy);
}).then(function () {
  var notFound = Object.keys(classesNotFound).length;
  var found = Object.keys(classesFound).length;

  if ( notFound > 0 ) {
    var allKeys = {};
    for ( k in classesNotFound ) allKeys[k] = true;
    for ( k in classesFound ) allKeys[k] = true;
    console.log('${found}/${Object.keys(allKeys).length} sources found (${notFound} missing)');
    console.log(Object.keys(classesNotFound));
    if ( debugDataDir !== null ) {
      require('fs').writeFileSync(
        path_.join(debugDataDir, 'classesWithNoSources.json'),
        JSON.stringify(Object.keys(classesNotFound)));
    }
  }

  if ( debugDataDir !== null ) {
    if ( debugFilesWritten.length != 0 ) {
      require('fs').writeFileSync(
        path_.join(debugDataDir, 'filesWritten.json'),
        JSON.stringify(debugFilesWritten));
      }
  }
});
<|endoftext|>


<commit_before>(function (mw, $, THREE) {
	"use strict";

	mw.PluginManager.add('video360', mw.KBasePlugin.extend({

		defaultConfig: {
			moveMultiplier: 0.3,
			mobileVibrationValue: (mw.isIOS() ? 0.0365 : 1),
			cameraOptions:{
				fov: 75,
				aspect: window.innerWidth / window.innerHeight,
				near: 0.1,
				far: 1000
			}
		},
		manualControl: false,
		longitude: 180,
		latitude: 0,
		savedX: 0,
		savedY: 0,
		savedLongitude: 0,
		savedLatitude: 0,
		enableKeyboardShortcuts: true,
		keyboardShortcutsMap: {
			"left":  "65",  // 'A'
			"up":    "87",  // 'W'
			"right": "68",  // 'D'
			"down":  "83"   // 'S'
		},

		isSafeEnviornment: function () {
			return !( mw.isIE8() || mw.isIE9() || mw.isIE10Comp() || // old IEs
				(mw.isIE11() && (mw.getUserOS() === 'Windows 7' || mw.getUserOS() === 'Windows 8')) || // ie11 on win7/8
				(mw.isIphone() && mw.isIOSBelow10()) || // iPhone and IOS < 10 - doesn't support inline playback
				mw.isIOSBelow9() ); // IOS < 9 doesn't support webgl
		},

		setup: function () {
			this.set360Config();
			this.addBindings();
		},

		set360Config:function(){
			//Get user configuration
			var userCameraOptions = this.getConfig("cameraOptions");
			//Deep extend custom config
			this.cameraOptions = $.extend({}, this.defaultConfig.cameraOptions, userCameraOptions);
		},

		addBindings: function () {
			this.bind("playerReady", function () {
				this.moveMultiplier = this.getConfig("moveMultiplier");
				this.mobileVibrationValue = this.getConfig("mobileVibrationValue");
				this.video = this.getPlayer().getPlayerElement();
				this.video.setAttribute('crossorigin', 'anonymous');
				if (mw.isIE11()) {
					// a workaround for ie11 texture issue
					// see https://github.com/mrdoob/three.js/issues/7560
					this.overrideVideoTextureMethod();
				}
				this.initComponents();
			}.bind(this));

			this.bind("firstPlay", function () {
				this.attachMotionListeners();
				this.getPlayer().layoutBuilder.removePlayerClickBindings();
				this.add360logo();
			}.bind(this));

			this.bind("playing", function () {
				$(this.video).hide();
				$(this.getPlayer()).css('z-index', '-1');
				var canvasSize = this.getCanvasSize();
				this.renderer.setSize(canvasSize.width, canvasSize.height);
				this.render();
			}.bind(this));

			this.bind("doStop", function () {
				cancelAnimationFrame(this.requestId)
			}.bind(this));

			this.bind("onChangeMedia", function () {
				this.clean();
			}.bind(this));

			this.bind("updateLayout", function () {
				if(this.renderer){
					var canvasSize = this.getCanvasSize();
					this.renderer.setSize(canvasSize.width, canvasSize.height);
				}
			}.bind(this));

			this.bind('addKeyBindCallback', function (e, addKeyCallback) {
				this.addKeyboardShortcuts(addKeyCallback);
			}.bind(this));
		},

		initComponents: function () {
			// setting up the renderer
			this.renderer = new THREE.WebGLRenderer();
			this.canvas = this.renderer.domElement;
			this.getPlayer().getVideoDisplay().append(this.canvas);
			$(this.canvas).addClass("canvas360");
			$(this.canvas).css('z-index', '2');

			// creating a new scene
			this.scene = new THREE.Scene();

			// adding a camera
			this.camera = new THREE.PerspectiveCamera(this.cameraOptions.fov, this.cameraOptions.aspect, this.cameraOptions.near, this.cameraOptions.far);
			this.camera.target = new THREE.Vector3(0, 0, 0);

			// creation of a big sphere geometry
			var sphere = new THREE.SphereGeometry(100, 100, 40);
			sphere.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
			this.texture = new THREE.VideoTexture(this.video);
			this.texture.minFilter = THREE.LinearFilter;
			this.texture.magFilter = THREE.LinearFilter;

			// creation of the sphere material
			var sphereMaterial = new THREE.MeshBasicMaterial();
			sphereMaterial.map = this.texture;

			// geometry + material = mesh (actual object)
			var sphereMesh = new THREE.Mesh(sphere, sphereMaterial);
			this.scene.add(sphereMesh);
		},

		overrideVideoTextureMethod: function () {
			THREE.VideoTexture = function (video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) {
				var scope = this;

				scope.video = video;
				scope.ctx2d = document.createElement('canvas').getContext('2d');
				var canvas = scope.ctx2d.canvas;
				canvas.width = video.width;
				canvas.height = video.height;

				scope.ctx2d.drawImage(scope.video, 0, 0, scope.width, scope.height);
				THREE.Texture.call(scope, scope.ctx2d.canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy);

				scope.generateMipmaps = false;

				function update() {
					requestAnimationFrame(update);
					if (video.readyState >= video.HAVE_CURRENT_DATA) {
						scope.ctx2d.drawImage(scope.video, 0, 0, scope.video.width, scope.video.height);
						scope.needsUpdate = true;
					}
				}

				update();
			};

			THREE.VideoTexture.prototype = Object.create(THREE.Texture.prototype);
			THREE.VideoTexture.prototype.constructor = THREE.VideoTexture;
		},

		getCanvasSize: function(){
			var videoDisplayWidth = this.getPlayer().getVideoDisplay().width();
			var videoDisplayHeight = this.getPlayer().getVideoDisplay().height();
			var pWidth = parseInt(this.video.videoWidth / this.video.videoHeight * videoDisplayHeight);
			var videoRatio;
			if(videoDisplayWidth < pWidth) {
				videoRatio = this.video.videoHeight / this.video.videoWidth;
				return {
					width: videoDisplayWidth,
					height: videoRatio * videoDisplayWidth
				};
			} else {
				videoRatio = this.video.videoWidth / this.video.videoHeight;
				return {
					width: videoRatio * videoDisplayHeight,
					height: videoDisplayHeight
				};
			}
		},

		updateCamera: function () {
			// limiting latitude from -85 to 85 (cannot point to the sky or under your feet)
			this.latitude = Math.max(-85, Math.min(85, this.latitude));

			// moving the camera according to current latitude (vertical movement) and longitude (horizontal movement)
			this.camera.target.x = 500 * Math.sin(THREE.Math.degToRad(90 - this.latitude)) * Math.cos(THREE.Math.degToRad(this.longitude));
			this.camera.target.y = 500 * Math.cos(THREE.Math.degToRad(90 - this.latitude));
			this.camera.target.z = 500 * Math.sin(THREE.Math.degToRad(90 - this.latitude)) * Math.sin(THREE.Math.degToRad(this.longitude));
			this.camera.lookAt(this.camera.target);
		},

		render: function () {
			if (this.texture && this.video.readyState >= this.video.HAVE_CURRENT_DATA) {
				this.texture.needsUpdate = true;
			}
			this.requestId = requestAnimationFrame(this.render.bind(this));

			this.updateCamera();

			// calling again render function
			this.renderer.render(this.scene, this.camera);
		},

		// when the mouse is pressed, we switch to manual control and save current coordinates
		onDocumentMouseDown: function (event) {
			this.manualControl = true;
			this.savedX = event.clientX || event.originalEvent.touches[0].pageX;
			this.savedY = event.clientY || event.originalEvent.touches[0].pageY;
			this.savedLongitude = this.longitude;
			this.savedLatitude = this.latitude;
			if( event.type === "touchstart" ) {
				$("#touchOverlay").trigger("touchstart");
			}
		},

		// when the mouse moves, if in manual control we adjust coordinates
		onDocumentMouseMove: function (event) {
			if (this.manualControl) {
				if (event.clientX || event.originalEvent.touches) {
					this.longitude = (this.savedX - (event.clientX || event.originalEvent.touches[0].pageX)) * this.moveMultiplier + this.savedLongitude;
				}
				if (event.clientY || event.originalEvent.touches) {
					this.latitude = ((event.clientY || event.originalEvent.touches[0].pageY) - this.savedY) * this.moveMultiplier + this.savedLatitude;
				}
				event.preventDefault();
				event.stopPropagation();
			}
		},

		// when the mouse is released, we turn manual control off
		onDocumentMouseUp: function (event) {
			this.manualControl = false;
		},

		onMobileOrientation: function (event) {
			if (event.rotationRate) {
				var x = event.rotationRate.alpha;
				var y = event.rotationRate.beta;
				var portrait = $(top).height() > $(top).width();
				var orientation = event.orientation || window.orientation;

				if (portrait) {
					this.longitude = this.longitude - y * this.mobileVibrationValue;
					this.latitude = this.latitude + x * this.mobileVibrationValue;
				} else { // landscape
					var orientationDegree = -90;
					if (typeof orientation != "undefined") {
						orientationDegree = orientation;
					}
					this.longitude = (orientationDegree == -90) ? this.longitude + x * this.mobileVibrationValue : this.longitude - x * this.mobileVibrationValue;
					this.latitude = (orientationDegree == -90) ? this.latitude + y * this.mobileVibrationValue : this.latitude - y * this.mobileVibrationValue;
				}
			}
		},

		attachMotionListeners: function () {
			$(this.canvas).on("mousedown touchstart", this.onDocumentMouseDown.bind(this));
			$(this.canvas).on("mousemove touchmove", this.onDocumentMouseMove.bind(this));
			$(document).on("mouseup touchend", this.onDocumentMouseUp.bind(this));
			window.addEventListener('devicemotion', this.onMobileOrientation.bind(this));
		},

		addKeyboardShortcuts: function (addKeyCallback) {
			addKeyCallback(this.keyboardShortcutsMap.left, function () {
				this.longitude -= 10 * this.moveMultiplier;
			}.bind(this));
			addKeyCallback(this.keyboardShortcutsMap.up, function () {
				this.latitude += 10 * this.moveMultiplier;
			}.bind(this));
			addKeyCallback(this.keyboardShortcutsMap.right, function () {
				this.longitude += 10 * this.moveMultiplier;
			}.bind(this));
			addKeyCallback(this.keyboardShortcutsMap.down, function () {
				this.latitude -= 10 * this.moveMultiplier;
			}.bind(this));
		},

		clean: function(){
			cancelAnimationFrame(this.requestId);
			$(this.canvas).remove();
		},

		add360logo: function () {
			var logo = $('<div />').addClass('logo360 bottomRight');
			this.getPlayer().getVideoHolder().append(logo);
		}
	}));

	// set EmbedPlayer.WebKitPlaysInline true for iPhone inline playback
	var playerConfig = window.kalturaIframePackageData.playerConfig;
	if (playerConfig) {
		if (!playerConfig.vars) {
			playerConfig.vars = {};
		}
		playerConfig.vars["EmbedPlayer.WebKitPlaysInline"] = true;
		mw.setConfig('KalturaSupport.PlayerConfig', playerConfig);
	}

})(window.mw, window.jQuery, window.THREE);<commit_msg>FEC-6494 Pop the canvas' z-index only on firstPlay (#3284)<commit_after>(function (mw, $, THREE) {
	"use strict";

	mw.PluginManager.add('video360', mw.KBasePlugin.extend({

		defaultConfig: {
			moveMultiplier: 0.3,
			mobileVibrationValue: (mw.isIOS() ? 0.0365 : 1),
			cameraOptions:{
				fov: 75,
				aspect: window.innerWidth / window.innerHeight,
				near: 0.1,
				far: 1000
			}
		},
		manualControl: false,
		longitude: 180,
		latitude: 0,
		savedX: 0,
		savedY: 0,
		savedLongitude: 0,
		savedLatitude: 0,
		enableKeyboardShortcuts: true,
		keyboardShortcutsMap: {
			"left":  "65",  // 'A'
			"up":    "87",  // 'W'
			"right": "68",  // 'D'
			"down":  "83"   // 'S'
		},

		isSafeEnviornment: function () {
			return !( mw.isIE8() || mw.isIE9() || mw.isIE10Comp() || // old IEs
				(mw.isIE11() && (mw.getUserOS() === 'Windows 7' || mw.getUserOS() === 'Windows 8')) || // ie11 on win7/8
				(mw.isIphone() && mw.isIOSBelow10()) || // iPhone and IOS < 10 - doesn't support inline playback
				mw.isIOSBelow9() ); // IOS < 9 doesn't support webgl
		},

		setup: function () {
			this.set360Config();
			this.addBindings();
		},

		set360Config:function(){
			//Get user configuration
			var userCameraOptions = this.getConfig("cameraOptions");
			//Deep extend custom config
			this.cameraOptions = $.extend({}, this.defaultConfig.cameraOptions, userCameraOptions);
		},

		addBindings: function () {
			this.bind("playerReady", function () {
				this.moveMultiplier = this.getConfig("moveMultiplier");
				this.mobileVibrationValue = this.getConfig("mobileVibrationValue");
				this.video = this.getPlayer().getPlayerElement();
				this.video.setAttribute('crossorigin', 'anonymous');
				if (mw.isIE11()) {
					// a workaround for ie11 texture issue
					// see https://github.com/mrdoob/three.js/issues/7560
					this.overrideVideoTextureMethod();
				}
				this.initComponents();
			}.bind(this));

			this.bind("firstPlay", function () {
				this.attachMotionListeners();
				this.getPlayer().layoutBuilder.removePlayerClickBindings();
				$(this.canvas).css('z-index', '2');
				this.add360logo();
			}.bind(this));

			this.bind("playing", function () {
				$(this.video).hide();
				$(this.getPlayer()).css('z-index', '-1');
				var canvasSize = this.getCanvasSize();
				this.renderer.setSize(canvasSize.width, canvasSize.height);
				this.render();
			}.bind(this));

			this.bind("doStop", function () {
				cancelAnimationFrame(this.requestId)
			}.bind(this));

			this.bind("onChangeMedia", function () {
				this.clean();
			}.bind(this));

			this.bind("updateLayout", function () {
				if(this.renderer){
					var canvasSize = this.getCanvasSize();
					this.renderer.setSize(canvasSize.width, canvasSize.height);
				}
			}.bind(this));

			this.bind('addKeyBindCallback', function (e, addKeyCallback) {
				this.addKeyboardShortcuts(addKeyCallback);
			}.bind(this));
		},

		initComponents: function () {
			// setting up the renderer
			this.renderer = new THREE.WebGLRenderer();
			this.canvas = this.renderer.domElement;
			this.getPlayer().getVideoDisplay().append(this.canvas);
			$(this.canvas).addClass("canvas360");

			// creating a new scene
			this.scene = new THREE.Scene();

			// adding a camera
			this.camera = new THREE.PerspectiveCamera(this.cameraOptions.fov, this.cameraOptions.aspect, this.cameraOptions.near, this.cameraOptions.far);
			this.camera.target = new THREE.Vector3(0, 0, 0);

			// creation of a big sphere geometry
			var sphere = new THREE.SphereGeometry(100, 100, 40);
			sphere.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
			this.texture = new THREE.VideoTexture(this.video);
			this.texture.minFilter = THREE.LinearFilter;
			this.texture.magFilter = THREE.LinearFilter;

			// creation of the sphere material
			var sphereMaterial = new THREE.MeshBasicMaterial();
			sphereMaterial.map = this.texture;

			// geometry + material = mesh (actual object)
			var sphereMesh = new THREE.Mesh(sphere, sphereMaterial);
			this.scene.add(sphereMesh);
		},

		overrideVideoTextureMethod: function () {
			THREE.VideoTexture = function (video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) {
				var scope = this;

				scope.video = video;
				scope.ctx2d = document.createElement('canvas').getContext('2d');
				var canvas = scope.ctx2d.canvas;
				canvas.width = video.width;
				canvas.height = video.height;

				scope.ctx2d.drawImage(scope.video, 0, 0, scope.width, scope.height);
				THREE.Texture.call(scope, scope.ctx2d.canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy);

				scope.generateMipmaps = false;

				function update() {
					requestAnimationFrame(update);
					if (video.readyState >= video.HAVE_CURRENT_DATA) {
						scope.ctx2d.drawImage(scope.video, 0, 0, scope.video.width, scope.video.height);
						scope.needsUpdate = true;
					}
				}

				update();
			};

			THREE.VideoTexture.prototype = Object.create(THREE.Texture.prototype);
			THREE.VideoTexture.prototype.constructor = THREE.VideoTexture;
		},

		getCanvasSize: function(){
			var videoDisplayWidth = this.getPlayer().getVideoDisplay().width();
			var videoDisplayHeight = this.getPlayer().getVideoDisplay().height();
			var pWidth = parseInt(this.video.videoWidth / this.video.videoHeight * videoDisplayHeight);
			var videoRatio;
			if(videoDisplayWidth < pWidth) {
				videoRatio = this.video.videoHeight / this.video.videoWidth;
				return {
					width: videoDisplayWidth,
					height: videoRatio * videoDisplayWidth
				};
			} else {
				videoRatio = this.video.videoWidth / this.video.videoHeight;
				return {
					width: videoRatio * videoDisplayHeight,
					height: videoDisplayHeight
				};
			}
		},

		updateCamera: function () {
			// limiting latitude from -85 to 85 (cannot point to the sky or under your feet)
			this.latitude = Math.max(-85, Math.min(85, this.latitude));

			// moving the camera according to current latitude (vertical movement) and longitude (horizontal movement)
			this.camera.target.x = 500 * Math.sin(THREE.Math.degToRad(90 - this.latitude)) * Math.cos(THREE.Math.degToRad(this.longitude));
			this.camera.target.y = 500 * Math.cos(THREE.Math.degToRad(90 - this.latitude));
			this.camera.target.z = 500 * Math.sin(THREE.Math.degToRad(90 - this.latitude)) * Math.sin(THREE.Math.degToRad(this.longitude));
			this.camera.lookAt(this.camera.target);
		},

		render: function () {
			if (this.texture && this.video.readyState >= this.video.HAVE_CURRENT_DATA) {
				this.texture.needsUpdate = true;
			}
			this.requestId = requestAnimationFrame(this.render.bind(this));

			this.updateCamera();

			// calling again render function
			this.renderer.render(this.scene, this.camera);
		},

		// when the mouse is pressed, we switch to manual control and save current coordinates
		onDocumentMouseDown: function (event) {
			this.manualControl = true;
			this.savedX = event.clientX || event.originalEvent.touches[0].pageX;
			this.savedY = event.clientY || event.originalEvent.touches[0].pageY;
			this.savedLongitude = this.longitude;
			this.savedLatitude = this.latitude;
			if( event.type === "touchstart" ) {
				$("#touchOverlay").trigger("touchstart");
			}
		},

		// when the mouse moves, if in manual control we adjust coordinates
		onDocumentMouseMove: function (event) {
			if (this.manualControl) {
				if (event.clientX || event.originalEvent.touches) {
					this.longitude = (this.savedX - (event.clientX || event.originalEvent.touches[0].pageX)) * this.moveMultiplier + this.savedLongitude;
				}
				if (event.clientY || event.originalEvent.touches) {
					this.latitude = ((event.clientY || event.originalEvent.touches[0].pageY) - this.savedY) * this.moveMultiplier + this.savedLatitude;
				}
				event.preventDefault();
				event.stopPropagation();
			}
		},

		// when the mouse is released, we turn manual control off
		onDocumentMouseUp: function (event) {
			this.manualControl = false;
		},

		onMobileOrientation: function (event) {
			if (event.rotationRate) {
				var x = event.rotationRate.alpha;
				var y = event.rotationRate.beta;
				var portrait = $(top).height() > $(top).width();
				var orientation = event.orientation || window.orientation;

				if (portrait) {
					this.longitude = this.longitude - y * this.mobileVibrationValue;
					this.latitude = this.latitude + x * this.mobileVibrationValue;
				} else { // landscape
					var orientationDegree = -90;
					if (typeof orientation != "undefined") {
						orientationDegree = orientation;
					}
					this.longitude = (orientationDegree == -90) ? this.longitude + x * this.mobileVibrationValue : this.longitude - x * this.mobileVibrationValue;
					this.latitude = (orientationDegree == -90) ? this.latitude + y * this.mobileVibrationValue : this.latitude - y * this.mobileVibrationValue;
				}
			}
		},

		attachMotionListeners: function () {
			$(this.canvas).on("mousedown touchstart", this.onDocumentMouseDown.bind(this));
			$(this.canvas).on("mousemove touchmove", this.onDocumentMouseMove.bind(this));
			$(document).on("mouseup touchend", this.onDocumentMouseUp.bind(this));
			window.addEventListener('devicemotion', this.onMobileOrientation.bind(this));
		},

		addKeyboardShortcuts: function (addKeyCallback) {
			addKeyCallback(this.keyboardShortcutsMap.left, function () {
				this.longitude -= 10 * this.moveMultiplier;
			}.bind(this));
			addKeyCallback(this.keyboardShortcutsMap.up, function () {
				this.latitude += 10 * this.moveMultiplier;
			}.bind(this));
			addKeyCallback(this.keyboardShortcutsMap.right, function () {
				this.longitude += 10 * this.moveMultiplier;
			}.bind(this));
			addKeyCallback(this.keyboardShortcutsMap.down, function () {
				this.latitude -= 10 * this.moveMultiplier;
			}.bind(this));
		},

		clean: function(){
			cancelAnimationFrame(this.requestId);
			$(this.canvas).remove();
		},

		add360logo: function () {
			var logo = $('<div />').addClass('logo360 bottomRight');
			this.getPlayer().getVideoHolder().append(logo);
		}
	}));

	// set EmbedPlayer.WebKitPlaysInline true for iPhone inline playback
	var playerConfig = window.kalturaIframePackageData.playerConfig;
	if (playerConfig) {
		if (!playerConfig.vars) {
			playerConfig.vars = {};
		}
		playerConfig.vars["EmbedPlayer.WebKitPlaysInline"] = true;
		mw.setConfig('KalturaSupport.PlayerConfig', playerConfig);
	}

})(window.mw, window.jQuery, window.THREE);<|endoftext|>


<commit_before>var _ = require('underscore');
var Future = require('fibers/future');

var files = require('./files.js');
var release = require('./release.js');
var buildmessage = require('./buildmessage.js');
var fiberHelpers = require('./fiber-helpers.js');
var runLog = require('./run-log.js');

var Console = require('./console.js').Console;

var Proxy = require('./run-proxy.js').Proxy;
var Selenium = require('./run-selenium.js').Selenium;
var HttpProxy = require('./run-httpproxy.js').HttpProxy;
var AppRunner = require('./run-app.js').AppRunner;
var MongoRunner = require('./run-mongo.js').MongoRunner;
var Updater = require('./run-updater.js').Updater;

// options: proxyPort, proxyHost, appPort, appHost, buildOptions,
// settingsFile, banner, program, onRunEnd, onFailure, watchForChanges,
// quiet, rootUrl, mongoUrl, oplogUrl, disableOplog,
// appDirForVersionCheck
var Runner = function (appDir, options) {
  var self = this;
  self.appDir = appDir;

  if (! _.has(options, 'proxyPort'))
    throw new Error("no proxyPort?");

  var listenPort = options.proxyPort;
  var mongoPort = listenPort + 1;
  self.specifiedAppPort = options.appPort;
  self.regenerateAppPort();

  self.stopped = false;
  self.quiet = options.quiet;
  self.banner = options.banner || files.prettyPath(self.appDir);
  if (options.rootUrl) {
    self.rootUrl = options.rootUrl;
  } else if (options.proxyHost) {
    self.rootUrl = 'http://' + options.proxyHost + ':' + listenPort + '/';
  } else {
    self.rootUrl = 'http://localhost:' + listenPort + '/';
  }

  self.extraRunners = options.extraRunners;

  self.proxy = new Proxy({
    listenPort: listenPort,
    listenHost: options.proxyHost,
    proxyToPort: self.appPort,
    proxyToHost: options.appHost,
    onFailure: options.onFailure
  });

  self.httpProxy = null;
  if (options.httpProxyPort) {
    self.httpProxy = new HttpProxy({
      listenPort: options.httpProxyPort
    });
  }

  self.mongoRunner = null;
  var mongoUrl, oplogUrl;
  if (options.mongoUrl) {
    mongoUrl = options.mongoUrl;
    oplogUrl = options.disableOplog ? null : options.oplogUrl;
  } else {
    self.mongoRunner = new MongoRunner({
      appDir: self.appDir,
      port: mongoPort,
      onFailure: options.onFailure,
      // For testing mongod failover, run with 3 mongod if the env var is
      // set. Note that data is not preserved from one run to the next.
      multiple: !!process.env.METEOR_TEST_MULTIPLE_MONGOD_REPLSET
    });

    mongoUrl = self.mongoRunner.mongoUrl();
    oplogUrl = options.disableOplog ? null : self.mongoRunner.oplogUrl();
  }

  self.updater = new Updater;

  self.appRunner = new AppRunner(appDir, {
    appDirForVersionCheck: options.appDirForVersionCheck,
    port: self.appPort,
    listenHost: options.appHost,
    mongoUrl: mongoUrl,
    oplogUrl: oplogUrl,
    buildOptions: options.buildOptions,
    rootUrl: self.rootUrl,
    settingsFile: options.settingsFile,
    program: options.program,
    debugPort: options.debugPort,
    proxy: self.proxy,
    onRunEnd: options.onRunEnd,
    watchForChanges: options.watchForChanges,
    noRestartBanner: self.quiet,
    recordPackageUsage: options.recordPackageUsage
  });

  self.selenium = null;
  if (options.selenium) {
    self.selenium = new Selenium({
      runner: self,
      browser: options.seleniumBrowser
    });
  }
};

_.extend(Runner.prototype, {
  // XXX leave a pidfile and check if we are already running
  start: function () {
    var self = this;

    self.proxy.start();

    // print the banner only once we've successfully bound the port
    if (! self.quiet && ! self.stopped) {
      runLog.log("[[[[[ " + self.banner + " ]]]]]\n");
      runLog.log("=> Started proxy.");
    }

    if (! self.stopped) {
      self.updater.start();
    }

    // print the banner only once we've successfully bound the port
    if (! self.stopped && self.httpProxy) {
      self.httpProxy.start();
      if (! self.quiet) {
        runLog.log("=> Started http proxy.");
      }
    }

    if (! self.stopped && self.mongoRunner) {
      var spinner = ['-', '\\', '|', '/'];
      // I looked at some Unicode indeterminate progress indicators, such as:
      //
      // spinner = "▁▃▄▅▆▇▆▅▄▃".split('');
      // spinner = "▉▊▋▌▍▎▏▎▍▌▋▊▉".split('');
      // spinner = "▏▎▍▌▋▊▉▊▋▌▍▎▏▁▃▄▅▆▇▆▅▄▃".split('');
      // spinner = "▉▊▋▌▍▎▏▎▍▌▋▊▉▇▆▅▄▃▁▃▄▅▆▇".split('');
      // spinner = "⠉⠒⠤⣀⠤⠒".split('');
      //
      // but none of them really seemed like an improvement. I think
      // the case for using unicode would be stronger in a determinate
      // progress indicator.
      //
      // There are also some four-frame options such as ◐◓◑◒ at
      //   http://stackoverflow.com/a/2685827/157965
      // but all of the ones I tried look terrible in the terminal.
      if (! self.quiet) {
        var animationFrame = 0;
        var printUpdate = fiberHelpers.bindEnvironment(function () {
          //runLog.logTemporary("=> Starting MongoDB... " +
          //                    spinner[animationFrame]);
          buildmessage.nudge();
          animationFrame = (animationFrame + 1) % spinner.length;
        });
        printUpdate();
        var mongoProgressTimer = setInterval(printUpdate, 200);
      }

      buildmessage.enterJob({ title: 'Starting MongoDB' }, function () {
        self.mongoRunner.start();
      });

      if (! self.quiet) {
        clearInterval(mongoProgressTimer);
        if (! self.stopped)
          runLog.log("=> Started MongoDB.");
      }
    }

    _.forEach(self.extraRunners, function (extraRunner) {
      if (! self.stopped) {
        var title = extraRunner.title;
        if (! self.quiet)
          runLog.logTemporary("=> Starting " + title + "...");
        extraRunner.start();
        if (! self.quiet && ! self.stopped)
          runLog.log("=> Started " + title + ".");
      }
    });

    if (! self.stopped) {
      if (! self.quiet)
        runLog.logTemporary("=> Starting your app...");
      self.appRunner.start();
      if (! self.quiet && ! self.stopped)
        runLog.log("=> Started your app.");
    }

    if (! self.stopped && ! self.quiet)
      runLog.log("\n=> App running at: " + self.rootUrl);

    if (self.selenium && ! self.stopped) {
      if (! self.quiet)
        runLog.logTemporary("=> Starting Selenium...");
      self.selenium.start();
      if (! self.quiet && ! self.stopped)
        runLog.log("=> Started Selenium.");
    }

    // XXX It'd be nice to (cosmetically) handle failure better. Right
    // now we overwrite the "starting foo..." message with the
    // error. It'd be better to overwrite it with "failed to start
    // foo" and then print the error.
  },

  // Idempotent
  stop: function () {
    var self = this;
    if (self.stopped)
      return;

    self.stopped = true;
    self.proxy.stop();
    self.httpProxy && self.httpProxy.stop();
    self.updater.stop();
    self.mongoRunner && self.mongoRunner.stop();
    _.forEach(self.extraRunners, function (extraRunner) {
      extraRunner.stop();
    });
    self.appRunner.stop();
    self.selenium && self.selenium.stop();
    // XXX does calling this 'finish' still make sense now that runLog is a
    // singleton?
    runLog.finish();
  },

  // Call this whenever you want to regenerate the app's port (if it is not
  // explicitly specified by the user).
  //
  // Rationale: if we randomly chose a port that's in use and the app failed to
  // listen on it, we should try a different port when we restart the app!
  regenerateAppPort: function () {
    var self = this;
    if (self.specifiedAppPort) {
      self.appPort = self.specifiedAppPort;
    } else {
      self.appPort = require('./utils.js').randomPort();
    }
    if (self.proxy)
      self.proxy.proxyToPort = self.appPort;
    if (self.appRunner)
      self.appRunner.port = self.appPort;
  }
});

// Run the app and all of its associated processes. Runs (and does not
// return) until an unrecoverable failure happens. Logs to
// stdout. Returns a suggested exit code.
//
// If 'once' is set, run the app process exactly once and pass through
// its exit code. Return an exit code of 255 if the app process was
// killed by a signal and 254 if the app process could not start
// (build failure, invalid program name, database couldn't start, and
// so on).
//
// If the 'once' option is not set, the default, restart the app
// process if it crashes or if source files change. (Non-app
// processes, such as the database, are always restarted as
// necessary.) The function will only return if there is an
// unrecoverable error, which generally means an error that could not
// be fixed by source code changes (such as the database refusing to
// run), but also currently includes Meteor version mismatches. So the
// exit code will always be 254 because in all other cases we'll
// persevere.
//
// Options:
//
// - proxyPort: the port to connect to to access the application (we will
//   run a proxy here that proxies to the actual app process). required
// - buildOptions: 'buildOptions' argument to bundler.bundle()
// - settingsFile: path to file containing deploy-time settings
// - program: the program in the app bundle to run
// - once: see above
// - banner: replace the application path that is normally printed on
//   startup with an arbitrary string (eg, 'Tests')
// - rootUrl: tell the app that traffic at this URL will be routed to
//   it at '/' (used by the app to construct absolute URLs)
// - disableOplog: don't use oplog tailing
// - mongoUrl: don't start a mongo process; instead use the mongo at
//   this mongo URL
// - oplogUrl: URL of the mongo oplog to use. if mongoUrl isn't
//   set (we're starting a mongo) a default will be provided, but can
//   be overridden. if mongoUrl is set, you must set this or you don't
//   get oplog tailing.
// - appDirForVersionCheck: when checking whether we're running the
//   right release of Meteor, check against this app rather than
//   appDir. Useful when you have autogenerated a test harness app
//   based on some other app.
// - recordPackageUsage: (default true) if set to false, don't send
//   information about packages used by this app to the package stats
//   server.
exports.run = function (appDir, options) {
  var runOptions = _.clone(options);
  var once = runOptions.once;
  delete runOptions.once;

  var fut = new Future;

  _.extend(runOptions, {
    onFailure: function () {
      // Ensure that runner stops now. You might think this is unnecessary
      // because the runner is stopped immediately after `fut.wait()`, but if
      // the failure happens while runner.start() is still running, we want the
      // rest of start to stop, and it's not like fut['return'] magically makes
      // us jump to a fut.wait() that hasn't happened yet!.
      runner.stop();
      fut.isResolved() || fut['return']({ outcome: 'failure' });
    },
    onRunEnd: function (result) {
      if (once ||
          result.outcome === "conflicting-versions" ||
          result.outcome === "wrong-release" ||
          (result.outcome === "terminated" &&
           result.signal === undefined && result.code === undefined)) {
        // Allow run() to continue (and call runner.stop()) only once the
        // AppRunner has processed our "return false"; otherwise we deadlock.
        process.nextTick(function () {
          fut.isResolved() || fut['return'](result);
        });
        return false;  // stop restarting
      }
      runner.regenerateAppPort();
      return true;  // restart it
    },
    watchForChanges: ! once,
    quiet: once
  });

  var runner = new Runner(appDir, runOptions);
  runner.start();
  Console.enableProgressBar(false);
  var result = fut.wait();
  runner.stop();

  if (result.outcome === "conflicting-versions") {
    process.stderr.write(
"The constraint solver could not find a set of package versions to use that would\n" +
"satisfy the constraints of .meteor/versions and .meteor/packages. This could be\n" +
"caused by conflicts in .meteor/versions, conflicts in .meteor/packages, and/or\n" +
"inconsistent changes to the dependencies in local packages.");
    return 254;
  }

  if (result.outcome === "wrong-release") {
    if (once)
      // We lost a race where the user ran 'meteor update' and 'meteor
      // run --once' simultaneously.
      throw new Error("wrong release?");

    // If the user did not specify a --release on the command line,
    // and simultaneously runs `meteor update` during this run, just
    // exit and let them restart the run. (We can do something fancy
    // like allowing this to work if the tools version didn't change,
    // or even springboarding if the tools version does change, but
    // this (which prevents weird errors) is a start.)
    var to = result.releaseNeeded;
    var from = release.current.name;
    process.stderr.write(
"Your app has been updated to Meteor " + to + " from " + "Meteor " + from +
".\n" +
"Restart meteor to use the new release.");
    return 254;
  }

  if (result.outcome === "failure" ||
      (result.outcome === "terminated" &&
       result.signal === undefined && result.code === undefined)) {
    // Fatal problem with something other than the app process. An
    // explanation should already have been logged.
    return 254;
  }

  if (once && result.outcome === "bundle-fail") {
    process.stderr.write("=> Build failed:\n\n" +
                         result.bundleResult.errors.formatMessages() + "\n");
    return 254;
  }

  if (once && result.outcome === "terminated") {
    if (result.signal) {
      process.stderr.write("Killed (" + result.signal + ")\n");
      return 255;
    } else if (typeof result.code === "number") {
      // We used to print 'Your application is exiting' here, but that
      // seems unnecessarily chatty? once mode is otherwise silent
      return result.code;
    } else {
      // If there is neither a code nor a signal, it means that we
      // failed to start the process. We logged the reason. Probably a
      // bad program name.
      return 254;
    }
  }

  throw new Error("unexpected outcome " + result.outcome);
};
<commit_msg>Make sure listenPort is an int before adding 1 to it<commit_after>var _ = require('underscore');
var Future = require('fibers/future');

var files = require('./files.js');
var release = require('./release.js');
var buildmessage = require('./buildmessage.js');
var fiberHelpers = require('./fiber-helpers.js');
var runLog = require('./run-log.js');

var Console = require('./console.js').Console;

var Proxy = require('./run-proxy.js').Proxy;
var Selenium = require('./run-selenium.js').Selenium;
var HttpProxy = require('./run-httpproxy.js').HttpProxy;
var AppRunner = require('./run-app.js').AppRunner;
var MongoRunner = require('./run-mongo.js').MongoRunner;
var Updater = require('./run-updater.js').Updater;

// options: proxyPort, proxyHost, appPort, appHost, buildOptions,
// settingsFile, banner, program, onRunEnd, onFailure, watchForChanges,
// quiet, rootUrl, mongoUrl, oplogUrl, disableOplog,
// appDirForVersionCheck
var Runner = function (appDir, options) {
  var self = this;
  self.appDir = appDir;

  if (! _.has(options, 'proxyPort'))
    throw new Error("no proxyPort?");

  var listenPort = options.proxyPort;
  var mongoPort = parseInt(listenPort) + 1;
  self.specifiedAppPort = options.appPort;
  self.regenerateAppPort();

  self.stopped = false;
  self.quiet = options.quiet;
  self.banner = options.banner || files.prettyPath(self.appDir);
  if (options.rootUrl) {
    self.rootUrl = options.rootUrl;
  } else if (options.proxyHost) {
    self.rootUrl = 'http://' + options.proxyHost + ':' + listenPort + '/';
  } else {
    self.rootUrl = 'http://localhost:' + listenPort + '/';
  }

  self.extraRunners = options.extraRunners;

  self.proxy = new Proxy({
    listenPort: listenPort,
    listenHost: options.proxyHost,
    proxyToPort: self.appPort,
    proxyToHost: options.appHost,
    onFailure: options.onFailure
  });

  self.httpProxy = null;
  if (options.httpProxyPort) {
    self.httpProxy = new HttpProxy({
      listenPort: options.httpProxyPort
    });
  }

  self.mongoRunner = null;
  var mongoUrl, oplogUrl;
  if (options.mongoUrl) {
    mongoUrl = options.mongoUrl;
    oplogUrl = options.disableOplog ? null : options.oplogUrl;
  } else {
    self.mongoRunner = new MongoRunner({
      appDir: self.appDir,
      port: mongoPort,
      onFailure: options.onFailure,
      // For testing mongod failover, run with 3 mongod if the env var is
      // set. Note that data is not preserved from one run to the next.
      multiple: !!process.env.METEOR_TEST_MULTIPLE_MONGOD_REPLSET
    });

    mongoUrl = self.mongoRunner.mongoUrl();
    oplogUrl = options.disableOplog ? null : self.mongoRunner.oplogUrl();
  }

  self.updater = new Updater;

  self.appRunner = new AppRunner(appDir, {
    appDirForVersionCheck: options.appDirForVersionCheck,
    port: self.appPort,
    listenHost: options.appHost,
    mongoUrl: mongoUrl,
    oplogUrl: oplogUrl,
    buildOptions: options.buildOptions,
    rootUrl: self.rootUrl,
    settingsFile: options.settingsFile,
    program: options.program,
    debugPort: options.debugPort,
    proxy: self.proxy,
    onRunEnd: options.onRunEnd,
    watchForChanges: options.watchForChanges,
    noRestartBanner: self.quiet,
    recordPackageUsage: options.recordPackageUsage
  });

  self.selenium = null;
  if (options.selenium) {
    self.selenium = new Selenium({
      runner: self,
      browser: options.seleniumBrowser
    });
  }
};

_.extend(Runner.prototype, {
  // XXX leave a pidfile and check if we are already running
  start: function () {
    var self = this;

    self.proxy.start();

    // print the banner only once we've successfully bound the port
    if (! self.quiet && ! self.stopped) {
      runLog.log("[[[[[ " + self.banner + " ]]]]]\n");
      runLog.log("=> Started proxy.");
    }

    if (! self.stopped) {
      self.updater.start();
    }

    // print the banner only once we've successfully bound the port
    if (! self.stopped && self.httpProxy) {
      self.httpProxy.start();
      if (! self.quiet) {
        runLog.log("=> Started http proxy.");
      }
    }

    if (! self.stopped && self.mongoRunner) {
      var spinner = ['-', '\\', '|', '/'];
      // I looked at some Unicode indeterminate progress indicators, such as:
      //
      // spinner = "▁▃▄▅▆▇▆▅▄▃".split('');
      // spinner = "▉▊▋▌▍▎▏▎▍▌▋▊▉".split('');
      // spinner = "▏▎▍▌▋▊▉▊▋▌▍▎▏▁▃▄▅▆▇▆▅▄▃".split('');
      // spinner = "▉▊▋▌▍▎▏▎▍▌▋▊▉▇▆▅▄▃▁▃▄▅▆▇".split('');
      // spinner = "⠉⠒⠤⣀⠤⠒".split('');
      //
      // but none of them really seemed like an improvement. I think
      // the case for using unicode would be stronger in a determinate
      // progress indicator.
      //
      // There are also some four-frame options such as ◐◓◑◒ at
      //   http://stackoverflow.com/a/2685827/157965
      // but all of the ones I tried look terrible in the terminal.
      if (! self.quiet) {
        var animationFrame = 0;
        var printUpdate = fiberHelpers.bindEnvironment(function () {
          //runLog.logTemporary("=> Starting MongoDB... " +
          //                    spinner[animationFrame]);
          buildmessage.nudge();
          animationFrame = (animationFrame + 1) % spinner.length;
        });
        printUpdate();
        var mongoProgressTimer = setInterval(printUpdate, 200);
      }

      buildmessage.enterJob({ title: 'Starting MongoDB' }, function () {
        self.mongoRunner.start();
      });

      if (! self.quiet) {
        clearInterval(mongoProgressTimer);
        if (! self.stopped)
          runLog.log("=> Started MongoDB.");
      }
    }

    _.forEach(self.extraRunners, function (extraRunner) {
      if (! self.stopped) {
        var title = extraRunner.title;
        if (! self.quiet)
          runLog.logTemporary("=> Starting " + title + "...");
        extraRunner.start();
        if (! self.quiet && ! self.stopped)
          runLog.log("=> Started " + title + ".");
      }
    });

    if (! self.stopped) {
      if (! self.quiet)
        runLog.logTemporary("=> Starting your app...");
      self.appRunner.start();
      if (! self.quiet && ! self.stopped)
        runLog.log("=> Started your app.");
    }

    if (! self.stopped && ! self.quiet)
      runLog.log("\n=> App running at: " + self.rootUrl);

    if (self.selenium && ! self.stopped) {
      if (! self.quiet)
        runLog.logTemporary("=> Starting Selenium...");
      self.selenium.start();
      if (! self.quiet && ! self.stopped)
        runLog.log("=> Started Selenium.");
    }

    // XXX It'd be nice to (cosmetically) handle failure better. Right
    // now we overwrite the "starting foo..." message with the
    // error. It'd be better to overwrite it with "failed to start
    // foo" and then print the error.
  },

  // Idempotent
  stop: function () {
    var self = this;
    if (self.stopped)
      return;

    self.stopped = true;
    self.proxy.stop();
    self.httpProxy && self.httpProxy.stop();
    self.updater.stop();
    self.mongoRunner && self.mongoRunner.stop();
    _.forEach(self.extraRunners, function (extraRunner) {
      extraRunner.stop();
    });
    self.appRunner.stop();
    self.selenium && self.selenium.stop();
    // XXX does calling this 'finish' still make sense now that runLog is a
    // singleton?
    runLog.finish();
  },

  // Call this whenever you want to regenerate the app's port (if it is not
  // explicitly specified by the user).
  //
  // Rationale: if we randomly chose a port that's in use and the app failed to
  // listen on it, we should try a different port when we restart the app!
  regenerateAppPort: function () {
    var self = this;
    if (self.specifiedAppPort) {
      self.appPort = self.specifiedAppPort;
    } else {
      self.appPort = require('./utils.js').randomPort();
    }
    if (self.proxy)
      self.proxy.proxyToPort = self.appPort;
    if (self.appRunner)
      self.appRunner.port = self.appPort;
  }
});

// Run the app and all of its associated processes. Runs (and does not
// return) until an unrecoverable failure happens. Logs to
// stdout. Returns a suggested exit code.
//
// If 'once' is set, run the app process exactly once and pass through
// its exit code. Return an exit code of 255 if the app process was
// killed by a signal and 254 if the app process could not start
// (build failure, invalid program name, database couldn't start, and
// so on).
//
// If the 'once' option is not set, the default, restart the app
// process if it crashes or if source files change. (Non-app
// processes, such as the database, are always restarted as
// necessary.) The function will only return if there is an
// unrecoverable error, which generally means an error that could not
// be fixed by source code changes (such as the database refusing to
// run), but also currently includes Meteor version mismatches. So the
// exit code will always be 254 because in all other cases we'll
// persevere.
//
// Options:
//
// - proxyPort: the port to connect to to access the application (we will
//   run a proxy here that proxies to the actual app process). required
// - buildOptions: 'buildOptions' argument to bundler.bundle()
// - settingsFile: path to file containing deploy-time settings
// - program: the program in the app bundle to run
// - once: see above
// - banner: replace the application path that is normally printed on
//   startup with an arbitrary string (eg, 'Tests')
// - rootUrl: tell the app that traffic at this URL will be routed to
//   it at '/' (used by the app to construct absolute URLs)
// - disableOplog: don't use oplog tailing
// - mongoUrl: don't start a mongo process; instead use the mongo at
//   this mongo URL
// - oplogUrl: URL of the mongo oplog to use. if mongoUrl isn't
//   set (we're starting a mongo) a default will be provided, but can
//   be overridden. if mongoUrl is set, you must set this or you don't
//   get oplog tailing.
// - appDirForVersionCheck: when checking whether we're running the
//   right release of Meteor, check against this app rather than
//   appDir. Useful when you have autogenerated a test harness app
//   based on some other app.
// - recordPackageUsage: (default true) if set to false, don't send
//   information about packages used by this app to the package stats
//   server.
exports.run = function (appDir, options) {
  var runOptions = _.clone(options);
  var once = runOptions.once;
  delete runOptions.once;

  var fut = new Future;

  _.extend(runOptions, {
    onFailure: function () {
      // Ensure that runner stops now. You might think this is unnecessary
      // because the runner is stopped immediately after `fut.wait()`, but if
      // the failure happens while runner.start() is still running, we want the
      // rest of start to stop, and it's not like fut['return'] magically makes
      // us jump to a fut.wait() that hasn't happened yet!.
      runner.stop();
      fut.isResolved() || fut['return']({ outcome: 'failure' });
    },
    onRunEnd: function (result) {
      if (once ||
          result.outcome === "conflicting-versions" ||
          result.outcome === "wrong-release" ||
          (result.outcome === "terminated" &&
           result.signal === undefined && result.code === undefined)) {
        // Allow run() to continue (and call runner.stop()) only once the
        // AppRunner has processed our "return false"; otherwise we deadlock.
        process.nextTick(function () {
          fut.isResolved() || fut['return'](result);
        });
        return false;  // stop restarting
      }
      runner.regenerateAppPort();
      return true;  // restart it
    },
    watchForChanges: ! once,
    quiet: once
  });

  var runner = new Runner(appDir, runOptions);
  runner.start();
  Console.enableProgressBar(false);
  var result = fut.wait();
  runner.stop();

  if (result.outcome === "conflicting-versions") {
    process.stderr.write(
"The constraint solver could not find a set of package versions to use that would\n" +
"satisfy the constraints of .meteor/versions and .meteor/packages. This could be\n" +
"caused by conflicts in .meteor/versions, conflicts in .meteor/packages, and/or\n" +
"inconsistent changes to the dependencies in local packages.");
    return 254;
  }

  if (result.outcome === "wrong-release") {
    if (once)
      // We lost a race where the user ran 'meteor update' and 'meteor
      // run --once' simultaneously.
      throw new Error("wrong release?");

    // If the user did not specify a --release on the command line,
    // and simultaneously runs `meteor update` during this run, just
    // exit and let them restart the run. (We can do something fancy
    // like allowing this to work if the tools version didn't change,
    // or even springboarding if the tools version does change, but
    // this (which prevents weird errors) is a start.)
    var to = result.releaseNeeded;
    var from = release.current.name;
    process.stderr.write(
"Your app has been updated to Meteor " + to + " from " + "Meteor " + from +
".\n" +
"Restart meteor to use the new release.");
    return 254;
  }

  if (result.outcome === "failure" ||
      (result.outcome === "terminated" &&
       result.signal === undefined && result.code === undefined)) {
    // Fatal problem with something other than the app process. An
    // explanation should already have been logged.
    return 254;
  }

  if (once && result.outcome === "bundle-fail") {
    process.stderr.write("=> Build failed:\n\n" +
                         result.bundleResult.errors.formatMessages() + "\n");
    return 254;
  }

  if (once && result.outcome === "terminated") {
    if (result.signal) {
      process.stderr.write("Killed (" + result.signal + ")\n");
      return 255;
    } else if (typeof result.code === "number") {
      // We used to print 'Your application is exiting' here, but that
      // seems unnecessarily chatty? once mode is otherwise silent
      return result.code;
    } else {
      // If there is neither a code nor a signal, it means that we
      // failed to start the process. We logged the reason. Probably a
      // bad program name.
      return 254;
    }
  }

  throw new Error("unexpected outcome " + result.outcome);
};
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');

module.exports = function() {
    var pathSep = process.platform === 'win32' ? ';' : ':';

    var directories = process.env.PATH.split(pathSep);

    for (var i = 0; i < directories.length; i++) {
    	for (var j = 0; j < arguments.length; j++) {
    		var filename = arguments[j];
	        var filePath = path.join(directories[i], filename);

	        if (fs.existsSync(filePath)) {
	            return filePath;
	        }
	    }
    }

    return null;
}<commit_msg>Fix install script<commit_after>var fs = require('fs');
var path = require('path');

module.exports = function() {
    var pathSep = process.platform === 'win32' ? ';' : ':';

    var directories = process.env.PATH.split(pathSep);

    for (var i = 0; i < directories.length; i++) {
    	for (var j = 0; j < arguments.length; j++) {
    		var filename = arguments[j];
	        var filePath = path.join(directories[i], filename);

	        if (fs.existsSync(filePath) && fs.lstatSync(filePath).isFile()) {
	            return filePath;
	        }
	    }
    }

    return null;
}<|endoftext|>


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

const attachTreeSelection = (function() {
  const filter = {
    acceptNode: function(node) {
      const role = node.getAttribute('role');

      if (role === 'treeitem') {
        return NodeFilter.FILTER_ACCEPT;
      }

      return NodeFilter.FILTER_SKIP;
    }
  };

  function isExpandedGroup(el) {
    return el.getAttribute('aria-expanded') === 'true';
  }

  function isGroup(el) {
    return el.hasAttribute('aria-expanded');
  }

  function setExpanded(el, expanded) {
    el.setAttribute('aria-expanded', expanded);
  }

  function setSelected(el, selected) {
    const evtName = selected ? 'selected' : 'deselected';

    el.tabIndex = selected ? 0 : -1;
    el.setAttribute('aria-selected', selected);
    el.dispatchEvent(new CustomEvent(evtName, {
      bubbles: true
    }));
  }

  function toggleExpanded(el) {
    if (isGroup(el)) {
      setExpanded(el, !isExpandedGroup(el));
    }
  }

  function first(walker) {
    walker.currentNode = walker.root;
    walker.nextNode();
    walker.currentNode.focus();
  }

  function last(walker) {
    walker.currentNode = walker.root;
    while(walker.lastChild() && isExpandedGroup(walker.currentNode));
    walker.currentNode.focus();
  }

  function handleBoundary(walker, forward, wrap) {
    if (forward == wrap) {
      first(walker);
    } else {
      last(walker);
    }
  }

  function next(walker, wrap, trap) {
    if (walker.currentNode === walker.root) {
      first(walker);
    } else if (trap) {
      if (!walker.nextSibling() && wrap) {
        while(walker.previousSibling());
      }
    } else if (isExpandedGroup(walker.currentNode)) {
      walker.firstChild();
    } else {
      while(!walker.nextSibling()) {
        if (!walker.parentNode()) {
          handleBoundary(walker, true, wrap);
          break;
        }
      }
    }

    walker.currentNode.focus();
  }

  function previous(walker, wrap, trap) {
    if (walker.currentNode === walker.root) {
      last(walker);
    } else if (trap) {
      if (!walker.previousSibling() && wrap) {
        while(walker.nextSibling());
      }
    } else if (!walker.previousSibling()) {
      if (!walker.parentNode()) {
        handleBoundary(walker, false, wrap);
      }
    } else {
      while(isExpandedGroup(walker.currentNode) && walker.lastChild());
    }

    walker.currentNode.focus();
  }

  function rightArrow(walker) {
    if (!isGroup(walker.currentNode)) {
      return;
    }

    setExpanded(walker.currentNode, true);
    walker.firstChild();
    walker.currentNode.focus();
  }

  function leftArrow(walker) {
    if (isExpandedGroup(walker.currentNode)) {
      setExpanded(walker.currentNode, false);
    } else {
      walker.parentNode();
    }

    walker.currentNode.focus();
  }

  function expandAll(walker) {
    const groups = walker.root.querySelectorAll('[aria-expanded]');
    for (const i = 0; i < groups.length; i += 1) {
      setExpanded(groups[i], true);
    }
  }

  return function(el, config) {
    const { wrap, trap } = config || {};
    const walker = document.createTreeWalker(el, NodeFilter.SHOW_ELEMENT, filter);
    let selected = walker.root;

    el.addEventListener('keydown', function(e) {
      let handled = true;

      switch (e.keyCode) {
        case 13: // enter
          toggleExpanded(walker.currentNode);
          break;
        case 35: // end
          last(walker);
          break;
        case 36: // home
          first(walker);
          break;
        case 37: // left
          leftArrow(walker);
          break;
        case 38: // up
          previous(walker, wrap, trap);
          break;
        case 39: // right
          rightArrow(walker);
          break;
        case 40: // down
          next(walker, wrap, trap);
          break;
        case 106: // keypad *
          expandAll(walker);
          break;
        default:
          handled = false;
          break;
      }

      if (handled) {
        e.preventDefault();
      }
    });

    el.addEventListener('focus', function(e) {
      const item = e.target.closest('[role="treeitem"]');

      if (!item || item === selected) {
        return;
      }

      if (selected) {
        setSelected(selected, false);
      }

      walker.currentNode = selected = item;
      setSelected(selected, true);
    }, true);

    el.addEventListener('dblclick', function(e) {
      const item = e.target.closest('[role="treeitem"]');

      if (!item) {
        return;
      }

      toggleExpanded(item);
      e.stopPropagation();
    });
  }
})();
<commit_msg>Fix loop variable being const.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const attachTreeSelection = (function() {
  const filter = {
    acceptNode: function(node) {
      const role = node.getAttribute('role');

      if (role === 'treeitem') {
        return NodeFilter.FILTER_ACCEPT;
      }

      return NodeFilter.FILTER_SKIP;
    }
  };

  function isExpandedGroup(el) {
    return el.getAttribute('aria-expanded') === 'true';
  }

  function isGroup(el) {
    return el.hasAttribute('aria-expanded');
  }

  function setExpanded(el, expanded) {
    el.setAttribute('aria-expanded', expanded);
  }

  function setSelected(el, selected) {
    const evtName = selected ? 'selected' : 'deselected';

    el.tabIndex = selected ? 0 : -1;
    el.setAttribute('aria-selected', selected);
    el.dispatchEvent(new CustomEvent(evtName, {
      bubbles: true
    }));
  }

  function toggleExpanded(el) {
    if (isGroup(el)) {
      setExpanded(el, !isExpandedGroup(el));
    }
  }

  function first(walker) {
    walker.currentNode = walker.root;
    walker.nextNode();
    walker.currentNode.focus();
  }

  function last(walker) {
    walker.currentNode = walker.root;
    while(walker.lastChild() && isExpandedGroup(walker.currentNode));
    walker.currentNode.focus();
  }

  function handleBoundary(walker, forward, wrap) {
    if (forward == wrap) {
      first(walker);
    } else {
      last(walker);
    }
  }

  function next(walker, wrap, trap) {
    if (walker.currentNode === walker.root) {
      first(walker);
    } else if (trap) {
      if (!walker.nextSibling() && wrap) {
        while(walker.previousSibling());
      }
    } else if (isExpandedGroup(walker.currentNode)) {
      walker.firstChild();
    } else {
      while(!walker.nextSibling()) {
        if (!walker.parentNode()) {
          handleBoundary(walker, true, wrap);
          break;
        }
      }
    }

    walker.currentNode.focus();
  }

  function previous(walker, wrap, trap) {
    if (walker.currentNode === walker.root) {
      last(walker);
    } else if (trap) {
      if (!walker.previousSibling() && wrap) {
        while(walker.nextSibling());
      }
    } else if (!walker.previousSibling()) {
      if (!walker.parentNode()) {
        handleBoundary(walker, false, wrap);
      }
    } else {
      while(isExpandedGroup(walker.currentNode) && walker.lastChild());
    }

    walker.currentNode.focus();
  }

  function rightArrow(walker) {
    if (!isGroup(walker.currentNode)) {
      return;
    }

    setExpanded(walker.currentNode, true);
    walker.firstChild();
    walker.currentNode.focus();
  }

  function leftArrow(walker) {
    if (isExpandedGroup(walker.currentNode)) {
      setExpanded(walker.currentNode, false);
    } else {
      walker.parentNode();
    }

    walker.currentNode.focus();
  }

  function expandAll(walker) {
    const groups = walker.root.querySelectorAll('[aria-expanded]');
    for (let i = 0; i < groups.length; i += 1) {
      setExpanded(groups[i], true);
    }
  }

  return function(el, config) {
    const { wrap, trap } = config || {};
    const walker = document.createTreeWalker(el, NodeFilter.SHOW_ELEMENT, filter);
    let selected = walker.root;

    el.addEventListener('keydown', function(e) {
      let handled = true;

      switch (e.keyCode) {
        case 13: // enter
          toggleExpanded(walker.currentNode);
          break;
        case 35: // end
          last(walker);
          break;
        case 36: // home
          first(walker);
          break;
        case 37: // left
          leftArrow(walker);
          break;
        case 38: // up
          previous(walker, wrap, trap);
          break;
        case 39: // right
          rightArrow(walker);
          break;
        case 40: // down
          next(walker, wrap, trap);
          break;
        case 106: // keypad *
          expandAll(walker);
          break;
        default:
          handled = false;
          break;
      }

      if (handled) {
        e.preventDefault();
      }
    });

    el.addEventListener('focus', function(e) {
      const item = e.target.closest('[role="treeitem"]');

      if (!item || item === selected) {
        return;
      }

      if (selected) {
        setSelected(selected, false);
      }

      walker.currentNode = selected = item;
      setSelected(selected, true);
    }, true);

    el.addEventListener('dblclick', function(e) {
      const item = e.target.closest('[role="treeitem"]');

      if (!item) {
        return;
      }

      toggleExpanded(item);
      e.stopPropagation();
    });
  }
})();
<|endoftext|>