<commit_before>// start anonymous scope
;(function() {
'use strict';

var dataRef = new Firebase("https://vidsync.firebaseio.com/");
var chatRef = new Firebase("https://vidsync.firebaseio.com/**chats**");
var id = Math.random().toString(16).slice(2);

var ytplayer;
var mp4player;
var controls;

var roomName;
var tokboxSession;
var tokboxToken;

var last_command;

// variables to modify behavior of incoming on room creation
var first_run;
var creating;

// how far behind a standardized time our local clock is
var realTimeOffset;

// setup
function init(){
  // listeners
  $("#create").click(function() {
    makeRoom($("#room")[0].value)
  });
  $("#join").click(function() {
    joinRoom($("#room")[0].value)
  });

  if (window.location.hash != "")
    joinRoom(window.location.hash.substring(1));

  filepicker.setKey("AzauR2MBSBeslVoQIcZ8gz");

  $.getJSON("php/time.php", function(res){
    realTimeOffset = parseInt(res.timestamp) - now()
  })

  toggleSidebar();
  $("#slide").click(toggleSidebar);

  if (window.location.hash != "")
    joinRoom(window.location.hash.substring(1));
}

// initiate room creation
function makeRoom (name) {
  dataRef = dataRef.root();
  creating = true;
  // get tokbox credentials
  $.getJSON("php/generate.php", function(res){
    dataRef = dataRef.child(name);
    dataRef.on('value', incoming);
    dataRef.update(
    {
      type: null,
      video: null,
      seek: 0,
      timestamp: now(),
      playing: false,
      sessionId: res.sessionId,
      token: res.token,
      owner: id
    });
    chatRef = chatRef.child(name);
    chatRef.set(null);
    chatRef.on('child_added', chatIn);
    first_run = true;
    roomName = name;
  });
}

// join a room
function joinRoom (name) {
  dataRef = dataRef.root();
  dataRef = dataRef.child(name);
  dataRef.on('value', incoming);
  chatRef = chatRef.child(name);
  chatRef.on('child_added', chatIn)
  first_run = true;
  roomName = name;

  if (!$("#slide").hasClass("flip"))
    toggleSidebar();

  window.location.hash=name;
}
function setupRoom(){
  $("#inputs").html("
	" +
      "<input id='vid' type='text' placeholder='Choose a video (Youtube or MP4)'></input>	" +
      "<button id='choose'>Load</button><button id='upload'>Upload</button>" +
      "

");

  $("#choose").click(function(){
    var vid = $("#vid")[0].value + "#";
    if (vid.indexOf("youtube.com")>-1){
      var key = vid.substring(vid.indexOf("v=")+2, vid.indexOf("#"));
      var type = "YT";
      dataRef.update({type: type, video: key, seek: 0});
    }
    else if (vid.indexOf(".mp4") > -1){
      dataRef.update({type:"mp4", video: $("#vid")[0].value, seek: 0});
    }
    else
      alert ("Link not recognized");
  });

  $("#message").keyup(function(e){
    e.preventDefault();
    var key = window.event ? e.keyCode : e.which;
    if (key == '13') {
      chatOut($("#message")[0].value);
      $("#message")[0].value = null;
    }
  });
  $("#upload").click(function(){
    // hack for filepicker logo
    function coverUp() {
      var iframe = $("#filepicker_dialog")
      var x = iframe.offset().left;
      var y = iframe.offset().top;
      var cover = $("<div class='cover' style=\"z-index: 99999999999999; position:absolute;background:white; width:200px; height:75px;top:"+y+";left:"+x+"\"
").appendTo("body")
    }

    filepicker.pickAndStore(
      {extension: '.mp4', multiple:false},
      {location: 'S3'},
      function(InkBlobs){
        console.log(InkBlobs);
        var key = InkBlobs[0].key;
        var url = "http://s3-us-west-2.amazonaws.com/vidsync/" + key;
        dataRef.update({type: "mp4", video: url});
        $(".cover").hide();
      },
      function(FEError){
        $(".cover").hide();
      });
    coverUp();
  });

  if ($("#chat h1").length == 0)
    $("#chat").prepend("
"+roomName+"
");
  $("#chat").show();
  if (tokboxSession == 0)
    alert ("Sorry, our Tokbox trial has expired. Please enjoy VidSync without webcam service for now.");
  else
    runWebcam(tokboxSession, tokboxToken);
  first_run = false;
}
// handle an incoming firebase message
function incoming (fb) {
  var data = fb.val();

  if (!creating && data == null){
    alert("This room does not exist! Creating a new room.");
    makeRoom(roomName);
    return;
  }
  else if (data == null){
    return;
  }
  creating = false;

  if (last_command && (data.video != last_command.video || data.type != last_command.type)){
    $("#players").html("");
    ytplayer = null;
    mp4player = null;
  }

  last_command = data;
  if (first_run){
    tokboxSession = data.sessionId;
    tokboxToken = data.token;
    setupRoom();
  }

  if (data.type == "YT") {
    if (ytplayer == null)
      makeYtPlayer(data.video);
    else
      handleYtCommand(data);
  }
  else if (data.type == "mp4"){
    if (mp4player == null)
      makeMp4Player(data);
    else
      handleMp4Command(data);
  }
}

function handleYtCommand (data){
  if (data.playing){
    data.seek += now() - data.timestamp + realTimeOffset;
  }
  ytplayer.seekTo(data.seek);
  controls.seek(data.seek)

  if (data.playing){
    ytplayer.playVideo()
    controls.playPause(true)
    setTimeout(updateSeek,500)
  }
  else if (!data.playing){
    ytplayer.pauseVideo()
    controls.playPause(false)
  }
}

function handleMp4Command (data){
  if (data.playing){
    data.seek += now() - data.timestamp + realTimeOffset;
  }
  mp4player.currentTime = data.seek;
  controls.seek(data.seek)

  if (data.playing){
    mp4player.play();
    controls.playPause(true)
    setTimeout(updateSeek,500)
  }
  else if (!data.playing){
    mp4player.pause();
    controls.playPause(false)
  }

}

// send a player action to the server
function outgoing (playing, seek) {
  dataRef.update(
    {playing: playing, seek: seek, timestamp: now(), owner: id}
  );
}

// handle an incoming chat firebase message
function chatIn (fb) {
  var data = fb.val();
  if (!data || !data.name || !  data.message)
    return;
  var date = new Date(data.timestamp).toLocaleTimeString()
  var me = data.id == id ? " class='self'" : "";
  var html = "
" +
                "" + data.name + " " +
                "" +
                date.substring(0, date.length - 6) + " " +
                "" + data.message + "" +
            "
";
  $("#chat ul").append(html);

  var chatbox = $("#chatbox")[0];
  chatbox.scrollTop = chatbox.scrollHeight;
}

// send a chat to the server
function chatOut (msg) {
  var nickname = $("#nickname")[0].value;
  if (msg == "" || !msg)
    return;
  chatRef.push({
    id: id,
    name: (nickname ? nickname : id),
    message: msg,
    timestamp: new Date().getTime()
  })
}

function now () {
  return new Date().getTime() / 1000;
}

// player functions

function makeYtPlayer(vid) {
  function onPlayerReady (){
    makeControls(ytplayer.getDuration())
    handleYtCommand(last_command);
  }

  $("#players").html("

");
  ytplayer = new YT.Player('ytplayer', {
    height: '100%',
    width: '100%',
    videoId: vid,
    events: {
      'onReady': onPlayerReady,
    },
    playerVars: {
      'controls': 0,
      'showinfo': 0,
      'modestbranding': 1,
      'enablejsapi': 1,
      'disablekb': 1,
      'fs': 0,

    },
  });
}

function makeMp4Player(data){

  function onPlayerReady(){
    makeControls(mp4player.duration);
    handleMp4Command(last_command);
  }
  $("#players").html(
    "<video id='mp4player' width='100%' height='100%'>" +
    "  <source src='" + data.video + "' type='video/mp4'>" +
    "  Sorry, your browser does not support HTML5 videos!" +
    "</video>")
  mp4player = $("#mp4player")[0];
  mp4player.onloadeddata = onPlayerReady
}

function updateSeek (){
  if (!getPlaying())
    return

  controls.seek(getCurrentTime())

  setTimeout(updateSeek, 500)
}

function getCurrentTime(){
  if (ytplayer)
    return ytplayer.getCurrentTime();
  else if (mp4player)
    return mp4player.currentTime;
  else
    return null;
}

function getPlaying(){
  if (ytplayer)
    return ytplayer.getPlayerState() == 1;
  else if (mp4player)
    return !mp4player.paused;
  else
    return null;
}

function makeControls(duration){
  $("#controls").css({
    top: $("#players").offset().top,
    left: $("#players").offset().left,
    width: $("#players").width(),
    height: $("#players").height()
  })
  controls = Controls("controls", duration, {
    onPlayPause: function(playing){
      outgoing(playing, getCurrentTime())
    },
    onSeek: function(seek){
      outgoing(getPlaying(), seek)
    }
  })
  updateSeek()
}

function toggleSidebar (){
  if($("#slide").hasClass("flip"))
    $("#right").animate({width: 50})
  else
    $("#right").animate({width: 200})
  $("#slide").toggleClass('flip');
}

// start!
$(document).ready(function(){
  init();
})

})(); // end anonymous wrapper
<commit_msg>fix offset bug<commit_after>// start anonymous scope
;(function() {
'use strict';

var dataRef = new Firebase("https://vidsync.firebaseio.com/");
var chatRef = new Firebase("https://vidsync.firebaseio.com/**chats**");
var id = Math.random().toString(16).slice(2);

var ytplayer;
var mp4player;
var controls;

var roomName;
var tokboxSession;
var tokboxToken;

var last_command;

// variables to modify behavior of incoming on room creation
var first_run;
var creating;

// how far behind a standardized time our local clock is
var realTimeOffset;

// setup
function init(){
  // listeners
  $("#create").click(function() {
    makeRoom($("#room")[0].value)
  });
  $("#join").click(function() {
    joinRoom($("#room")[0].value)
  });

  if (window.location.hash != "")
    joinRoom(window.location.hash.substring(1));

  filepicker.setKey("AzauR2MBSBeslVoQIcZ8gz");

  $.getJSON("php/time.php", function(res){
    realTimeOffset = parseInt(res.timestamp) - now()
    console.log(realTimeOffset)
  })

  toggleSidebar();
  $("#slide").click(toggleSidebar);

  if (window.location.hash != "")
    joinRoom(window.location.hash.substring(1));
}

// initiate room creation
function makeRoom (name) {
  dataRef = dataRef.root();
  creating = true;
  // get tokbox credentials
  $.getJSON("php/generate.php", function(res){
    dataRef = dataRef.child(name);
    dataRef.on('value', incoming);
    dataRef.update(
    {
      type: null,
      video: null,
      seek: 0,
      timestamp: now() + realTimeOffset,
      playing: false,
      sessionId: res.sessionId,
      token: res.token,
      owner: id
    });
    chatRef = chatRef.child(name);
    chatRef.set(null);
    chatRef.on('child_added', chatIn);
    first_run = true;
    roomName = name;
  });
}

// join a room
function joinRoom (name) {
  dataRef = dataRef.root();
  dataRef = dataRef.child(name);
  dataRef.on('value', incoming);
  chatRef = chatRef.child(name);
  chatRef.on('child_added', chatIn)
  first_run = true;
  roomName = name;

  if (!$("#slide").hasClass("flip"))
    toggleSidebar();

  window.location.hash=name;
}
function setupRoom(){
  $("#inputs").html("
	" +
      "<input id='vid' type='text' placeholder='Choose a video (Youtube or MP4)'></input>	" +
      "<button id='choose'>Load</button><button id='upload'>Upload</button>" +
      "

");

  $("#choose").click(function(){
    var vid = $("#vid")[0].value + "#";
    if (vid.indexOf("youtube.com")>-1){
      var key = vid.substring(vid.indexOf("v=")+2, vid.indexOf("#"));
      var type = "YT";
      dataRef.update({type: type, video: key, seek: 0, playing: false});
    }
    else if (vid.indexOf(".mp4") > -1){
      dataRef.update({type:"mp4", video: $("#vid")[0].value, seek: 0, playing: false});
    }
    else
      alert ("Link not recognized");
  });

  $("#message").keyup(function(e){
    e.preventDefault();
    var key = window.event ? e.keyCode : e.which;
    if (key == '13') {
      chatOut($("#message")[0].value);
      $("#message")[0].value = null;
    }
  });
  $("#upload").click(function(){
    // hack for filepicker logo
    function coverUp() {
      var iframe = $("#filepicker_dialog")
      var x = iframe.offset().left;
      var y = iframe.offset().top;
      var cover = $("<div class='cover' style=\"z-index: 99999999999999; position:absolute;background:white; width:200px; height:75px;top:"+y+";left:"+x+"\"
").appendTo("body")
    }

    filepicker.pickAndStore(
      {extension: '.mp4', multiple:false},
      {location: 'S3'},
      function(InkBlobs){
        console.log(InkBlobs);
        var key = InkBlobs[0].key;
        var url = "http://s3-us-west-2.amazonaws.com/vidsync/" + key;
        dataRef.update({type: "mp4", video: url, seek: 0, playing: false});
        $(".cover").hide();
      },
      function(FEError){
        $(".cover").hide();
      });
    coverUp();
  });

  if ($("#chat h1").length == 0)
    $("#chat").prepend("
"+roomName+"
");
  $("#chat").show();
  if (tokboxSession == 0)
    alert ("Sorry, our Tokbox trial has expired. Please enjoy VidSync without webcam service for now.");
  else
    runWebcam(tokboxSession, tokboxToken);
  first_run = false;
}
// handle an incoming firebase message
function incoming (fb) {
  var data = fb.val();

  if (!creating && data == null){
    alert("This room does not exist! Creating a new room.");
    makeRoom(roomName);
    return;
  }
  else if (data == null){
    return;
  }
  creating = false;

  if (last_command && (data.video != last_command.video || data.type != last_command.type)){
    $("#players").html("");
    ytplayer = null;
    mp4player = null;
  }

  last_command = data;
  if (first_run){
    tokboxSession = data.sessionId;
    tokboxToken = data.token;
    setupRoom();
  }

  data.timestamp -= realTimeOffset;

  if (data.type == "YT") {
    if (ytplayer == null)
      makeYtPlayer(data.video);
    else
      handleYtCommand(data);
  }
  else if (data.type == "mp4"){
    if (mp4player == null)
      makeMp4Player(data);
    else
      handleMp4Command(data);
  }
}

function handleYtCommand (data){
  if (data.playing){
    data.seek += now() - data.timestamp;
  }
  ytplayer.seekTo(data.seek);
  controls.seek(data.seek)

  if (data.playing){
    ytplayer.playVideo()
    controls.playPause(true)
    setTimeout(updateSeek,1000)
  }
  else if (!data.playing){
    ytplayer.pauseVideo()
    controls.playPause(false)
  }
}

function handleMp4Command (data){
  if (data.playing){
    data.seek += now() - data.timestamp;
  }
  mp4player.currentTime = data.seek;
  controls.seek(data.seek)

  if (data.playing){
    mp4player.play();
    controls.playPause(true)
    setTimeout(updateSeek,1000)
  }
  else if (!data.playing){
    mp4player.pause();
    controls.playPause(false)
  }

}

// send a player action to the server
function outgoing (playing, seek) {
  dataRef.update(
    {playing: playing, seek: seek, timestamp: now() + realTimeOffset, owner: id}
  );
}

// handle an incoming chat firebase message
function chatIn (fb) {
  var data = fb.val();
  if (!data || !data.name || !  data.message)
    return;
  var date = new Date(data.timestamp).toLocaleTimeString()
  var me = data.id == id ? " class='self'" : "";
  var html = "
" +
                "" + data.name + " " +
                "" +
                date.substring(0, date.length - 6) + " " +
                "" + data.message + "" +
            "
";
  $("#chat ul").append(html);

  var chatbox = $("#chatbox")[0];
  chatbox.scrollTop = chatbox.scrollHeight;
}

// send a chat to the server
function chatOut (msg) {
  var nickname = $("#nickname")[0].value;
  if (msg == "" || !msg)
    return;
  chatRef.push({
    id: id,
    name: (nickname ? nickname : id),
    message: msg,
    timestamp: new Date().getTime()
  })
}

function now () {
  return new Date().getTime() / 1000;
}

// player functions

function makeYtPlayer(vid) {
  function onPlayerReady (){
    makeControls(ytplayer.getDuration())
    handleYtCommand(last_command);
  }

  $("#players").html("

");
  ytplayer = new YT.Player('ytplayer', {
    height: '100%',
    width: '100%',
    videoId: vid,
    events: {
      'onReady': onPlayerReady,
    },
    playerVars: {
      'controls': 0,
      'showinfo': 0,
      'modestbranding': 1,
      'enablejsapi': 1,
      'disablekb': 1,
      'fs': 0,

    },
  });
}

function makeMp4Player(data){

  function onPlayerReady(){
    makeControls(mp4player.duration);
    handleMp4Command(last_command);
  }
  $("#players").html(
    "<video id='mp4player' width='100%' height='100%'>" +
    "  <source src='" + data.video + "' type='video/mp4'>" +
    "  Sorry, your browser does not support HTML5 videos!" +
    "</video>")
  mp4player = $("#mp4player")[0];
  mp4player.onloadeddata = onPlayerReady
}

function updateSeek (){
  if (!getPlaying())
    return

  controls.seek(getCurrentTime())

  setTimeout(updateSeek, 500)
}

function getCurrentTime(){
  if (ytplayer)
    return ytplayer.getCurrentTime();
  else if (mp4player)
    return mp4player.currentTime;
  else
    return null;
}

function getPlaying(){
  if (ytplayer)
    return ytplayer.getPlayerState() == 1;
  else if (mp4player)
    return !mp4player.paused;
  else
    return null;
}

function makeControls(duration){
  $("#controls").css({
    top: $("#players").offset().top,
    left: $("#players").offset().left,
    width: $("#players").width(),
    height: $("#players").height()
  })
  controls = Controls("controls", duration, {
    onPlayPause: function(playing){
      outgoing(playing, getCurrentTime())
    },
    onSeek: function(seek){
      outgoing(getPlaying(), seek)
    }
  })
  updateSeek()
}

function toggleSidebar (){
  if($("#slide").hasClass("flip"))
    $("#right").animate({width: 50})
  else
    $("#right").animate({width: 200})
  $("#slide").toggleClass('flip');
}

// start!
$(document).ready(function(){
  init();
})

})(); // end anonymous wrapper
<|endoftext|>


<commit_before><commit_msg>added tests<commit_after><|endoftext|>


<commit_before>define(
	[
		"./meta",
		"./class",
		"./type",
		"./bind",
		"./array"
	],
	function (meta, Class, type) {
		var Base = Class(
			meta({
				"entity": "class",
				"name": "Base",
				"description": "Abstract class."
			})
		);

		Base.method(
			meta({
				"name": "superConstructor",
				"description": "Calls this._super.constructor with this as the context.",
				"arguments": [{
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": superConstructor
			})
		);

		Base.method(
			meta({
				"name": "superConstructorApply",
				"description": "Applies this._super.constructor with this as the context.",
				"signature": "arguments|array",
				"arguments": [{
					"type": "arguments|array",
					"name": "args",
					"required": false
				}],
				"implementation": superConstructorApply
			})
		);

		Base.method(
			meta({
				"name": "superInvoke",
				"description": "Invokes a super method with this as the context.",
				"arguments": [{
					"type": "string",
					"name": "method",
					"required": true
				}, {
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": superInvoke,
				"returns": "any"
			})
		);

		Base.method
			meta({
				"name": "superApply",
				"description": "Invokes a super method with this as the context; applying args.",
				"arguments": [{
					"type": "arguments|array",
					"name": "args",
					"required": false
				}],
				"implementation": superApply,
				"returns": "any"
			})
		);

		Base.method(
			meta({
				"name": "invoke",
				"description": "Useful for invoking local functions as private methods.",
				"arguments": [{
					"type": "function",
					"name": "fn"
				}, {
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": invokeFunction,
				"returns": "any"
			})
		);

		Base.method(
			meta({
				"name": "invoke",
				"description": "Included for consistency with the proxy method api.",
				"arguments": [{
					"type": "string",
					"name": "method"
				}, {
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": invokeMethod,
				"returns": "any"
			})
		);

		Base.method(
			meta({
				"name": "proxy",
				"description": "Creates a function bound to this; useful for attaching event listeners.",
				"arguments": [{
					"type": "function",
					"name": "fn",
					"description": "Any function in scope. Doesn't need to be a public method."
				}, {
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": proxyFunction,
				"returns": "function"
			})
		);

		Base.method(
			meta({
				"name": "proxy",
				"description": "Creates a function bound to this; useful for attaching event listeners.",
				"arguments": [{
					"type": "string",
					"name": "method",
					"description": "The name of the method to bind."
				}, {
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": proxyMethod,
				"returns": "function"
			})
		);

		Base.prototype._super = {};

		function superConstructor () {
			return this._super.constructor.apply(this, arguments);
		}

		function superConstructorApply (args) {
			return this._super.constructor.apply(this, args);
		}

		function superInvoke (method) {
			var args = Array.fromArguments(arguments).slice(1);
			return this.superApply(method, args);
		}

		function superApply (method, args) {
			var superMethod = this._super[method];
			if (type.is("function", superMethod)) {
				return this._super[method].apply(this, args);
			}
		}

		function invokeMethod (method) {
			var args = Array.fromArguments(arguments).slice(1);
			return this[method].apply(this, args);
		}

		function invokeFunction (fn) {
			var args = Array.fromArguments(arguments).slice(1);
			return fn.apply(this, args);
		}

		function proxyMethod (method) {
			var args = Array.fromArguments(arguments).slice(1),
				fn = this[method];
			args.splice(0, 1, this);
			return fn.bind.apply(fn, args);
		}

		function proxyFunction (fn) {
			var args = Array.fromArguments(arguments);
			args.splice(0, 1, this);
			return fn.bind.apply(fn, args);
		}

		return Base;
	}
);<commit_msg>missing (<commit_after>define(
	[
		"./meta",
		"./class",
		"./type",
		"./bind",
		"./array"
	],
	function (meta, Class, type) {
		var Base = Class(
			meta({
				"entity": "class",
				"name": "Base",
				"description": "Abstract class."
			})
		);

		Base.method(
			meta({
				"name": "superConstructor",
				"description": "Calls this._super.constructor with this as the context.",
				"arguments": [{
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": superConstructor
			})
		);

		Base.method(
			meta({
				"name": "superConstructorApply",
				"description": "Applies this._super.constructor with this as the context.",
				"signature": "arguments|array",
				"arguments": [{
					"type": "arguments|array",
					"name": "args",
					"required": false
				}],
				"implementation": superConstructorApply
			})
		);

		Base.method(
			meta({
				"name": "superInvoke",
				"description": "Invokes a super method with this as the context.",
				"arguments": [{
					"type": "string",
					"name": "method",
					"required": true
				}, {
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": superInvoke,
				"returns": "any"
			})
		);

		Base.method(
			meta({
				"name": "superApply",
				"description": "Invokes a super method with this as the context; applying args.",
				"arguments": [{
					"type": "arguments|array",
					"name": "args",
					"required": false
				}],
				"implementation": superApply,
				"returns": "any"
			})
		);

		Base.method(
			meta({
				"name": "invoke",
				"description": "Useful for invoking local functions as private methods.",
				"arguments": [{
					"type": "function",
					"name": "fn"
				}, {
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": invokeFunction,
				"returns": "any"
			})
		);

		Base.method(
			meta({
				"name": "invoke",
				"description": "Included for consistency with the proxy method api.",
				"arguments": [{
					"type": "string",
					"name": "method"
				}, {
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": invokeMethod,
				"returns": "any"
			})
		);

		Base.method(
			meta({
				"name": "proxy",
				"description": "Creates a function bound to this; useful for attaching event listeners.",
				"arguments": [{
					"type": "function",
					"name": "fn",
					"description": "Any function in scope. Doesn't need to be a public method."
				}, {
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": proxyFunction,
				"returns": "function"
			})
		);

		Base.method(
			meta({
				"name": "proxy",
				"description": "Creates a function bound to this; useful for attaching event listeners.",
				"arguments": [{
					"type": "string",
					"name": "method",
					"description": "The name of the method to bind."
				}, {
					"type": "any",
					"required": false,
					"repeating": true
				}],
				"implementation": proxyMethod,
				"returns": "function"
			})
		);

		Base.prototype._super = {};

		function superConstructor () {
			return this._super.constructor.apply(this, arguments);
		}

		function superConstructorApply (args) {
			return this._super.constructor.apply(this, args);
		}

		function superInvoke (method) {
			var args = Array.fromArguments(arguments).slice(1);
			return this.superApply(method, args);
		}

		function superApply (method, args) {
			var superMethod = this._super[method];
			if (type.is("function", superMethod)) {
				return this._super[method].apply(this, args);
			}
		}

		function invokeMethod (method) {
			var args = Array.fromArguments(arguments).slice(1);
			return this[method].apply(this, args);
		}

		function invokeFunction (fn) {
			var args = Array.fromArguments(arguments).slice(1);
			return fn.apply(this, args);
		}

		function proxyMethod (method) {
			var args = Array.fromArguments(arguments).slice(1),
				fn = this[method];
			args.splice(0, 1, this);
			return fn.bind.apply(fn, args);
		}

		function proxyFunction (fn) {
			var args = Array.fromArguments(arguments);
			args.splice(0, 1, this);
			return fn.bind.apply(fn, args);
		}

		return Base;
	}
);<|endoftext|>


<commit_before>function initOpenLayers() {
  OpenLayers.ImgPath = "/images/OpenLayers/"

  var map = new OpenLayers.Map("map_canvas", {
    projection: "EPSG:900913",
    controls: [],
    theme: null
  });

  map.addControl(new OpenLayers.Control.PanZoomBar());
  map.addControl(new OpenLayers.Control.Navigation());
  map.addControl(new OpenLayers.Control.KeyboardDefaults());
  map.addControl(new OpenLayers.Control.Attribution());
  map.addControl(new OpenLayers.Control.ScaleLine({geodesic: true}));

  var osmLayer = new OpenLayers.Layer.OSM("OpenStreetMap");
  osmLayer.addOptions({
    transitionEffect: "resize",
    numZoomLevels: 18
  });

  map.addLayer(osmLayer);

  var airspace = new OpenLayers.Layer.XYZ("Airspace",
    "http://www.prosoar.de/airspace/${z}/${x}/${y}.png", {
    isBaseLayer: false,
    transparent: true,
    'visibility': true,
    'displayInLayerSwitcher': true
  });
  map.addLayer(airspace);

  var flightPathLayer = new OpenLayers.Layer.Vector("Flight", {
    styleMap: new OpenLayers.StyleMap({
      'default': {
        strokeColor: "${color}",
        strokeWidth: 2
      },
      'plane': {
        // Set the external graphic and background graphic images.
        externalGraphic: "/images/glider_symbol.png",
        // Makes sure the background graphic is placed correctly relative
        // to the external graphic.
        graphicXOffset: -(40/2),
        graphicYOffset: -8,
        graphicWidth: 40,
        graphicHeight: 24,
        rotation: "${rotation}",
        // Set the z-indexes of both graphics to make sure the background
        // graphics stay in the background (shadows on top of markers looks
        // odd; let's not do that).
        graphicZIndex: 2000
      }
    })
  });

  map.addLayer(flightPathLayer);
//  map.events.register("moveend", flightPathLayer, flightPathLayer.redraw);

  map.setCenter(new OpenLayers.LonLat(30, 0).
    transform(new OpenLayers.Projection("EPSG:4326"),map.getProjectionObject() ),
    9);

  var initRedrawLayer = function(layer) {
    if (this.timer) return;
    this.timer = window.setTimeout(function() { this.timer = null; layer.redraw(); }, 200);
  };
  map.events.register("move", this, function() { initRedrawLayer(flightPathLayer); });

  return map;
};

//+ Carlos R. L. Rodrigues
//@ http://jsfromhell.com/array/nearest-number [rev. #0]
function getNearestNumber(a, n){
  if((l = a.length) < 2)
    return l - 1;
  for(var l, p = Math.abs(a[--l] - n); l--;)
    if(p < (p = Math.abs(a[l] - n)))
      break;
  return l + 1;
}

// add leading zeros to a number
function pad(num, size) {
  var s = "000000000" + num;
  return s.substr(s.length-size);
}

function formatSecondsAsTime(seconds) {
  seconds %= 86400;
  var h = Math.floor(seconds/3600);
  var m = Math.floor((seconds%3600)/60);
  var s = Math.floor(seconds%3600%60);

  return pad(h,2) + ":" + pad(m,2) + ":" + pad(s,2); // Format the result into time strings
}

function render_barogram(element) {
  var barogram = Raphael("barogram", "100%", "100%");

  var linechart = barogram.linechart(30, 0,
                      element.innerWidth() - 50, element.innerHeight() - 10,
                      barogram_t,
                      [barogram_h],
                      { axis: "0 0 1 1",
                        axisxstep: 8,
                        axisxfunc: formatSecondsAsTime });

  var position = barogram.set().hide();

  var hoverLine = barogram.path().attr({
    stroke: '#c44',
    'stroke-width': 2
  }).hide();

  var text = barogram.text().hide();

  var textRect = barogram.rect().attr({
    fill: '#fff',
    opacity: .8,
    stroke: '#999',
    r: 3
  }).hide();

  var hoverCircle = barogram.circle().attr({
    stroke: '#c44',
    'stroke-width': 2,
    r: 6
  }).hide();

  position.push(hoverLine);
  position.push(hoverCircle);
  position.push(textRect.insertAfter(hoverCircle));
  position.push(text.insertAfter(textRect));

  var mouse_container = $("

");
  mouse_container.css({
    width: element.innerWidth(),
    height: element.innerHeight(),
    'top': 0,
    'left': 0,
    position: 'absolute',
    opacity: 0,
    'background-color': '#fff'
  });
  element.append(mouse_container);

  mouse_container.mousemove(function(e) {
    hidePlanePosition();
    var prop = linechart.getProperties();
    var kx_inv = 1 / prop.kx;
    var rel_x = e.clientX - mouse_container.offset().left;
    var x = prop.minx + (rel_x - prop.x - prop.gutter) * kx_inv;
    if (x < prop.minx || x > prop.maxx) return;

    var baro_index = getNearestNumber(barogram_t, x);
    var rel_y = prop.y - (barogram_h[baro_index] - prop.miny) * prop.ky + prop.height - prop.gutter;

    hoverColumn(baro_index, rel_x, rel_y);

    if (x < barogram_t[baro_index]) {
      var dx = (x - barogram_t[baro_index-1])/(barogram_t[baro_index] - barogram_t[baro_index-1]);
      showPlanePosition(baro_index - 1, dx);
    } else {
      var dx = (x - barogram_t[baro_index])/(barogram_t[baro_index+1] - barogram_t[baro_index]);
      showPlanePosition(baro_index, dx);
    }
  });

  mouse_container.mouseout(function(e) {
    hidePlanePosition();
    position && position.hide();
  });

  hoverColumn = function(index, x, y) {
    position.toFront();
    var attrs = linechart.getProperties();

    hoverLine.attr({ path:
      "M " + x.toString() + " " + attrs.height + " " +
      "l 0 " + (-(attrs.height - y) + 6).toString() +
      "M " + x.toString() + " 0 " +
      "l 0 " + (y - 6).toString()
    });

    var vario = null;
    if (barogram_t[index+1] != undefined) {
      vario = (barogram_h[index+1] - barogram_h[index]) / (barogram_t[index+1] - barogram_t[index]);
    }

    var speed = null;
    if (barogram_t[index+1] != undefined) {
      speed = OpenLayers.Util.distVincenty(lonlat[index+1], lonlat[index]);
      speed *= 1000;
      speed /= (barogram_t[index+1] - barogram_t[index]);
    }

    text.attr({
      x: x,
      y: attrs.height - 20,
      text: Math.round(barogram_h[index]) + " m" +
            ((vario !== null)?"\n" + (Math.round(vario*10)/10) + " m/s":"") +
            ((speed !== null)?"\n" + (Math.round(speed*3.6*10)/10) + " km/h":"") +
            "\n" + formatSecondsAsTime(barogram_t[index])
    });

    hoverCircle.attr({
      cx: x,
      cy: y,
    });

    var textBBox = text.getBBox();
    textRect.attr({
      x: textBBox.x - 2,
      y: textBBox.y - 2,
      width: textBBox.width + 4,
      height: textBBox.height + 4
    });

    position.show();

    this.index = getNearestNumber(barogram_t, this.axis);
  };

  return linechart;
}

function showPlanePosition(id, dx) {
  var rotation = 0;
  if (lonlat[id+1] != 'undefined') {
    rotation = Math.atan2(lonlat[id+1].lon-lonlat[id].lon, lonlat[id+1].lat-lonlat[id].lat) * 180/Math.PI;
  } else if (lonlat[id-1] != 'undefined') {
    rotation = Math.atan2(lonlat[id].lon-lonlat[id-1].lon, lonlat[id].lat-lonlat[id-1].lat) * 180/Math.PI;
  }

  var lon = lonlat[id].lon + (lonlat[id+1].lon - lonlat[id].lon)*dx;
  var lat = lonlat[id].lat + (lonlat[id+1].lat - lonlat[id].lat)*dx;

  plane.attributes.rotation = rotation;
  plane.geometry = new OpenLayers.Geometry.Point(lon, lat).
    transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
  map.getLayersByName("Flight")[0].drawFeature(plane);
}

function hidePlanePosition(id) {
  map.getLayersByName("Flight")[0].removeFeatures(plane);
}

function scaleBarogram(linechart, flight, element) {

  var updateBarogram = function(e) {
    var largest_partition = null;
    var delta_t = 0;
    var first = 0;
    var last = barogram_t.length - 1;

    // only show longest single part of trace in barogram
    for (part_geo in flight.partitionedGeometries) {
      var components = flight.partitionedGeometries[part_geo].components;
      var temp = barogram_t[components[components.length-1].originalIndex] -
        barogram_t[components[0].originalIndex];

      if (temp > delta_t) {
        delta_t = temp;
        largest_partition = part_geo;
        first = components[0].originalIndex;
        last = components[components.length-1].originalIndex;
      }
    }

/*
    // show barogram of all trace parts visible
    var length = flight.partitionedGeometries.length;
    var comp_length = flight.partitionedGeometries[length-1].components.length;
    first = flight.partitionedGeometries[0].components[0].originalIndex;
    last = flight.partitionedGeometries[length-1].components[comp_length-1].originalIndex;
*/
    setTimeout(function() { linechart.zoomInto(first, last)}, 0);
  };

  map.events.register("moveend", this, updateBarogram);
}

<commit_msg>public.js.flight: remove obsolte comma<commit_after>function initOpenLayers() {
  OpenLayers.ImgPath = "/images/OpenLayers/"

  var map = new OpenLayers.Map("map_canvas", {
    projection: "EPSG:900913",
    controls: [],
    theme: null
  });

  map.addControl(new OpenLayers.Control.PanZoomBar());
  map.addControl(new OpenLayers.Control.Navigation());
  map.addControl(new OpenLayers.Control.KeyboardDefaults());
  map.addControl(new OpenLayers.Control.Attribution());
  map.addControl(new OpenLayers.Control.ScaleLine({geodesic: true}));

  var osmLayer = new OpenLayers.Layer.OSM("OpenStreetMap");
  osmLayer.addOptions({
    transitionEffect: "resize",
    numZoomLevels: 18
  });

  map.addLayer(osmLayer);

  var airspace = new OpenLayers.Layer.XYZ("Airspace",
    "http://www.prosoar.de/airspace/${z}/${x}/${y}.png", {
    isBaseLayer: false,
    transparent: true,
    'visibility': true,
    'displayInLayerSwitcher': true
  });
  map.addLayer(airspace);

  var flightPathLayer = new OpenLayers.Layer.Vector("Flight", {
    styleMap: new OpenLayers.StyleMap({
      'default': {
        strokeColor: "${color}",
        strokeWidth: 2
      },
      'plane': {
        // Set the external graphic and background graphic images.
        externalGraphic: "/images/glider_symbol.png",
        // Makes sure the background graphic is placed correctly relative
        // to the external graphic.
        graphicXOffset: -(40/2),
        graphicYOffset: -8,
        graphicWidth: 40,
        graphicHeight: 24,
        rotation: "${rotation}",
        // Set the z-indexes of both graphics to make sure the background
        // graphics stay in the background (shadows on top of markers looks
        // odd; let's not do that).
        graphicZIndex: 2000
      }
    })
  });

  map.addLayer(flightPathLayer);
//  map.events.register("moveend", flightPathLayer, flightPathLayer.redraw);

  map.setCenter(new OpenLayers.LonLat(30, 0).
    transform(new OpenLayers.Projection("EPSG:4326"),map.getProjectionObject() ),
    9);

  var initRedrawLayer = function(layer) {
    if (this.timer) return;
    this.timer = window.setTimeout(function() { this.timer = null; layer.redraw(); }, 200);
  };
  map.events.register("move", this, function() { initRedrawLayer(flightPathLayer); });

  return map;
};

//+ Carlos R. L. Rodrigues
//@ http://jsfromhell.com/array/nearest-number [rev. #0]
function getNearestNumber(a, n){
  if((l = a.length) < 2)
    return l - 1;
  for(var l, p = Math.abs(a[--l] - n); l--;)
    if(p < (p = Math.abs(a[l] - n)))
      break;
  return l + 1;
}

// add leading zeros to a number
function pad(num, size) {
  var s = "000000000" + num;
  return s.substr(s.length-size);
}

function formatSecondsAsTime(seconds) {
  seconds %= 86400;
  var h = Math.floor(seconds/3600);
  var m = Math.floor((seconds%3600)/60);
  var s = Math.floor(seconds%3600%60);

  return pad(h,2) + ":" + pad(m,2) + ":" + pad(s,2); // Format the result into time strings
}

function render_barogram(element) {
  var barogram = Raphael("barogram", "100%", "100%");

  var linechart = barogram.linechart(30, 0,
                      element.innerWidth() - 50, element.innerHeight() - 10,
                      barogram_t,
                      [barogram_h],
                      { axis: "0 0 1 1",
                        axisxstep: 8,
                        axisxfunc: formatSecondsAsTime });

  var position = barogram.set().hide();

  var hoverLine = barogram.path().attr({
    stroke: '#c44',
    'stroke-width': 2
  }).hide();

  var text = barogram.text().hide();

  var textRect = barogram.rect().attr({
    fill: '#fff',
    opacity: .8,
    stroke: '#999',
    r: 3
  }).hide();

  var hoverCircle = barogram.circle().attr({
    stroke: '#c44',
    'stroke-width': 2,
    r: 6
  }).hide();

  position.push(hoverLine);
  position.push(hoverCircle);
  position.push(textRect.insertAfter(hoverCircle));
  position.push(text.insertAfter(textRect));

  var mouse_container = $("

");
  mouse_container.css({
    width: element.innerWidth(),
    height: element.innerHeight(),
    'top': 0,
    'left': 0,
    position: 'absolute',
    opacity: 0,
    'background-color': '#fff'
  });
  element.append(mouse_container);

  mouse_container.mousemove(function(e) {
    hidePlanePosition();
    var prop = linechart.getProperties();
    var kx_inv = 1 / prop.kx;
    var rel_x = e.clientX - mouse_container.offset().left;
    var x = prop.minx + (rel_x - prop.x - prop.gutter) * kx_inv;
    if (x < prop.minx || x > prop.maxx) return;

    var baro_index = getNearestNumber(barogram_t, x);
    var rel_y = prop.y - (barogram_h[baro_index] - prop.miny) * prop.ky + prop.height - prop.gutter;

    hoverColumn(baro_index, rel_x, rel_y);

    if (x < barogram_t[baro_index]) {
      var dx = (x - barogram_t[baro_index-1])/(barogram_t[baro_index] - barogram_t[baro_index-1]);
      showPlanePosition(baro_index - 1, dx);
    } else {
      var dx = (x - barogram_t[baro_index])/(barogram_t[baro_index+1] - barogram_t[baro_index]);
      showPlanePosition(baro_index, dx);
    }
  });

  mouse_container.mouseout(function(e) {
    hidePlanePosition();
    position && position.hide();
  });

  hoverColumn = function(index, x, y) {
    position.toFront();
    var attrs = linechart.getProperties();

    hoverLine.attr({ path:
      "M " + x.toString() + " " + attrs.height + " " +
      "l 0 " + (-(attrs.height - y) + 6).toString() +
      "M " + x.toString() + " 0 " +
      "l 0 " + (y - 6).toString()
    });

    var vario = null;
    if (barogram_t[index+1] != undefined) {
      vario = (barogram_h[index+1] - barogram_h[index]) / (barogram_t[index+1] - barogram_t[index]);
    }

    var speed = null;
    if (barogram_t[index+1] != undefined) {
      speed = OpenLayers.Util.distVincenty(lonlat[index+1], lonlat[index]);
      speed *= 1000;
      speed /= (barogram_t[index+1] - barogram_t[index]);
    }

    text.attr({
      x: x,
      y: attrs.height - 20,
      text: Math.round(barogram_h[index]) + " m" +
            ((vario !== null)?"\n" + (Math.round(vario*10)/10) + " m/s":"") +
            ((speed !== null)?"\n" + (Math.round(speed*3.6*10)/10) + " km/h":"") +
            "\n" + formatSecondsAsTime(barogram_t[index])
    });

    hoverCircle.attr({
      cx: x,
      cy: y
    });

    var textBBox = text.getBBox();
    textRect.attr({
      x: textBBox.x - 2,
      y: textBBox.y - 2,
      width: textBBox.width + 4,
      height: textBBox.height + 4
    });

    position.show();

    this.index = getNearestNumber(barogram_t, this.axis);
  };

  return linechart;
}

function showPlanePosition(id, dx) {
  var rotation = 0;
  if (lonlat[id+1] != 'undefined') {
    rotation = Math.atan2(lonlat[id+1].lon-lonlat[id].lon, lonlat[id+1].lat-lonlat[id].lat) * 180/Math.PI;
  } else if (lonlat[id-1] != 'undefined') {
    rotation = Math.atan2(lonlat[id].lon-lonlat[id-1].lon, lonlat[id].lat-lonlat[id-1].lat) * 180/Math.PI;
  }

  var lon = lonlat[id].lon + (lonlat[id+1].lon - lonlat[id].lon)*dx;
  var lat = lonlat[id].lat + (lonlat[id+1].lat - lonlat[id].lat)*dx;

  plane.attributes.rotation = rotation;
  plane.geometry = new OpenLayers.Geometry.Point(lon, lat).
    transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
  map.getLayersByName("Flight")[0].drawFeature(plane);
}

function hidePlanePosition(id) {
  map.getLayersByName("Flight")[0].removeFeatures(plane);
}

function scaleBarogram(linechart, flight, element) {

  var updateBarogram = function(e) {
    var largest_partition = null;
    var delta_t = 0;
    var first = 0;
    var last = barogram_t.length - 1;

    // only show longest single part of trace in barogram
    for (part_geo in flight.partitionedGeometries) {
      var components = flight.partitionedGeometries[part_geo].components;
      var temp = barogram_t[components[components.length-1].originalIndex] -
        barogram_t[components[0].originalIndex];

      if (temp > delta_t) {
        delta_t = temp;
        largest_partition = part_geo;
        first = components[0].originalIndex;
        last = components[components.length-1].originalIndex;
      }
    }

/*
    // show barogram of all trace parts visible
    var length = flight.partitionedGeometries.length;
    var comp_length = flight.partitionedGeometries[length-1].components.length;
    first = flight.partitionedGeometries[0].components[0].originalIndex;
    last = flight.partitionedGeometries[length-1].components[comp_length-1].originalIndex;
*/
    setTimeout(function() { linechart.zoomInto(first, last)}, 0);
  };

  map.events.register("moveend", this, updateBarogram);
}

<|endoftext|>


<commit_before>'use strict';

const assert = require('assert');
const JSON5 = require('json5');

const format = require('./format');

const DEFAULTS = { exportType: 'json', space: 4 };

// different format exports declaration in different fields
// and this specific point is used for detecting input format
// if it isn't passed. This logic performed by detect method
// which called from parse method.
const fieldByFormat = {
    v1: 'blocks',
    enb: 'deps',
    v2: ''
};

const generators = {
    json5: (obj, space) => JSON5.stringify(obj, null, space),
    json: (obj, space) => JSON.stringify(obj, null, space),
    commonjs: (obj, space) => `module.exports = ${JSON5.stringify(obj, null, space)};\n`,
    es2015: (obj, space) => `export default ${JSON5.stringify(obj, null, space)};\n`
};
// Aliases
generators.es6 = generators.es2015;
generators.cjs = generators.commonjs;

/**
 * Create string representation of declaration
 *
 * @param {BemCell|BemCell[]} decl - source declaration
 * @param {Object} opts - additional options
 * @param {String} opts.format - format of declaration (v1, v2, enb)
 * @param {String} [opts.exportType=json5] - defines how to wrap result (commonjs, json5, json, es6|es2015)
 * @param {String|Number} [opts.space] - number of space characters or string to use as a white space
 * @returns {String}
 */
module.exports = function (decl, opts) {
    const options = Object.assign({}, DEFAULTS, opts);

    assert(options.format, 'You must declare target format');
    assert(fieldByFormat.hasOwnProperty(options.format), 'Specified format isn\'t supported');
    assert(generators.hasOwnProperty(options.exportType), 'Specified export type isn\'t supported');

    Array.isArray(decl) || (decl = [decl]);

    const formatedDecl = format(decl, { format: options.format });
    const field = fieldByFormat[options.format];
    let stringifiedObj = { format: options.format };

    if (field) {
        stringifiedObj[field] = formatedDecl;
    } else {
        stringifiedObj = formatedDecl;
    }

    return generators[options.exportType](stringifiedObj, options.space);
};
<commit_msg>revert value for the fieldByFormat.v2 (set the "deps" value again)<commit_after>'use strict';

const assert = require('assert');
const JSON5 = require('json5');

const format = require('./format');

const DEFAULTS = { exportType: 'json', space: 4 };

// different format exports declaration in different fields
// and this specific point is used for detecting input format
// if it isn't passed. This logic performed by detect method
// which called from parse method.
const fieldByFormat = {
    v1: 'blocks',
    enb: 'deps',
    v2: 'deps'
};

const generators = {
    json5: (obj, space) => JSON5.stringify(obj, null, space),
    json: (obj, space) => JSON.stringify(obj, null, space),
    commonjs: (obj, space) => `module.exports = ${JSON5.stringify(obj, null, space)};\n`,
    es2015: (obj, space) => `export default ${JSON5.stringify(obj, null, space)};\n`
};
// Aliases
generators.es6 = generators.es2015;
generators.cjs = generators.commonjs;

/**
 * Create string representation of declaration
 *
 * @param {BemCell|BemCell[]} decl - source declaration
 * @param {Object} opts - additional options
 * @param {String} opts.format - format of declaration (v1, v2, enb)
 * @param {String} [opts.exportType=json5] - defines how to wrap result (commonjs, json5, json, es6|es2015)
 * @param {String|Number} [opts.space] - number of space characters or string to use as a white space
 * @returns {String}
 */
module.exports = function (decl, opts) {
    const options = Object.assign({}, DEFAULTS, opts);

    assert(options.format, 'You must declare target format');
    assert(fieldByFormat.hasOwnProperty(options.format), 'Specified format isn\'t supported');
    assert(generators.hasOwnProperty(options.exportType), 'Specified export type isn\'t supported');

    Array.isArray(decl) || (decl = [decl]);

    const formatedDecl = format(decl, { format: options.format });
    const field = fieldByFormat[options.format];
    let stringifiedObj = { format: options.format };

    if (field) {
        stringifiedObj[field] = formatedDecl;
    } else {
        stringifiedObj = formatedDecl;
    }

    return generators[options.exportType](stringifiedObj, options.space);
};
<|endoftext|>


<commit_before>/*
 * Copyright 2019 The Android Open Source Project
 *
 * 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.
 */
import { EventEmitter } from "events";
import { Empty } from "google-protobuf/google/protobuf/empty_pb";
import { ThemeProvider } from "@material-ui/core";
import { kStringMaxLength } from "buffer";
/**
 * This drives the jsep protocol with the emulator, and can be used to
 * send key/mouse/touch events to the emulator. Events will be send
 * over the data channel if open, otherwise they will be send via the
 * grpc endpoint.
 *
 *  The jsep protocol is described here:
 * https://rtcweb-wg.github.io/jsep/.
 *
 *  This class can fire two events:
 *
 * - `connected` when the stream has become available.
 * - `disconnected` when the stream broke down, or when we failed to establish a connection
 *
 * You usually want to start the stream after instantiating this object. Do not forget to
 * disconnect once you are finished to terminate the message pump.
 *
 *
 * @example
 *  jsep = new JsepProtocolDriver(emulator, s => { video.srcObject = s; video.play() });
 *  jsep.startStream();
 *
 * @export
 * @class JsepProtocol
 */
export default class JsepProtocol {
  /**
   * Creates an instance of JsepProtocol.
   * @param {EmulatorService} emulator Service used to make the gRPC calls
   * @param {RtcService} rtc Service used to open up the rtc calls.
   * @param {boolean} poll True if we should use polling
   * @param {callback} onConnect optional callback that is invoked when a stream is available
   * @param {callback} onDisconnect optional callback that is invoked when the stream is closed.
   * @memberof JsepProtocol
   */
  constructor(emulator, rtc, poll, onConnect, onDisconnect) {
    this.emulator = emulator;
    this.rtc = rtc;
    this.events = new EventEmitter();

    // Workaround for older emulators that send messages out of order
    // and do not handle all answers properly.
    this.old_emu_patch = {
      candidates: [],
      sdp: null,
      haveOffer: false,
      answer: false,
    };

    this.poll = poll;
    this.guid = null;
    this.stream = null;
    this.event_forwarders = {};
    if (onConnect) this.events.on("connected", onConnect);
    if (onDisconnect) this.events.on("disconnected", onDisconnect);
  }

  on = (name, fn) => {
    this.events.on(name, fn);
  };

  /**
   * Disconnects the stream. This will stop the message pump as well.
   *
   * @memberof JsepProtocol
   */
  disconnect = () => {
    this.connected = false;
    if (this.peerConnection) this.peerConnection.close();
    if (this.stream) {
      this.stream.cancel();
      this.stream = null;
    }
    this.active = false;
    this.old_emu_patch = {
      candidates: [],
      sdp: null,
      haveOffer: false,
      answer: false,
    };
    this.events.emit("disconnected", this);
  };

  /**
   * Initiates the JSEP protocol.
   *
   * @memberof JsepProtocol
   */
  startStream = () => {
    const self = this;
    this.connected = false;
    this.peerConnection = null;
    this.active = true;
    this.old_emu_patch = {
      candidates: [],
      sdp: null,
      haveOffer: false,
      answer: false,
    };
    var request = new Empty();
    this.rtc.requestRtcStream(request, {}, (err, response) => {
      if (err) {
        console.error("Failed to configure rtc stream: " + JSON.stringify(err));
        this.disconnect();
        return;
      }

      // Configure
      self.guid = response;
      self.connected = true;

      if (!this.poll) {
        // Streaming envoy based.
        self._streamJsepMessage();
      } else {
        // Poll pump messages, go/envoy based proxy.
        console.debug("Polling jsep messages.");
        self._receiveJsepMessage();
      }
    });
  };

  cleanup = () => {
    this.disconnect();
    if (this.peerConnection) {
      this.peerConnection.removeEventListener(
        "track",
        this._handlePeerConnectionTrack
      );
      this.peerConnection.removeEventListener(
        "icecandidate",
        this._handlePeerIceCandidate
      );
      this.peerConnection = null;
    }
    this.event_forwarders = {};
  };

  _handlePeerConnectionTrack = (e) => {
    this.events.emit("connected", e.track);
  };

  _handlePeerConnectionStateChange = (e) => {
    switch (this.peerConnection.connectionState) {
      case "disconnected":
      // At least one of the ICE transports for the connection is in the "disconnected" state
      // and none of the other transports are in the state "failed", "connecting",
      // or "checking".
      case "failed":
      // One or more of the ICE transports on the connection is in the "failed" state.
      case "closed":
        //The RTCPeerConnection is closed.
        this.disconnect();
    }
  };

  send(label, msg) {
    let bytes = msg.serializeBinary();
    let forwarder = this.event_forwarders[label];
    console.log("Send " + label + " " + JSON.stringify(msg.toObject()));
    // Send via data channel/gRPC bridge.
    if (this.connected && forwarder && forwarder.readyState == "open") {
      this.event_forwarders[label].send(bytes);
    } else {
      // Fallback to using the gRPC protocol
      switch (label) {
        case "mouse":
          this.emulator.sendMouse(msg);
          break;
        case "keyboard":
          this.emulator.sendKey(msg);
          break;
        case "touch":
          this.emulator.sendTouch(msg);
          break;
      }
    }
  }

  _handlePeerIceCandidate = (e) => {
    if (e.candidate === null) return;
    this._sendJsep({ candidate: e.candidate });
  };

  _handleDataChannel = (e) => {
    let channel = e.channel;
    this.event_forwarders[channel.label] = channel;
  };

  _handleStart = (signal) => {
    this.peerConnection = new RTCPeerConnection(signal.start);
    this.peerConnection.ontrack = this._handlePeerConnectionTrack;
    this.peerConnection.onicecandidate = this._handlePeerIceCandidate;
    this.peerConnection.onconnectionstatechange = this._handlePeerConnectionStateChange;
    this.peerConnection.ondatachannel = this._handleDataChannel;
    this.peerConnection.onsignalingstatechange = this._handlePeerState;
  };

  _handlePeerState = (event) => {
    if (!this.peerConnection) {
      console.log("Peerconnection no longer available, ignoring signal state.");
    }
    switch (this.peerConnection.signalingState) {
      case "have-remote-offer":
        this.old_emu_patch.haveOffer = true;
        while (this.old_emu_patch.candidates.length > 0) {
          this._handleCandidate(this.old_emu_patch.candidates.shift());
        }
        break;
    }
  };

  _handleSDP = async (signal) => {
    // We should not call this more than once..
    this.old_emu_patch.sdp = null;
    this.peerConnection.setRemoteDescription(new RTCSessionDescription(signal));
    const answer = await this.peerConnection.createAnswer();
    if (answer) {
      // Older emulators cannot handle multiple answers, so make sure we do not send one
      // again.
      if (!this.old_emu_patch.answer) {
        this.old_emu_patch.answer = true;
        this.peerConnection.setLocalDescription(answer);
        this._sendJsep({ sdp: answer });
      }
    } else {
      this.disconnect();
    }
  };

  _handleCandidate = (signal) => {
    this.peerConnection.addIceCandidate(new RTCIceCandidate(signal));
  };

  _handleSignal = (signal) => {
    if (signal.start) {
      this._handleStart(signal);
    }
    if (signal.bye) {
      this._handleBye();
    }
    if (signal.sdp && !this.old_emu_patch.sdp) {
      this.old_emu_patch.sdp = signal;
    }
    if (signal.candidate) {
      this.old_emu_patch.candidates.push(signal);
    }

    if (!!this.peerConnection) {
      // We have created a peer connection..
      if (this.old_emu_patch.sdp) {
        this._handleSDP(this.old_emu_patch.sdp);
      }

      if (this.haveOffer) {
        // We have a remote peer, add the candidates in.
        while (this.old_emu_patch.candidates.length > 0) {
          this._handleCandidate(this.old_emu_patch.candidates.shift());
        }
      }
    }
  };

  _handleJsepMessage = (message) => {
    try {
      const signal = JSON.parse(message);
      this._handleSignal(signal);
    } catch (e) {
      console.error(
        "Failed to handle message: [" +
          message +
          "], due to: " +
          JSON.stringify(e)
      );
    }
  };

  _handleBye = () => {
    if (this.connected) {
      this.disconnect();
    }
  };

  _sendJsep = (jsonObject) => {
    /* eslint-disable */
    var request = new proto.android.emulation.control.JsepMsg();
    request.setId(this.guid);
    request.setMessage(JSON.stringify(jsonObject));
    this.rtc.sendJsepMessage(request);
  };

  _streamJsepMessage = () => {
    if (!this.connected) return;
    var self = this;

    this.stream = this.rtc.receiveJsepMessages(this.guid, {});
    this.stream.on("data", (response) => {
      const msg = response.getMessage();
      self._handleJsepMessage(msg);
    });
    this.stream.on("error", (e) => {
      console.log("Jsep message stream error:", e);
      if (self.connected) {
        console.log("Attempting to reconnect to jsep message stream.");
        self._streamJsepMessage();
      }
    });
    this.stream.on("end", (e) => {
      if (self.connected) {
        console.log("Stream end while still connected.. Reconnecting");
        self._streamJsepMessage();
      }
    });
  };

  // This function is a fallback for v1 (go based proxy), that does not support streaming.
  _receiveJsepMessage = () => {
    if (!this.connected) return;

    var self = this;

    // This is a blocking call, that will return as soon as a series
    // of messages have been made available, or if we reach a timeout
    this.rtc.receiveJsepMessage(this.guid, {}, (err, response) => {
      if (err) {
        console.error(
          "Failed to receive jsep message, disconnecting: " +
            JSON.stringify(err)
        );
        this.disconnect();
      }
      const msg = response.getMessage();
      // Handle only if we received a useful message.
      // it is possible to get nothing if the server decides
      // to kick us out.
      if (msg) {
        self._handleJsepMessage(response.getMessage());
      }

      // And pump messages. Note we must continue the message pump as we
      // can receive new ICE candidates at any point in time.
      if (self.active) {
        self._receiveJsepMessage();
      }
    });
  };
}
<commit_msg>Fix missing remote candidates<commit_after>/*
 * Copyright 2019 The Android Open Source Project
 *
 * 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.
 */
import { EventEmitter } from "events";
import { Empty } from "google-protobuf/google/protobuf/empty_pb";
import { ThemeProvider } from "@material-ui/core";
import { kStringMaxLength } from "buffer";
/**
 * This drives the jsep protocol with the emulator, and can be used to
 * send key/mouse/touch events to the emulator. Events will be send
 * over the data channel if open, otherwise they will be send via the
 * grpc endpoint.
 *
 *  The jsep protocol is described here:
 * https://rtcweb-wg.github.io/jsep/.
 *
 *  This class can fire two events:
 *
 * - `connected` when the stream has become available.
 * - `disconnected` when the stream broke down, or when we failed to establish a connection
 *
 * You usually want to start the stream after instantiating this object. Do not forget to
 * disconnect once you are finished to terminate the message pump.
 *
 *
 * @example
 *  jsep = new JsepProtocolDriver(emulator, s => { video.srcObject = s; video.play() });
 *  jsep.startStream();
 *
 * @export
 * @class JsepProtocol
 */
export default class JsepProtocol {
  /**
   * Creates an instance of JsepProtocol.
   * @param {EmulatorService} emulator Service used to make the gRPC calls
   * @param {RtcService} rtc Service used to open up the rtc calls.
   * @param {boolean} poll True if we should use polling
   * @param {callback} onConnect optional callback that is invoked when a stream is available
   * @param {callback} onDisconnect optional callback that is invoked when the stream is closed.
   * @memberof JsepProtocol
   */
  constructor(emulator, rtc, poll, onConnect, onDisconnect) {
    this.emulator = emulator;
    this.rtc = rtc;
    this.events = new EventEmitter();

    // Workaround for older emulators that send messages out of order
    // and do not handle all answers properly.
    this.old_emu_patch = {
      candidates: [],
      sdp: null,
      haveOffer: false,
      answer: false,
    };

    this.poll = poll;
    this.guid = null;
    this.stream = null;
    this.event_forwarders = {};
    if (onConnect) this.events.on("connected", onConnect);
    if (onDisconnect) this.events.on("disconnected", onDisconnect);
  }

  on = (name, fn) => {
    this.events.on(name, fn);
  };

  /**
   * Disconnects the stream. This will stop the message pump as well.
   *
   * @memberof JsepProtocol
   */
  disconnect = () => {
    this.connected = false;
    if (this.peerConnection) this.peerConnection.close();
    if (this.stream) {
      this.stream.cancel();
      this.stream = null;
    }
    this.active = false;
    this.old_emu_patch = {
      candidates: [],
      sdp: null,
      haveOffer: false,
      answer: false,
    };
    this.events.emit("disconnected", this);
  };

  /**
   * Initiates the JSEP protocol.
   *
   * @memberof JsepProtocol
   */
  startStream = () => {
    const self = this;
    this.connected = false;
    this.peerConnection = null;
    this.active = true;
    this.old_emu_patch = {
      candidates: [],
      sdp: null,
      haveOffer: false,
      answer: false,
    };
    var request = new Empty();
    this.rtc.requestRtcStream(request, {}, (err, response) => {
      if (err) {
        console.error("Failed to configure rtc stream: " + JSON.stringify(err));
        this.disconnect();
        return;
      }

      // Configure
      self.guid = response;
      self.connected = true;

      if (!this.poll) {
        // Streaming envoy based.
        self._streamJsepMessage();
      } else {
        // Poll pump messages, go/envoy based proxy.
        console.debug("Polling jsep messages.");
        self._receiveJsepMessage();
      }
    });
  };

  cleanup = () => {
    this.disconnect();
    if (this.peerConnection) {
      this.peerConnection.removeEventListener(
        "track",
        this._handlePeerConnectionTrack
      );
      this.peerConnection.removeEventListener(
        "icecandidate",
        this._handlePeerIceCandidate
      );
      this.peerConnection = null;
    }
    this.event_forwarders = {};
  };

  _handlePeerConnectionTrack = (e) => {
    this.events.emit("connected", e.track);
  };

  _handlePeerConnectionStateChange = (e) => {
    switch (this.peerConnection.connectionState) {
      case "disconnected":
      // At least one of the ICE transports for the connection is in the "disconnected" state
      // and none of the other transports are in the state "failed", "connecting",
      // or "checking".
      case "failed":
      // One or more of the ICE transports on the connection is in the "failed" state.
      case "closed":
        //The RTCPeerConnection is closed.
        this.disconnect();
    }
  };

  send(label, msg) {
    let bytes = msg.serializeBinary();
    let forwarder = this.event_forwarders[label];
    console.log("Send " + label + " " + JSON.stringify(msg.toObject()));
    // Send via data channel/gRPC bridge.
    if (this.connected && forwarder && forwarder.readyState == "open") {
      this.event_forwarders[label].send(bytes);
    } else {
      // Fallback to using the gRPC protocol
      switch (label) {
        case "mouse":
          this.emulator.sendMouse(msg);
          break;
        case "keyboard":
          this.emulator.sendKey(msg);
          break;
        case "touch":
          this.emulator.sendTouch(msg);
          break;
      }
    }
  }

  _handlePeerIceCandidate = (e) => {
    if (e.candidate === null) return;
    this._sendJsep({ candidate: e.candidate });
  };

  _handleDataChannel = (e) => {
    let channel = e.channel;
    this.event_forwarders[channel.label] = channel;
  };

  _handleStart = (signal) => {
    this.peerConnection = new RTCPeerConnection(signal.start);
    this.peerConnection.ontrack = this._handlePeerConnectionTrack;
    this.peerConnection.onicecandidate = this._handlePeerIceCandidate;
    this.peerConnection.onconnectionstatechange = this._handlePeerConnectionStateChange;
    this.peerConnection.ondatachannel = this._handleDataChannel;
    this.peerConnection.onsignalingstatechange = this._handlePeerState;
  };

  _handlePeerState = (event) => {
    if (!this.peerConnection) {
      console.log("Peerconnection no longer available, ignoring signal state.");
    }
    switch (this.peerConnection.signalingState) {
      case "have-remote-offer":
        this.old_emu_patch.haveOffer = true;
        while (this.old_emu_patch.candidates.length > 0) {
          this._handleCandidate(this.old_emu_patch.candidates.shift());
        }
        break;
    }
  };

  _handleSDP = async (signal) => {
    // We should not call this more than once..
    this.old_emu_patch.sdp = null;
    this.peerConnection.setRemoteDescription(new RTCSessionDescription(signal));
    const answer = await this.peerConnection.createAnswer();
    if (answer) {
      // Older emulators cannot handle multiple answers, so make sure we do not send one
      // again.
      if (!this.old_emu_patch.answer) {
        this.old_emu_patch.answer = true;
        this.peerConnection.setLocalDescription(answer);
        this._sendJsep({ sdp: answer });
      }
    } else {
      this.disconnect();
    }
  };

  _handleCandidate = (signal) => {
    this.peerConnection.addIceCandidate(new RTCIceCandidate(signal));
  };

  _handleSignal = (signal) => {
    if (signal.start) {
      this._handleStart(signal);
    }
    if (signal.bye) {
      this._handleBye();
    }
    if (signal.sdp && !this.old_emu_patch.sdp) {
      this.old_emu_patch.sdp = signal;
    }
    if (signal.candidate) {
      this.old_emu_patch.candidates.push(signal);
    }

    if (!!this.peerConnection) {
      // We have created a peer connection..
      if (this.old_emu_patch.sdp) {
        this._handleSDP(this.old_emu_patch.sdp);
      }

      if (this.old_emu_patch.haveOffer) {
        // We have a remote peer, add the candidates in.
        while (this.old_emu_patch.candidates.length > 0) {
          this._handleCandidate(this.old_emu_patch.candidates.shift());
        }
      }
    }
  };

  _handleJsepMessage = (message) => {
    try {
      const signal = JSON.parse(message);
      this._handleSignal(signal);
    } catch (e) {
      console.error(
        "Failed to handle message: [" +
          message +
          "], due to: " +
          JSON.stringify(e)
      );
    }
  };

  _handleBye = () => {
    if (this.connected) {
      this.disconnect();
    }
  };

  _sendJsep = (jsonObject) => {
    /* eslint-disable */
    var request = new proto.android.emulation.control.JsepMsg();
    request.setId(this.guid);
    request.setMessage(JSON.stringify(jsonObject));
    this.rtc.sendJsepMessage(request);
  };

  _streamJsepMessage = () => {
    if (!this.connected) return;
    var self = this;

    this.stream = this.rtc.receiveJsepMessages(this.guid, {});
    this.stream.on("data", (response) => {
      const msg = response.getMessage();
      self._handleJsepMessage(msg);
    });
    this.stream.on("error", (e) => {
      console.log("Jsep message stream error:", e);
      if (self.connected) {
        console.log("Attempting to reconnect to jsep message stream.");
        self._streamJsepMessage();
      }
    });
    this.stream.on("end", (e) => {
      if (self.connected) {
        console.log("Stream end while still connected.. Reconnecting");
        self._streamJsepMessage();
      }
    });
  };

  // This function is a fallback for v1 (go based proxy), that does not support streaming.
  _receiveJsepMessage = () => {
    if (!this.connected) return;

    var self = this;

    // This is a blocking call, that will return as soon as a series
    // of messages have been made available, or if we reach a timeout
    this.rtc.receiveJsepMessage(this.guid, {}, (err, response) => {
      if (err) {
        console.error(
          "Failed to receive jsep message, disconnecting: " +
            JSON.stringify(err)
        );
        this.disconnect();
      }
      const msg = response.getMessage();
      // Handle only if we received a useful message.
      // it is possible to get nothing if the server decides
      // to kick us out.
      if (msg) {
        self._handleJsepMessage(response.getMessage());
      }

      // And pump messages. Note we must continue the message pump as we
      // can receive new ICE candidates at any point in time.
      if (self.active) {
        self._receiveJsepMessage();
      }
    });
  };
}
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import ReactDOM from 'react-dom'

import { Navbar, Nav, NavItem, NavDropdown, MenuItem, Glyphicon } from 'react-bootstrap'
import { LinkContainer } from 'react-router-bootstrap'

export default class DatatoolsNavbar extends Component {

  static propTypes = {
    title: PropTypes.string,
    username: PropTypes.string,
    managerUrl: PropTypes.string,
    editorUrl: PropTypes.string,
    userAdminUrl: PropTypes.string,
    alertsUrl: PropTypes.string,
    signConfigUrl: PropTypes.string,
    loginHandler: PropTypes.func,
    logoutHandler: PropTypes.func,
    resetPasswordHandler: PropTypes.func
  };

  render () {
    var userControl
    if (!this.props.username) {
      userControl = (<NavItem onClick={this.props.loginHandler} href='#'>Log In</NavItem>)
    } else {
      userControl = (
        <NavDropdown title={
          <Glyphicon glyph='user' /> {this.props.username}
        } id='basic-nav-dropdown'>
          <MenuItem onClick={this.props.resetPasswordHandler}>Reset Password</MenuItem>
          <MenuItem onClick={this.props.logoutHandler}>Log Out</MenuItem>
        </NavDropdown>
      )
    }
    let projectControl
    if (!this.props.username) {
      projectControl = ('')
    } else if (!this.props.projects || !this.props.projects.active) {
      projectControl = (<NavItem href='#'>No project selected</NavItem>)
    } else {
      let activeProject = this.props.projects.active
      projectControl = (
        <NavDropdown
          title={<Glyphicon glyph='briefcase' /> {activeProject.name}}
          id='basic-nav-dropdown'
        >
          {this.props.projects.all.map(proj => {
            return (
              <MenuItem
                onClick={(evt) =>{
                  evt.preventDefault()
                  this.props.setActiveProject(proj)
                }}
              >{proj.name}</MenuItem>
            )
          })}
        </NavDropdown>
      )
    }

    return (
      <Navbar>
        <Navbar.Header>
          <Navbar.Brand>
            {this.props.title}
          </Navbar.Brand>
          <Navbar.Toggle />
        </Navbar.Header>
        <Navbar.Collapse>

            <LinkContainer to={this.props.managerUrl}>
              <NavItem>Manager</NavItem>
            </LinkContainer>
            {this.props.editorUrl
              ? <NavItem href={this.props.editorUrl} active={this.props.editorUrl === '#'}>
                  Editor
                </NavItem>
              : null
            }
            {this.props.alertsUrl
              ? <LinkContainer to={this.props.alertsUrl}>
                  <NavItem>Alerts</NavItem>
                </LinkContainer>
              : null
            }
            {this.props.userAdminUrl
              ? <LinkContainer to={this.props.userAdminUrl}>
                  <NavItem>Users</NavItem>
                </LinkContainer>
              : null
            }
            {this.props.signConfigUrl
              ? <LinkContainer to={this.props.signConfigUrl}>
                  <NavItem>Sign Config</NavItem>
                </LinkContainer>
              : null
            }

            {projectControl}
            <NavItem href='#'><Glyphicon glyph='question-sign' /> Guide</NavItem>
            {userControl}

        </Navbar.Collapse>
      </Navbar>
    )
  }
}
<commit_msg>show nothing if no active project<commit_after>import React, {Component, PropTypes} from 'react'
import ReactDOM from 'react-dom'

import { Navbar, Nav, NavItem, NavDropdown, MenuItem, Glyphicon } from 'react-bootstrap'
import { LinkContainer } from 'react-router-bootstrap'

export default class DatatoolsNavbar extends Component {

  static propTypes = {
    title: PropTypes.string,
    username: PropTypes.string,
    managerUrl: PropTypes.string,
    editorUrl: PropTypes.string,
    userAdminUrl: PropTypes.string,
    alertsUrl: PropTypes.string,
    signConfigUrl: PropTypes.string,
    loginHandler: PropTypes.func,
    logoutHandler: PropTypes.func,
    resetPasswordHandler: PropTypes.func
  };

  render () {
    var userControl
    if (!this.props.username) {
      userControl = (<NavItem onClick={this.props.loginHandler} href='#'>Log In</NavItem>)
    } else {
      userControl = (
        <NavDropdown title={
          <Glyphicon glyph='user' /> {this.props.username}
        } id='basic-nav-dropdown'>
          <MenuItem onClick={this.props.resetPasswordHandler}>Reset Password</MenuItem>
          <MenuItem onClick={this.props.logoutHandler}>Log Out</MenuItem>
        </NavDropdown>
      )
    }
    let projectControl
    if (!this.props.username) {
      projectControl = ('')
    } else if (!this.props.projects || !this.props.projects.active) {
      projectControl = ('') // (<NavItem href='#'>No project selected</NavItem>)
    } else {
      let activeProject = this.props.projects.active
      projectControl = (
        <NavDropdown
          title={<Glyphicon glyph='briefcase' /> {activeProject.name}}
          id='basic-nav-dropdown'
        >
          {this.props.projects.all.map(proj => {
            return (
              <MenuItem
                key={proj.id}
                onClick={(evt) =>{
                  evt.preventDefault()
                  this.props.setActiveProject(proj)
                }}
              >{proj.name}</MenuItem>
            )
          })}
        </NavDropdown>
      )
    }

    return (
      <Navbar>
        <Navbar.Header>
          <Navbar.Brand>
            {this.props.title}
          </Navbar.Brand>
          <Navbar.Toggle />
        </Navbar.Header>
        <Navbar.Collapse>

            <LinkContainer to={this.props.managerUrl}>
              <NavItem>Manager</NavItem>
            </LinkContainer>
            {this.props.editorUrl
              ? <NavItem href={this.props.editorUrl} active={this.props.editorUrl === '#'}>
                  Editor
                </NavItem>
              : null
            }
            {this.props.alertsUrl
              ? <LinkContainer to={this.props.alertsUrl}>
                  <NavItem>Alerts</NavItem>
                </LinkContainer>
              : null
            }
            {this.props.userAdminUrl
              ? <LinkContainer to={this.props.userAdminUrl}>
                  <NavItem>Users</NavItem>
                </LinkContainer>
              : null
            }
            {this.props.signConfigUrl
              ? <LinkContainer to={this.props.signConfigUrl}>
                  <NavItem>Sign Config</NavItem>
                </LinkContainer>
              : null
            }

            {projectControl}
            <NavItem href='#'><Glyphicon glyph='question-sign' /> Guide</NavItem>
            {userControl}

        </Navbar.Collapse>
      </Navbar>
    )
  }
}
<|endoftext|>


<commit_before>import { run } from '@ember/runloop';
import { module, test } from 'qunit';
import Sprite from 'ember-animated/-private/sprite';
import { Move } from 'ember-animated/motions/move';
import {
  equalBounds,
  approxEqualPixels,
  visuallyConstant,
} from '../helpers/assertions';
import { MotionTester, TimeControl } from 'ember-animated/test-support';

let tester, environment, offsetParent, target, innerContent, time;

module('Unit | Move', function(hooks) {
  hooks.beforeEach(function(assert) {
    assert.equalBounds = equalBounds;
    assert.approxEqualPixels = approxEqualPixels;
    assert.visuallyConstant = visuallyConstant;

    time = new TimeControl();

    tester = MotionTester.create({
      motion: Move,
    });

    let fixture = document.querySelector('#qunit-fixture');
    fixture.innerHTML = `

`;
    environment = fixture.querySelector('.environment');
    offsetParent = fixture.querySelector('.offset-parent');
    target = fixture.querySelector('.target');
    innerContent = fixture.querySelector('.inner-content');
    environment.style.width = '600px';
    offsetParent.style.position = 'relative';
    innerContent.style.width = '400px';
  });

  hooks.afterEach(function() {
    time.finished();
  });

  test('simple motion', function(assert) {
    assert.expect(2);
    let p = Sprite.offsetParentStartingAt(target);
    p.measureFinalBounds();
    let s = Sprite.positionedStartingAt(target, p);
    let startBounds = s.element.getBoundingClientRect();
    target.style.left = '300px';
    target.style.top = '400px';
    let endBounds = s.element.getBoundingClientRect();
    s.measureFinalBounds();
    s.lock();

    tester.beforeAnimation = () => {
      assert.equalBounds(s.element.getBoundingClientRect(), startBounds);
    };

    tester.afterAnimation = () => {
      assert.equalBounds(s.element.getBoundingClientRect(), endBounds);
    };

    let done = assert.async();
    run(() => {
      tester.run(s, { duration: 60 }).then(done, done);
      time.advance(60);
    });
  });

  test('simple motion, interrupted', function(assert) {
    target.style['margin-left'] = '0px';
    target.style['margin-top'] = '0px';
    target.style.position = 'relative';

    let p = Sprite.offsetParentStartingAt(target);
    p.measureFinalBounds();
    let s = Sprite.positionedStartingAt(target, p);

    target.style.left = '300px';
    target.style.top = '400px';

    s.measureFinalBounds();
    s.lock();

    run(() => {
      tester.run(s, { duration: 1000 });
    });

    return time.advance(500).then(() => {
      assert.approxEqualPixels(
        s.getCurrentBounds().top,
        s.initialBounds.top + 200,
        'top',
      );
      assert.approxEqualPixels(
        s.getCurrentBounds().left,
        s.initialBounds.left + 150,
        'left',
      );
      let newSprite = Sprite.positionedStartingAt(target, p);
      newSprite.lock();
      target.style.left = '400px';
      target.style.top = '500px';
      newSprite.unlock();
      newSprite.measureFinalBounds();
      newSprite.lock();

      run(() => {
        tester.run(newSprite, { duration: 1000 });
      });
      assert.approxEqualPixels(
        newSprite.getCurrentBounds().top,
        s.initialBounds.top + 200,
        'top continuity',
      );
      assert.approxEqualPixels(
        newSprite.getCurrentBounds().left,
        s.initialBounds.left + 150,
        'left continuity',
      );
      return time.advance(1005).then(() => {
        assert.visuallyConstant(target, () => {
          newSprite.unlock();
        });
      });
    });
  });

  test('interrupting with same destination does not extend animation time', function(assert) {
    let p = Sprite.offsetParentStartingAt(target);
    p.measureFinalBounds();
    let s = Sprite.positionedStartingAt(target, p);
    s.lock();
    document.querySelector('#qunit-fixture .sibling').style.height = '50px';
    s.unlock();
    s.measureFinalBounds();
    s.lock();

    run(() => {
      tester.run(s, { duration: 1000 });
    });

    return time.advance(500).then(() => {
      assert.approxEqualPixels(
        s.getCurrentBounds().top,
        s.initialBounds.top + 25,
        'top',
      );
      let newSprite = Sprite.positionedStartingAt(target, p);
      newSprite.lock();
      newSprite.unlock();
      newSprite.measureFinalBounds();

      newSprite.lock();

      run(() => {
        tester.run(newSprite, { duration: 1000 });
      });
      return time.advance(501).then(() => {
        assert.ok(!tester.get('isAnimating'), 'should be finished by now');
      });
    });
  });

  test('overshooting motion', async function(assert) {
    target.style['margin-left'] = '0px';
    target.style['margin-top'] = '0px';
    target.style.position = 'relative';

    let p = Sprite.offsetParentStartingAt(target);
    p.measureFinalBounds();
    let s = Sprite.positionedStartingAt(target, p);
    target.style.left = '400px';
    s.measureFinalBounds();
    s.lock();

    let motion = new Move(s, {
      easing: (v) => v < 0.5 ? -v : 2 - v
    });

    tester.run(motion, { duration: 400 });
    await time.advance(100);
    assert.equal(s.getCurrentBounds().left, -100);
    await time.advance(200);
    assert.equal(s.getCurrentBounds().left, 500);
    await time.advance(100);
    assert.equal(s.getCurrentBounds().left, 400);
  });
});
<commit_msg>Fix lint error<commit_after>import { run } from '@ember/runloop';
import { module, test } from 'qunit';
import Sprite from 'ember-animated/-private/sprite';
import { Move } from 'ember-animated/motions/move';
import {
  equalBounds,
  approxEqualPixels,
  visuallyConstant,
} from '../helpers/assertions';
import { MotionTester, TimeControl } from 'ember-animated/test-support';

let tester, environment, offsetParent, target, innerContent, time;

module('Unit | Move', function(hooks) {
  hooks.beforeEach(function(assert) {
    assert.equalBounds = equalBounds;
    assert.approxEqualPixels = approxEqualPixels;
    assert.visuallyConstant = visuallyConstant;

    time = new TimeControl();

    tester = MotionTester.create({
      motion: Move,
    });

    let fixture = document.querySelector('#qunit-fixture');
    fixture.innerHTML = `

`;
    environment = fixture.querySelector('.environment');
    offsetParent = fixture.querySelector('.offset-parent');
    target = fixture.querySelector('.target');
    innerContent = fixture.querySelector('.inner-content');
    environment.style.width = '600px';
    offsetParent.style.position = 'relative';
    innerContent.style.width = '400px';
  });

  hooks.afterEach(function() {
    time.finished();
  });

  test('simple motion', function(assert) {
    assert.expect(2);
    let p = Sprite.offsetParentStartingAt(target);
    p.measureFinalBounds();
    let s = Sprite.positionedStartingAt(target, p);
    let startBounds = s.element.getBoundingClientRect();
    target.style.left = '300px';
    target.style.top = '400px';
    let endBounds = s.element.getBoundingClientRect();
    s.measureFinalBounds();
    s.lock();

    tester.beforeAnimation = () => {
      assert.equalBounds(s.element.getBoundingClientRect(), startBounds);
    };

    tester.afterAnimation = () => {
      assert.equalBounds(s.element.getBoundingClientRect(), endBounds);
    };

    let done = assert.async();
    run(() => {
      tester.run(s, { duration: 60 }).then(done, done);
      time.advance(60);
    });
  });

  test('simple motion, interrupted', function(assert) {
    target.style['margin-left'] = '0px';
    target.style['margin-top'] = '0px';
    target.style.position = 'relative';

    let p = Sprite.offsetParentStartingAt(target);
    p.measureFinalBounds();
    let s = Sprite.positionedStartingAt(target, p);

    target.style.left = '300px';
    target.style.top = '400px';

    s.measureFinalBounds();
    s.lock();

    run(() => {
      tester.run(s, { duration: 1000 });
    });

    return time.advance(500).then(() => {
      assert.approxEqualPixels(
        s.getCurrentBounds().top,
        s.initialBounds.top + 200,
        'top',
      );
      assert.approxEqualPixels(
        s.getCurrentBounds().left,
        s.initialBounds.left + 150,
        'left',
      );
      let newSprite = Sprite.positionedStartingAt(target, p);
      newSprite.lock();
      target.style.left = '400px';
      target.style.top = '500px';
      newSprite.unlock();
      newSprite.measureFinalBounds();
      newSprite.lock();

      run(() => {
        tester.run(newSprite, { duration: 1000 });
      });
      assert.approxEqualPixels(
        newSprite.getCurrentBounds().top,
        s.initialBounds.top + 200,
        'top continuity',
      );
      assert.approxEqualPixels(
        newSprite.getCurrentBounds().left,
        s.initialBounds.left + 150,
        'left continuity',
      );
      return time.advance(1005).then(() => {
        assert.visuallyConstant(target, () => {
          newSprite.unlock();
        });
      });
    });
  });

  test('interrupting with same destination does not extend animation time', function(assert) {
    let p = Sprite.offsetParentStartingAt(target);
    p.measureFinalBounds();
    let s = Sprite.positionedStartingAt(target, p);
    s.lock();
    document.querySelector('#qunit-fixture .sibling').style.height = '50px';
    s.unlock();
    s.measureFinalBounds();
    s.lock();

    run(() => {
      tester.run(s, { duration: 1000 });
    });

    return time.advance(500).then(() => {
      assert.approxEqualPixels(
        s.getCurrentBounds().top,
        s.initialBounds.top + 25,
        'top',
      );
      let newSprite = Sprite.positionedStartingAt(target, p);
      newSprite.lock();
      newSprite.unlock();
      newSprite.measureFinalBounds();

      newSprite.lock();

      run(() => {
        tester.run(newSprite, { duration: 1000 });
      });
      return time.advance(501).then(() => {
        assert.ok(!tester.get('isAnimating'), 'should be finished by now');
      });
    });
  });

  test('overshooting motion', async function(assert) {
    target.style['margin-left'] = '0px';
    target.style['margin-top'] = '0px';
    target.style.position = 'relative';

    let p = Sprite.offsetParentStartingAt(target);
    p.measureFinalBounds();
    let s = Sprite.positionedStartingAt(target, p);
    target.style.left = '400px';
    s.measureFinalBounds();
    s.lock();

    let motion = new Move(s, {
      easing: v => (v < 0.5 ? -v : 2 - v),
    });

    tester.run(motion, { duration: 400 });
    await time.advance(100);
    assert.equal(s.getCurrentBounds().left, -100);
    await time.advance(200);
    assert.equal(s.getCurrentBounds().left, 500);
    await time.advance(100);
    assert.equal(s.getCurrentBounds().left, 400);
  });
});
<|endoftext|>