<commit_before>const fs = require('fs-extra');
const fetch = require('node-fetch');
const jsonStableStringify = require('json-stable-stringify');

const getGames = async (sessionId) => {
  if (!sessionId) {
    throw new Error("Missing session ID as 'session' in env.");
  };

  console.log("Getting game list from server.");
  const gameMetas = await getGameList(sessionId);
  const games = [];

  for (const metadata of gameMetas) {
    games.push(await getGameDetails(metadata));
  }

  games.sort((a, b) => a.meta.time - b.meta.time);

  return games;
};

const getGameList = async (sessionId) => {
  const response = await fetch('https://api.overtrack.gg/games', {
    headers: {
      'Cookie': `session=${sessionId}`
    }
  });
  const data = await response.json();
  return data['games'];
};

// Gets the full detailed data for a game.
// Game data will be saved for later.
const getGameDetails = async (gameMetadata) => {
  const m = gameMetadata;
  const fileKey = `${m.player_name}-${new Date(m.time * 1000).toISOString().replace('.000Z', '').replace(/[^0-9]/g, '')}-${m.map}`.replace(/[^a-z0-9]+/gi, '-').toLowerCase();
  const path = `games/${fileKey}.json`;
  let data;

  try {
    data = JSON.parse(await fs.readFile(path));
    console.log(`Loaded ${path}.`);
  } catch (error) {
    const response = await fetch(gameMetadata['url']);
    data = {
      name: fileKey,
      meta: gameMetadata,
      data: await response.json()
    }
    await fs.writeFile(path, stringify(data));
    console.log(`Fetched ${path}.`);
  }

  return data;
};

const stringify = (object) => {
  return jsonStableStringify(object, {space: 2});
};

module.exports = {getGames};
<commit_msg>⏰🍀 Updated with Glitch<commit_after>const fs = require('fs-extra');
const fetch = require('node-fetch');
const jsonStableStringify = require('json-stable-stringify');

const getGames = async (sessionId) => {
  if (!sessionId) {
    throw new Error("Missing session ID as 'session' in env.");
  };

  console.log("Getting game list from server.");
  const gameMetas = await getGameList(sessionId);
  const games = [];

  for (const metadata of gameMetas) {
    games.push(await getGameDetails(metadata));
  }

  games.sort((a, b) => a.meta.time - b.meta.time);

  return games;
};

const getGameList = async (sessionId) => {
  const response = await fetch('https://api.overtrack.gg/games', {
    headers: {
      'Cookie': `session=${sessionId}`
    }
  });
  const data = await response.json();
  return data['games'];
};

// Gets the full detailed data for a game.
// Game data will be saved for later.
const getGameDetails = async (gameMetadata) => {
  const m = gameMetadata;
  const fileKey = `${m.player_name}-${new Date(m.time * 1000).toISOString().replace('.000Z', '').replace(/[^0-9]/g, '')}-${m.map}`.replace(/[^a-z0-9]+/gi, '-').toLowerCase();
  const path = `games/${fileKey}.json`;
  let data;

  try {
    data = JSON.parse(await fs.readFile(path));
    console.log(`Loaded ${path}.`);
  } catch (error) {
    const response = await fetch(gameMetadata['url']);
    data = {
      meta: gameMetadata,
      data: await response.json()
    }
    await fs.writeFile(path, stringify(data));
    console.log(`Fetched ${path}.`);
  }

  data.name = fileKey;
  return data;
};

const stringify = (object) => {
  return jsonStableStringify(object, {space: 2});
};

module.exports = {getGames};
<|endoftext|>


<commit_before>(function() {
	'use strict';

	var VERSION = 1.1;

	// initialize options
	var options = {};
	var gui = require('nw.gui');
	gui.App.argv.forEach(function(item, index) {
		if(item == '-debug') {
			options.debug = true;
		}
	});

	// show devtools if console
	var win = gui.Window.get();
	if(options.debug) {
		win.showDevTools();
	}

	// language
	var translator = require('translator');
	var lang = translator.getLanguage();
	// console.log('language: ' + lang);

	// logger
	var logger = {
		v: function(str) {
			console.log(str);
		},
		i: function(str) {
			console.info('%c' + str, 'color: dodgerblue');
		},
		w: function(str) {
			console.warn('%c' + str, 'color: orange');
		},
		e: function(str) {
			console.error('%c' + str, 'color: red');
		}
	};
	require('logger').set(logger);

	// server
	var server = require('entry');
	server.open(logger);

	// router
	var router = require('router').init(server);
	window.router = router;

	// flasher
	var flasher = require('flasher');

	// ui & control
	$('#driver').text(translator.translate('Install Device Driver'));
	$('#firmware').text(translator.translate('Install Firmware'));
	$('#other-robot .text').text(translator.translate('Connect Other Hardware'));
	$('#entry .text').text(translator.translate('Show Entry Web Page'));
//	$('#reconnect .text').text(translator.translate('Reconnect Hardware'));

	var ui = {
		countRobot: 0,
		showRobotList: function() {
			router.close();
			router.stopScan();
			delete window.currentConfig;
			$('#title').text(translator.translate('Select hardware'));
			$('#hwList').show();
			$('#hwPanel').hide();
			ui.showIeGuide();
			this.hideAlert();
		},
		showConnecting: function() {
			$('#title').text(translator.translate('hardware > connecting'));
			$('#hwList').hide();
			$('#hwPanel').show();
			ui.hideIeGuide();
			this.showAlert(translator.translate('Connecting to hardware device.'));
		},
		showConnected: function() {
			$('#title').text(translator.translate('hardware > connected'));
			$('#hwList').hide();
			$('#hwPanel').show();
			ui.hideIeGuide();
			this.showAlert(translator.translate('Connected to hardware device.'), 2000);
		},
		showDisconnected: function() {
			$('#title').text(translator.translate('hardware > disconnected'));
			$('#hwList').hide();
			$('#hwPanel').show();
			ui.hideIeGuide();
			this.showAlert(translator.translate('Hardware device is disconnected. Please restart this program.'));
		},
		showAlert: function(message, duration) {
			$('#alert').text(message);

			$('#alert').css({
				height: '0px'
			});
			$('#alert').animate({
				height: '35px'
			});

			if (duration) {
				setTimeout(function(){
					$('#alert').animate({
						height: '0px'
					});
				}, duration);
			}
		},
		hideAlert: function(message) {
			$('#alert').animate({
				height: '0px'
			});
		},
		addRobot: function(config) {
			ui.showRobotList();
			var name;
			if(config.name) {
				name = config.name[lang] || config.name['en'];
			}

			$('#hwList').append(
					'
' +
					'' +
					'
' + name + '

');

			$('#' + config.id).click(function() {
				if(config.hardware.type === 'bluetooth') {
					is_select_port = true;
				}

				// if(config.select_com_port) {
				// 	var com_port = prompt('사용하실 COM 포트를 적어주세요.');
				// 	if(!com_port)
				// 		return;
				// 	config.this_com_port = com_port;
				// }

				ui.hardware = config.id.substring(0, 4);
				ui.numLevel = 1;
//				if(config.level) {
//					ui.numLevel = parseInt(config.level);
//				}
				ui.showConnecting();
				router.startScan(config);
				window.currentConfig = config;

				var icon = '../modules/' + config.icon;
				$('#selectedHWThumb').attr('src', icon);

				if(config.url) {
					$('#url').text(config.url);
					$('#url').show();
					$('#url').unbind('click');
					$('#url').click(function() {
						gui.Shell.openItem(config.url);
					});
				} else {
					$('#url').hide();
				}

//				$('#reconnect').click(function() {
//					ui.showConnecting();
//					router.startScan(config);
//				});

				$('#driver').hide();
				$('#firmware').hide();
				if(config.driver) {
					var os = process.platform + '-' + (isOSWin64() ? 'x64' : process.arch);
					var driverPath = config.driver[os];
					if(driverPath) {
						$('#driver').show();
						$('#driver').unbind('click');
						$('#driver').click(function() {
							var path = require('path');
							gui.Shell.openItem(path.join(process.cwd() + '/drivers/', driverPath));
						});
					}
					if (config.firmware) {
						$('#firmware').show();
						$('#firmware').unbind('click');

						$('#firmware').click(function() {
							ui.flashFirmware(config.firmware, config);
						});
					}
				}
//				if(config.description) {
//					var connection = config.description.connection;
//					if(connection) {
//						var description = connection[lang] || connection['en'];
//						$('#description').text(description);
//					}
//				}
			});
		},
		flashFirmware: function(firmware, config) {
			$('#firmware').hide();
			if (!router.connector) {
				alert(translator.translate('Hardware Device Is Not Connected'));
				return;
			}
			ui.showAlert(translator.translate("Firmware Uploading..."));
			var port = router.connector.sp.path;
			router.close();
            setTimeout(function () {
    			flasher.flash(
    				firmware,
    				port,
    				function(error, stdout, stderr) {
    					// console.log(error, stdout, stderr);
    					$('#firmware').show();
    					ui.showAlert(translator.translate("Firmware Uploaded!"));
    					router.startScan(config);
    				}
    			);
            }, 200);
		},
		setState: function(state) {
			if(state == 'connected') {
				ui.showConnected();
//				if(ui.numLevel > 1) {
//					ui.showLevelPanel();
//				} else {
//					ui.level = 1;
//				}
			} else if(state == 'lost') {
				$('#message').text(translator.translate('Connecting...'));
			} else if(state == 'disconnected') {
				ui.showDisconnected();
//				ui.showReconnectButton(true);
			}
		},
		quit: function() {
			win.close();
		},
		showIeGuide: function() {
			$('#ieGuide').show();
		},
		hideIeGuide: function() {
			$('#ieGuide').hide();
		}
	};

	$('#back.navigate_button').click(function(e) {
		delete window.currentConfig.this_com_port;
		ui.showRobotList();
		router.close();
	});

	$('.chromeButton').click(function(e) {
		window.open("https://www.google.com/chrome/browser/desktop/index.html");
	});

	function isOSWin64() {
  		return process.arch === 'x64' || process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
	};

	// close
	win.on('close', function() {
		this.hide();
		router.close();
		server.close();
		if(ui.winEntry) {
			ui.winEntry.close(true);
			ui.winEntry = undefined;
		}
		this.close(true);
	});

	$('#select_port').dblclick(function () {
		$('#btn_select_port').trigger('click');
	});

	$('#btn_select_port').click(function(e) {
		var com_port = $("#select_port").val();
		if(!com_port) {
			alert('연결할 COM PORT를 선택하세요.');
		} else {
			window.currentConfig.this_com_port = com_port;
			clear_select_port();
		}
	});

	$('#btn_select_port_cancel').click(function(e) {
		clear_select_port();
		clearTimeout(select_port_connection);
		ui.showRobotList();
	});

	function clear_select_port() {
		is_select_port = false;
		_cache_object = '';
		$('#select_port_box').css('display', 'none');
	}

	var _cache_object = '';
	var _com_port = '';
	var is_select_port = true;
	var select_port_connection;
	// state
	router.on('state', function(state, data) {
		if (state === "select_port") {
			router.close();
			var _temp = JSON.stringify(data);
			if(_temp !== _cache_object) {
				var port_html = '';
				data.forEach(function (port) {
					port_html += '<option>' + port.comName + '</option>';
				});

				$('#select_port_box').css('display', 'flex');
				$('#select_port_box select').html(port_html);

				_cache_object = _temp;
			}
			if(is_select_port) {
				select_port_connection = setTimeout(function () {
					router.startScan(window.currentConfig);
				}, 1000);
			} else {
				is_select_port = true;
			}
			return;
		} else if (state === "flash") {
			console.log('flash');
            $('#firmware').trigger('click');
		} else if (state === "connect" && window.currentConfig.softwareReset) {
			var sp = router.connector.sp;
			sp.set({dtr: false}, function(){});
			setTimeout(function() {sp.set({dtr: true}, function(){})}, 1000);
			return;
		} else if ((state === "lost" || state === "disconnected") && window.currentConfig.reconnect) {
			router.close();
			ui.showConnecting();
			router.startScan(window.currentConfig);
			return;
		}
		ui.setState(state);
		server.setState(state);
	});

	// configuration
	var fs = require('fs');
	fs.readdir('./modules', function(error, files) {
		if(error) {
			logger.e(error);
			return;
		}
		files.filter(function(file) {
			return /(?:\.([^.]+))?$/.exec(file)[1] == 'json';
		}).forEach(function(file) {
			try {
				var config = fs.readFileSync('./modules/' + file);
				ui.addRobot(JSON.parse(config));
			} catch(e) {}
		});
	});
}());
<commit_msg>fix source<commit_after>(function() {
	'use strict';

	var VERSION = 1.1;

	// initialize options
	var options = {};
	var gui = require('nw.gui');
	gui.App.argv.forEach(function(item, index) {
		if(item == '-debug') {
			options.debug = true;
		}
	});

	// show devtools if console
	var win = gui.Window.get();
	if(options.debug) {
		win.showDevTools();
	}

	// language
	var translator = require('translator');
	var lang = translator.getLanguage();
	// console.log('language: ' + lang);

	// logger
	var logger = {
		v: function(str) {
			console.log(str);
		},
		i: function(str) {
			console.info('%c' + str, 'color: dodgerblue');
		},
		w: function(str) {
			console.warn('%c' + str, 'color: orange');
		},
		e: function(str) {
			console.error('%c' + str, 'color: red');
		}
	};
	require('logger').set(logger);

	// server
	var server = require('entry');
	server.open(logger);

	// router
	var router = require('router').init(server);
	window.router = router;

	// flasher
	var flasher = require('flasher');

	// ui & control
	$('#driver').text(translator.translate('Install Device Driver'));
	$('#firmware').text(translator.translate('Install Firmware'));
	$('#other-robot .text').text(translator.translate('Connect Other Hardware'));
	$('#entry .text').text(translator.translate('Show Entry Web Page'));
//	$('#reconnect .text').text(translator.translate('Reconnect Hardware'));

	var ui = {
		countRobot: 0,
		showRobotList: function() {
			router.close();
			router.stopScan();
			delete window.currentConfig;
			$('#title').text(translator.translate('Select hardware'));
			$('#hwList').show();
			$('#hwPanel').hide();
			ui.showIeGuide();
			this.hideAlert();
		},
		showConnecting: function() {
			$('#title').text(translator.translate('hardware > connecting'));
			$('#hwList').hide();
			$('#hwPanel').show();
			ui.hideIeGuide();
			this.showAlert(translator.translate('Connecting to hardware device.'));
		},
		showConnected: function() {
			$('#title').text(translator.translate('hardware > connected'));
			$('#hwList').hide();
			$('#hwPanel').show();
			ui.hideIeGuide();
			this.showAlert(translator.translate('Connected to hardware device.'), 2000);
		},
		showDisconnected: function() {
			$('#title').text(translator.translate('hardware > disconnected'));
			$('#hwList').hide();
			$('#hwPanel').show();
			ui.hideIeGuide();
			this.showAlert(translator.translate('Hardware device is disconnected. Please restart this program.'));
		},
		showAlert: function(message, duration) {
			$('#alert').text(message);

			$('#alert').css({
				height: '0px'
			});
			$('#alert').animate({
				height: '35px'
			});

			if (duration) {
				setTimeout(function(){
					$('#alert').animate({
						height: '0px'
					});
				}, duration);
			}
		},
		hideAlert: function(message) {
			$('#alert').animate({
				height: '0px'
			});
		},
		addRobot: function(config) {
			ui.showRobotList();
			var name;
			if(config.name) {
				name = config.name[lang] || config.name['en'];
			}

			$('#hwList').append(
					'
' +
					'' +
					'
' + name + '

');

			$('#' + config.id).click(function() {
				if(config.hardware.type === 'bluetooth') {
					is_select_port = true;
				}

				// if(config.select_com_port) {
				// 	var com_port = prompt('사용하실 COM 포트를 적어주세요.');
				// 	if(!com_port)
				// 		return;
				// 	config.this_com_port = com_port;
				// }

				ui.hardware = config.id.substring(0, 4);
				ui.numLevel = 1;
//				if(config.level) {
//					ui.numLevel = parseInt(config.level);
//				}
				ui.showConnecting();
				router.startScan(config);
				window.currentConfig = config;

				var icon = '../modules/' + config.icon;
				$('#selectedHWThumb').attr('src', icon);

				if(config.url) {
					$('#url').text(config.url);
					$('#url').show();
					$('#url').unbind('click');
					$('#url').click(function() {
						gui.Shell.openItem(config.url);
					});
				} else {
					$('#url').hide();
				}

//				$('#reconnect').click(function() {
//					ui.showConnecting();
//					router.startScan(config);
//				});

				$('#driver').hide();
				$('#firmware').hide();
				if(config.driver) {
					var os = process.platform + '-' + (isOSWin64() ? 'x64' : process.arch);
					var driverPath = config.driver[os];
					if(driverPath) {
						$('#driver').show();
						$('#driver').unbind('click');
						$('#driver').click(function() {
							var path = require('path');
							gui.Shell.openItem(path.join(process.cwd() + '/drivers/', driverPath));
						});
					}
					if (config.firmware) {
						$('#firmware').show();
						$('#firmware').unbind('click');

						$('#firmware').click(function() {
							ui.flashFirmware(config.firmware, config);
						});
					}
				}
//				if(config.description) {
//					var connection = config.description.connection;
//					if(connection) {
//						var description = connection[lang] || connection['en'];
//						$('#description').text(description);
//					}
//				}
			});
		},
		flashFirmware: function(firmware, config) {
			$('#firmware').hide();
			if (!router.connector) {
				alert(translator.translate('Hardware Device Is Not Connected'));
				return;
			}
			ui.showAlert(translator.translate("Firmware Uploading..."));
			var port = router.connector.sp.path;
			router.close();
            setTimeout(function () {
    			flasher.flash(
    				firmware,
    				port,
    				function(error, stdout, stderr) {
    					// console.log(error, stdout, stderr);
    					$('#firmware').show();
    					ui.showAlert(translator.translate("Firmware Uploaded!"));
    					router.startScan(config);
    				}
    			);
            }, 200);
		},
		setState: function(state) {
			if(state == 'connected') {
				ui.showConnected();
//				if(ui.numLevel > 1) {
//					ui.showLevelPanel();
//				} else {
//					ui.level = 1;
//				}
			} else if(state == 'lost') {
				$('#message').text(translator.translate('Connecting...'));
			} else if(state == 'disconnected') {
				ui.showDisconnected();
//				ui.showReconnectButton(true);
			}
		},
		quit: function() {
			win.close();
		},
		showIeGuide: function() {
			$('#ieGuide').show();
		},
		hideIeGuide: function() {
			$('#ieGuide').hide();
		}
	};

	$('#back.navigate_button').click(function(e) {
		delete window.currentConfig.this_com_port;
		ui.showRobotList();
		router.close();
	});

	$('.chromeButton').click(function(e) {
		window.open("https://www.google.com/chrome/browser/desktop/index.html");
	});

	function isOSWin64() {
  		return process.arch === 'x64' || process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
	};

	// close
	win.on('close', function() {
		this.hide();
		router.close();
		server.close();
		if(ui.winEntry) {
			ui.winEntry.close(true);
			ui.winEntry = undefined;
		}
		this.close(true);
	});

	$('#select_port').dblclick(function () {
		$('#btn_select_port').trigger('click');
	});

	$('#btn_select_port').click(function(e) {
		var com_port = $("#select_port").val();
		if(!com_port) {
			alert('연결할 COM PORT를 선택하세요.');
		} else {
			window.currentConfig.this_com_port = com_port[0];
			clear_select_port();
		}
	});

	$('#btn_select_port_cancel').click(function(e) {
		clear_select_port();
		clearTimeout(select_port_connection);
		ui.showRobotList();
	});

	function clear_select_port() {
		is_select_port = false;
		_cache_object = '';
		$('#select_port_box').css('display', 'none');
	}

	var _cache_object = '';
	var _com_port = '';
	var is_select_port = true;
	var select_port_connection;
	// state
	router.on('state', function(state, data) {
		if (state === "select_port") {
			router.close();
			var _temp = JSON.stringify(data);
			if(_temp !== _cache_object) {
				var port_html = '';
				data.forEach(function (port) {
					port_html += '<option>' + port.comName + '</option>';
				});

				$('#select_port_box').css('display', 'flex');
				$('#select_port_box select').html(port_html);

				_cache_object = _temp;
			}
			if(is_select_port) {
				select_port_connection = setTimeout(function () {
					router.startScan(window.currentConfig);
				}, 1000);
			} else {
				is_select_port = true;
			}
			return;
		} else if (state === "flash") {
			console.log('flash');
            $('#firmware').trigger('click');
		} else if (state === "connect" && window.currentConfig.softwareReset) {
			var sp = router.connector.sp;
			sp.set({dtr: false}, function(){});
			setTimeout(function() {sp.set({dtr: true}, function(){})}, 1000);
			return;
		} else if ((state === "lost" || state === "disconnected") && window.currentConfig.reconnect) {
			router.close();
			ui.showConnecting();
			router.startScan(window.currentConfig);
			return;
		}
		ui.setState(state);
		server.setState(state);
	});

	// configuration
	var fs = require('fs');
	fs.readdir('./modules', function(error, files) {
		if(error) {
			logger.e(error);
			return;
		}
		files.filter(function(file) {
			return /(?:\.([^.]+))?$/.exec(file)[1] == 'json';
		}).forEach(function(file) {
			try {
				var config = fs.readFileSync('./modules/' + file);
				ui.addRobot(JSON.parse(config));
			} catch(e) {}
		});
	});
}());
<|endoftext|>


<commit_before>'use strict';

import EventTarget from 'event-target-shim';
import {DeviceEventEmitter, NativeModules} from 'react-native';

import MediaStream from './MediaStream';
import MediaStreamEvent from './MediaStreamEvent';
import MediaStreamTrack from './MediaStreamTrack';
import RTCDataChannel from './RTCDataChannel';
import RTCDataChannelEvent from './RTCDataChannelEvent';
import RTCSessionDescription from './RTCSessionDescription';
import RTCIceCandidate from './RTCIceCandidate';
import RTCIceCandidateEvent from './RTCIceCandidateEvent';
import RTCEvent from './RTCEvent';

const {WebRTCModule} = NativeModules;

type RTCSignalingState =
  'stable' |
  'have-local-offer' |
  'have-remote-offer' |
  'have-local-pranswer' |
  'have-remote-pranswer' |
  'closed';

type RTCIceGatheringState =
  'new' |
  'gathering' |
  'complete';

type RTCIceConnectionState =
  'new' |
  'checking' |
  'connected' |
  'completed' |
  'failed' |
  'disconnected' |
  'closed';

/**
 * The default constraints of RTCPeerConnection's createOffer() and
 * createAnswer().
 */
const DEFAULT_SDP_CONSTRAINTS = {
  mandatory: {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true,
  }
};

/**
 * The default constraints of RTCPeerConnection's WebRTCModule.peerConnectionInit.
 */
const DEFAULT_PC_CONSTRAINTS = {
  mandatory: {},
  optional: [
    { DtlsSrtpKeyAgreement: true },
  ],
};

const PEER_CONNECTION_EVENTS = [
  'connectionstatechange',
  'icecandidate',
  'icecandidateerror',
  'iceconnectionstatechange',
  'icegatheringstatechange',
  'negotiationneeded',
  'signalingstatechange',
  // Peer-to-peer Data API:
  'datachannel',
  // old:
  'addstream',
  'removestream',
];

let nextPeerConnectionId = 0;

export default class RTCPeerConnection extends EventTarget(PEER_CONNECTION_EVENTS) {
  localDescription: RTCSessionDescription;
  remoteDescription: RTCSessionDescription;

  signalingState: RTCSignalingState = 'stable';
  iceGatheringState: RTCIceGatheringState = 'new';
  iceConnectionState: RTCIceConnectionState = 'new';

  onconnectionstatechange: ?Function;
  onicecandidate: ?Function;
  onicecandidateerror: ?Function;
  oniceconnectionstatechange: ?Function;
  onicegatheringstatechange: ?Function;
  onnegotiationneeded: ?Function;
  onsignalingstatechange: ?Function;

  onaddstream: ?Function;
  onremovestream: ?Function;

  _peerConnectionId: number;
  _remoteStreams: Array<MediaStream> = [];
  _subscriptions: Array<any>;

  /**
   * The RTCDataChannel.id allocator of this RTCPeerConnection.
   */
  _dataChannelIds: Set = new Set();

  constructor(configuration) {
    super();
    this._peerConnectionId = nextPeerConnectionId++;
    WebRTCModule.peerConnectionInit(
        configuration,
        DEFAULT_PC_CONSTRAINTS,
        this._peerConnectionId);
    this._registerEvents();
  }

  addStream(stream: MediaStream) {
    WebRTCModule.peerConnectionAddStream(stream.reactTag, this._peerConnectionId);
  }

  removeStream(stream: MediaStream) {
    WebRTCModule.peerConnectionRemoveStream(stream.reactTag, this._peerConnectionId);
  }

  _mergeMediaConstraints(options) {
    const constraints = Object.assign({}, DEFAULT_SDP_CONSTRAINTS);
    if (options) {
      if (options.mandatory) {
        constraints.mandatory = {...constraints.mandatory, ...options.mandatory};
      }
      if (options.optional) {
        constraints.optional = {...constraints.optional, ...options.optional};
      }
    }
    return constraints;
  }

  createOffer(successCallback: ?Function, failureCallback: ?Function, options) {
    WebRTCModule.peerConnectionCreateOffer(
        this._peerConnectionId,
        this._mergeMediaConstraints(options),
        (successful, data) => {
          if (successful) {
            successCallback(new RTCSessionDescription(data));
          } else {
            failureCallback(data); // TODO: convert to NavigatorUserMediaError
          }
        });
  }

  createAnswer(successCallback: ?Function, failureCallback: ?Function, options) {
    WebRTCModule.peerConnectionCreateAnswer(
        this._peerConnectionId,
        this._mergeMediaConstraints(options),
        (successful, data) => {
          if (successful) {
            successCallback(new RTCSessionDescription(data));
          } else {
            failureCallback(data);
          }
        });
  }

  setConfiguration(configuration) {
    WebRTCModule.peerConnectionSetConfiguration(configuration, this._peerConnectionId);
  }

  setLocalDescription(sessionDescription: RTCSessionDescription, success: ?Function, failure: ?Function, constraints) {
    WebRTCModule.peerConnectionSetLocalDescription(sessionDescription.toJSON(), this._peerConnectionId, (successful, data) => {
      if (successful) {
        this.localDescription = sessionDescription;
        success();
      } else {
        failure(data);
      }
    });
  }

  setRemoteDescription(sessionDescription: RTCSessionDescription, success: ?Function, failure: ?Function) {
    WebRTCModule.peerConnectionSetRemoteDescription(sessionDescription.toJSON(), this._peerConnectionId, (successful, data) => {
      if (successful) {
        this.remoteDescription = sessionDescription;
        success();
      } else {
        failure(data);
      }
    });
  }

  addIceCandidate(candidate, success, failure) { // TODO: success, failure
    WebRTCModule.peerConnectionAddICECandidate(candidate.toJSON(), this._peerConnectionId, (successful) => {
      if (successful) {
        success && success();
      } else {
        failure && failure();
      }
    });
  }

  getStats(track, success, failure) {
    if (WebRTCModule.peerConnectionGetStats) {
      WebRTCModule.peerConnectionGetStats(
        (track && track.id) || '',
        this._peerConnectionId,
        stats => {
          if (success) {
            // It turns out that on Android it is faster to construct a single
            // JSON string representing the array of StatsReports and have it
            // pass through the React Native bridge rather than the array of
            // StatsReports.
            if (typeof stats === 'string') {
              try {
                stats = JSON.parse(stats);
              } catch (e) {
                failure(e);
                return;
              }
            }
            success(stats);
          }
        });
    } else {
      console.warn('RTCPeerConnection getStats not supported');
    }
  }

  getRemoteStreams() {
    return this._remoteStreams.slice();
  }

  close() {
    WebRTCModule.peerConnectionClose(this._peerConnectionId);
  }

  _unregisterEvents(): void {
    this._subscriptions.forEach(e => e.remove());
    this._subscriptions = [];
  }

  _registerEvents(): void {
    this._subscriptions = [
      DeviceEventEmitter.addListener('peerConnectionOnRenegotiationNeeded', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        this.dispatchEvent(new RTCEvent('negotiationneeded'));
      }),
      DeviceEventEmitter.addListener('peerConnectionIceConnectionChanged', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        this.iceConnectionState = ev.iceConnectionState;
        this.dispatchEvent(new RTCEvent('iceconnectionstatechange'));
        if (ev.iceConnectionState === 'closed') {
          // This PeerConnection is done, clean up event handlers.
          this._unregisterEvents();
        }
      }),
      DeviceEventEmitter.addListener('peerConnectionSignalingStateChanged', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        this.signalingState = ev.signalingState;
        this.dispatchEvent(new RTCEvent('signalingstatechange'));
      }),
      DeviceEventEmitter.addListener('peerConnectionAddedStream', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        const stream = new MediaStream(ev.streamId, ev.streamReactTag);
        const tracks = ev.tracks;
        for (let i = 0; i < tracks.length; i++) {
          stream.addTrack(new MediaStreamTrack(tracks[i]));
        }
        this._remoteStreams.push(stream);
        this.dispatchEvent(new MediaStreamEvent('addstream', {stream}));
      }),
      DeviceEventEmitter.addListener('peerConnectionRemovedStream', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        const stream = this._remoteStreams.find(s => s.reactTag === ev.streamId);
        if (stream) {
          const index = this._remoteStreams.indexOf(stream);
          if (index > -1) {
            this._remoteStreams.splice(index, 1);
          }
        }
        this.dispatchEvent(new MediaStreamEvent('removestream', {stream}));
      }),
      DeviceEventEmitter.addListener('peerConnectionGotICECandidate', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        const candidate = new RTCIceCandidate(ev.candidate);
        const event = new RTCIceCandidateEvent('icecandidate', {candidate});
        this.dispatchEvent(event);
      }),
      DeviceEventEmitter.addListener('peerConnectionIceGatheringChanged', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        this.iceGatheringState = ev.iceGatheringState;

        if (this.iceGatheringState === 'complete') {
          this.dispatchEvent(new RTCIceCandidateEvent('icecandidate', null));
        }

        this.dispatchEvent(new RTCEvent('icegatheringstatechange'));
      }),
      DeviceEventEmitter.addListener('peerConnectionDidOpenDataChannel', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        const evDataChannel = ev.dataChannel;
        const id = evDataChannel.id;
        // XXX RTP data channels are not defined by the WebRTC standard, have
        // been deprecated in Chromium, and Google have decided (in 2015) to no
        // longer support them (in the face of multiple reported issues of
        // breakages).
        if (typeof id !== 'number' || id === -1) {
          return;
        }
        const channel
          = new RTCDataChannel(
              this._peerConnectionId,
              evDataChannel.label,
              evDataChannel);
        // XXX webrtc::PeerConnection checked that id was not in use in its own
        // SID allocator before it invoked us. Additionally, its own SID
        // allocator is the authority on ResourceInUse. Consequently, it is
        // (pretty) safe to update our RTCDataChannel.id allocator without
        // checking for ResourceInUse.
        this._dataChannelIds.add(id);
        this.dispatchEvent(new RTCDataChannelEvent('datachannel', {channel}));
      })
    ];
  }

  /**
   * Creates a new RTCDataChannel object with the given label. The
   * RTCDataChannelInit dictionary can be used to configure properties of the
   * underlying channel such as data reliability.
   *
   * @param {string} label - the value with which the label attribute of the new
   * instance is to be initialized
   * @param {RTCDataChannelInit} dataChannelDict - an optional dictionary of
   * values with which to initialize corresponding attributes of the new
   * instance such as id
   */
  createDataChannel(label: string, dataChannelDict?: ?RTCDataChannelInit) {
    let id;
    const dataChannelIds = this._dataChannelIds;
    if (dataChannelDict && 'id' in dataChannelDict) {
      id = dataChannelDict.id;
      if (typeof id !== 'number') {
        throw new TypeError('DataChannel id must be a number: ' + id);
      }
      if (dataChannelIds.has(id)) {
        throw new ResourceInUse('DataChannel id already in use: ' + id);
      }
    } else {
      // Allocate a new id.
      // TODO Remembering the last used/allocated id and then incrementing it to
      // generate the next id to use will surely be faster. However, I want to
      // reuse ids (in the future) as the RTCDataChannel.id space is limited to
      // unsigned short by the standard:
      // https://www.w3.org/TR/webrtc/#dom-datachannel-id. Additionally, 65535
      // is reserved due to SCTP INIT and INIT-ACK chunks only allowing a
      // maximum of 65535 streams to be negotiated (as defined by the WebRTC
      // Data Channel Establishment Protocol).
      for (id = 0; id < 65535 && dataChannelIds.has(id); ++id);
      // TODO Throw an error if no unused id is available.
      dataChannelDict = Object.assign({id}, dataChannelDict);
    }
    WebRTCModule.createDataChannel(
        this._peerConnectionId,
        label,
        dataChannelDict);
    dataChannelIds.add(id);
    return new RTCDataChannel(this._peerConnectionId, label, dataChannelDict);
  }
}
<commit_msg>js: fix `_mergeMediaConstraints()`<commit_after>'use strict';

import EventTarget from 'event-target-shim';
import {DeviceEventEmitter, NativeModules} from 'react-native';

import MediaStream from './MediaStream';
import MediaStreamEvent from './MediaStreamEvent';
import MediaStreamTrack from './MediaStreamTrack';
import RTCDataChannel from './RTCDataChannel';
import RTCDataChannelEvent from './RTCDataChannelEvent';
import RTCSessionDescription from './RTCSessionDescription';
import RTCIceCandidate from './RTCIceCandidate';
import RTCIceCandidateEvent from './RTCIceCandidateEvent';
import RTCEvent from './RTCEvent';

const {WebRTCModule} = NativeModules;

type RTCSignalingState =
  'stable' |
  'have-local-offer' |
  'have-remote-offer' |
  'have-local-pranswer' |
  'have-remote-pranswer' |
  'closed';

type RTCIceGatheringState =
  'new' |
  'gathering' |
  'complete';

type RTCIceConnectionState =
  'new' |
  'checking' |
  'connected' |
  'completed' |
  'failed' |
  'disconnected' |
  'closed';

/**
 * The default constraints of RTCPeerConnection's createOffer() and
 * createAnswer().
 */
const DEFAULT_SDP_CONSTRAINTS = {
  mandatory: {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true,
  },
  optional: [],
};

/**
 * The default constraints of RTCPeerConnection's WebRTCModule.peerConnectionInit.
 */
const DEFAULT_PC_CONSTRAINTS = {
  mandatory: {},
  optional: [
    { DtlsSrtpKeyAgreement: true },
  ],
};

const PEER_CONNECTION_EVENTS = [
  'connectionstatechange',
  'icecandidate',
  'icecandidateerror',
  'iceconnectionstatechange',
  'icegatheringstatechange',
  'negotiationneeded',
  'signalingstatechange',
  // Peer-to-peer Data API:
  'datachannel',
  // old:
  'addstream',
  'removestream',
];

let nextPeerConnectionId = 0;

export default class RTCPeerConnection extends EventTarget(PEER_CONNECTION_EVENTS) {
  localDescription: RTCSessionDescription;
  remoteDescription: RTCSessionDescription;

  signalingState: RTCSignalingState = 'stable';
  iceGatheringState: RTCIceGatheringState = 'new';
  iceConnectionState: RTCIceConnectionState = 'new';

  onconnectionstatechange: ?Function;
  onicecandidate: ?Function;
  onicecandidateerror: ?Function;
  oniceconnectionstatechange: ?Function;
  onicegatheringstatechange: ?Function;
  onnegotiationneeded: ?Function;
  onsignalingstatechange: ?Function;

  onaddstream: ?Function;
  onremovestream: ?Function;

  _peerConnectionId: number;
  _remoteStreams: Array<MediaStream> = [];
  _subscriptions: Array<any>;

  /**
   * The RTCDataChannel.id allocator of this RTCPeerConnection.
   */
  _dataChannelIds: Set = new Set();

  constructor(configuration) {
    super();
    this._peerConnectionId = nextPeerConnectionId++;
    WebRTCModule.peerConnectionInit(
        configuration,
        DEFAULT_PC_CONSTRAINTS,
        this._peerConnectionId);
    this._registerEvents();
  }

  addStream(stream: MediaStream) {
    WebRTCModule.peerConnectionAddStream(stream.reactTag, this._peerConnectionId);
  }

  removeStream(stream: MediaStream) {
    WebRTCModule.peerConnectionRemoveStream(stream.reactTag, this._peerConnectionId);
  }

  /**
   * Merge custom constraints with the default one. The custom one take precedence.
   *
   * @param {Object} options - webrtc constraints
   * @return {Object} constraints - merged webrtc constraints
   */
  _mergeMediaConstraints(options) {
    const constraints = Object.assign({}, DEFAULT_SDP_CONSTRAINTS);
    if (options) {
      if (options.mandatory) {
        constraints.mandatory = {...constraints.mandatory, ...options.mandatory};
      }
      if (options.optional && Array.isArray(options.optional)) {
        // `optional` is an array, webrtc only finds first and ignore the rest if duplicate.
        constraints.optional = options.optional.concat(constraints.optional);
      }
    }
    return constraints;
  }

  createOffer(successCallback: ?Function, failureCallback: ?Function, options) {
    WebRTCModule.peerConnectionCreateOffer(
        this._peerConnectionId,
        this._mergeMediaConstraints(options),
        (successful, data) => {
          if (successful) {
            successCallback(new RTCSessionDescription(data));
          } else {
            failureCallback(data); // TODO: convert to NavigatorUserMediaError
          }
        });
  }

  createAnswer(successCallback: ?Function, failureCallback: ?Function, options) {
    WebRTCModule.peerConnectionCreateAnswer(
        this._peerConnectionId,
        this._mergeMediaConstraints(options),
        (successful, data) => {
          if (successful) {
            successCallback(new RTCSessionDescription(data));
          } else {
            failureCallback(data);
          }
        });
  }

  setConfiguration(configuration) {
    WebRTCModule.peerConnectionSetConfiguration(configuration, this._peerConnectionId);
  }

  setLocalDescription(sessionDescription: RTCSessionDescription, success: ?Function, failure: ?Function, constraints) {
    WebRTCModule.peerConnectionSetLocalDescription(sessionDescription.toJSON(), this._peerConnectionId, (successful, data) => {
      if (successful) {
        this.localDescription = sessionDescription;
        success();
      } else {
        failure(data);
      }
    });
  }

  setRemoteDescription(sessionDescription: RTCSessionDescription, success: ?Function, failure: ?Function) {
    WebRTCModule.peerConnectionSetRemoteDescription(sessionDescription.toJSON(), this._peerConnectionId, (successful, data) => {
      if (successful) {
        this.remoteDescription = sessionDescription;
        success();
      } else {
        failure(data);
      }
    });
  }

  addIceCandidate(candidate, success, failure) { // TODO: success, failure
    WebRTCModule.peerConnectionAddICECandidate(candidate.toJSON(), this._peerConnectionId, (successful) => {
      if (successful) {
        success && success();
      } else {
        failure && failure();
      }
    });
  }

  getStats(track, success, failure) {
    if (WebRTCModule.peerConnectionGetStats) {
      WebRTCModule.peerConnectionGetStats(
        (track && track.id) || '',
        this._peerConnectionId,
        stats => {
          if (success) {
            // It turns out that on Android it is faster to construct a single
            // JSON string representing the array of StatsReports and have it
            // pass through the React Native bridge rather than the array of
            // StatsReports.
            if (typeof stats === 'string') {
              try {
                stats = JSON.parse(stats);
              } catch (e) {
                failure(e);
                return;
              }
            }
            success(stats);
          }
        });
    } else {
      console.warn('RTCPeerConnection getStats not supported');
    }
  }

  getRemoteStreams() {
    return this._remoteStreams.slice();
  }

  close() {
    WebRTCModule.peerConnectionClose(this._peerConnectionId);
  }

  _unregisterEvents(): void {
    this._subscriptions.forEach(e => e.remove());
    this._subscriptions = [];
  }

  _registerEvents(): void {
    this._subscriptions = [
      DeviceEventEmitter.addListener('peerConnectionOnRenegotiationNeeded', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        this.dispatchEvent(new RTCEvent('negotiationneeded'));
      }),
      DeviceEventEmitter.addListener('peerConnectionIceConnectionChanged', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        this.iceConnectionState = ev.iceConnectionState;
        this.dispatchEvent(new RTCEvent('iceconnectionstatechange'));
        if (ev.iceConnectionState === 'closed') {
          // This PeerConnection is done, clean up event handlers.
          this._unregisterEvents();
        }
      }),
      DeviceEventEmitter.addListener('peerConnectionSignalingStateChanged', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        this.signalingState = ev.signalingState;
        this.dispatchEvent(new RTCEvent('signalingstatechange'));
      }),
      DeviceEventEmitter.addListener('peerConnectionAddedStream', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        const stream = new MediaStream(ev.streamId, ev.streamReactTag);
        const tracks = ev.tracks;
        for (let i = 0; i < tracks.length; i++) {
          stream.addTrack(new MediaStreamTrack(tracks[i]));
        }
        this._remoteStreams.push(stream);
        this.dispatchEvent(new MediaStreamEvent('addstream', {stream}));
      }),
      DeviceEventEmitter.addListener('peerConnectionRemovedStream', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        const stream = this._remoteStreams.find(s => s.reactTag === ev.streamId);
        if (stream) {
          const index = this._remoteStreams.indexOf(stream);
          if (index > -1) {
            this._remoteStreams.splice(index, 1);
          }
        }
        this.dispatchEvent(new MediaStreamEvent('removestream', {stream}));
      }),
      DeviceEventEmitter.addListener('peerConnectionGotICECandidate', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        const candidate = new RTCIceCandidate(ev.candidate);
        const event = new RTCIceCandidateEvent('icecandidate', {candidate});
        this.dispatchEvent(event);
      }),
      DeviceEventEmitter.addListener('peerConnectionIceGatheringChanged', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        this.iceGatheringState = ev.iceGatheringState;

        if (this.iceGatheringState === 'complete') {
          this.dispatchEvent(new RTCIceCandidateEvent('icecandidate', null));
        }

        this.dispatchEvent(new RTCEvent('icegatheringstatechange'));
      }),
      DeviceEventEmitter.addListener('peerConnectionDidOpenDataChannel', ev => {
        if (ev.id !== this._peerConnectionId) {
          return;
        }
        const evDataChannel = ev.dataChannel;
        const id = evDataChannel.id;
        // XXX RTP data channels are not defined by the WebRTC standard, have
        // been deprecated in Chromium, and Google have decided (in 2015) to no
        // longer support them (in the face of multiple reported issues of
        // breakages).
        if (typeof id !== 'number' || id === -1) {
          return;
        }
        const channel
          = new RTCDataChannel(
              this._peerConnectionId,
              evDataChannel.label,
              evDataChannel);
        // XXX webrtc::PeerConnection checked that id was not in use in its own
        // SID allocator before it invoked us. Additionally, its own SID
        // allocator is the authority on ResourceInUse. Consequently, it is
        // (pretty) safe to update our RTCDataChannel.id allocator without
        // checking for ResourceInUse.
        this._dataChannelIds.add(id);
        this.dispatchEvent(new RTCDataChannelEvent('datachannel', {channel}));
      })
    ];
  }

  /**
   * Creates a new RTCDataChannel object with the given label. The
   * RTCDataChannelInit dictionary can be used to configure properties of the
   * underlying channel such as data reliability.
   *
   * @param {string} label - the value with which the label attribute of the new
   * instance is to be initialized
   * @param {RTCDataChannelInit} dataChannelDict - an optional dictionary of
   * values with which to initialize corresponding attributes of the new
   * instance such as id
   */
  createDataChannel(label: string, dataChannelDict?: ?RTCDataChannelInit) {
    let id;
    const dataChannelIds = this._dataChannelIds;
    if (dataChannelDict && 'id' in dataChannelDict) {
      id = dataChannelDict.id;
      if (typeof id !== 'number') {
        throw new TypeError('DataChannel id must be a number: ' + id);
      }
      if (dataChannelIds.has(id)) {
        throw new ResourceInUse('DataChannel id already in use: ' + id);
      }
    } else {
      // Allocate a new id.
      // TODO Remembering the last used/allocated id and then incrementing it to
      // generate the next id to use will surely be faster. However, I want to
      // reuse ids (in the future) as the RTCDataChannel.id space is limited to
      // unsigned short by the standard:
      // https://www.w3.org/TR/webrtc/#dom-datachannel-id. Additionally, 65535
      // is reserved due to SCTP INIT and INIT-ACK chunks only allowing a
      // maximum of 65535 streams to be negotiated (as defined by the WebRTC
      // Data Channel Establishment Protocol).
      for (id = 0; id < 65535 && dataChannelIds.has(id); ++id);
      // TODO Throw an error if no unused id is available.
      dataChannelDict = Object.assign({id}, dataChannelDict);
    }
    WebRTCModule.createDataChannel(
        this._peerConnectionId,
        label,
        dataChannelDict);
    dataChannelIds.add(id);
    return new RTCDataChannel(this._peerConnectionId, label, dataChannelDict);
  }
}
<|endoftext|>


<commit_before>// hid.js
//  This code represents the Human Interaction Device, which
// means things the user really interacts with.
//  Code related to the touch display, gamepad, keyboard, all
// this should be here. This function makes then the resulting
// of this interaction something the rest of the code can use.

var HID = {
    scale: 1,
    bsz: 96,
    offset: 416,
    axeNotNull: 0.4,
    inputs: {
        up: {
            letter: 'W',
            active: false,
            mapX: 96,
            mapY: 0,
            color: '#111111',
            gamepad: function(pad) {
               if (!(typeof pad === "undefined")){
                   return (pad.axes[1] < -HID.axeNotNull);
               } else {
                   return false
               }
            }
        },
        left: {
            letter: 'A',
            active: false,
            mapX: 0,
            mapY: 96,
            color: '#111111',
            gamepad: function(pad) {
                if (!(typeof pad === "undefined")){
                    return (pad.axes[0] < -HID.axeNotNull);
                } else {
                    return false
                }
            }
        },
        down: {
            letter: 'S',
            active: false,
            mapX: 96,
            mapY: 192,
            color: '#111111',
            gamepad: function(pad) {
                if (!(typeof pad === "undefined")){
                    return (pad.axes[1] > HID.axeNotNull);
                } else {
                    return false
                }
            }
        },
        right: {
            letter: 'D',
            active: false,
            mapX: 192,
            mapY: 96,
            color: '#111111',
            gamepad: function(pad) {
                if (!(typeof pad === "undefined")) {
                    return (pad.axes[0] > HID.axeNotNull);
                } else {
                    return false
                }
            }
        },
        accept: {
            letter: 'I',
            active: false,
            mapX: 304,
            mapY: 32,
            color: '#127826',
            gamepad: function(pad) {
                if (!(typeof pad === "undefined")) {
                    return pad.buttons[0].pressed;
                } else {
                    return false
                }
            }
        },
        cancel: {
            letter: 'J',
            active: false,
            mapX: 304,
            mapY: 160,
            color: '#ed1c24',
            gamepad: function(pad) {
                if (!(typeof pad === "undefined")){
                    return pad.buttons[1].pressed;
                } else {
                    return false
                }
            }
        }
    },
    debugKeys: {
        resize: {
            letter: '0',
            toggle: function() {
                screen.resize()
            }
        },
        layer1: {
            letter: '1',
            toggle: function() {
                debug.showLayer.layer1 = !debug.showLayer.layer1
                if (debug.showLayer.layer1) {
                    actions.alert("layer1 shown.")
                } else {
                    actions.alert("layer1 hidden.")
                }
            }
        },
        layer2: {
            letter: '2',
            toggle: function() {
                debug.showLayer.layer2 = !debug.showLayer.layer2
                if (debug.showLayer.layer2) {
                    actions.alert("layer2 shown.")
                } else {
                    actions.alert("layer2 hidden.")
                }
            }
        },
        layer3: {
            letter: '3',
            toggle: function() {
                debug.showLayer.layer3 = !debug.showLayer.layer3
                if (debug.showLayer.layer3) {
                    actions.alert("layer3 shown.")
                } else {
                    actions.alert("layer3 hidden.")
                }
            }
        },
        layer4: {
            letter: '4',
            toggle: function() {
                debug.showLayer.layer4 = !debug.showLayer.layer4
                if (debug.showLayer.layer4) {
                    actions.alert("layer4 shown.")
                } else {
                    actions.alert("layer4 hidden.")
                }
            }
        }
    },

    processGamepad: function() {
        if (!(typeof HID.gamepads === "undefined")) {

            if(HID.gamepads.length > 0){
                for (var i = 0; i < HID.gamepads.length; ++i) {
                    var pad = navigator.getGamepads()[HID.gamepads[i]];

                    if (!(typeof pad === "undefined")) {
                        for (var tag in HID.inputs) {
                            var HIDItem = HID.inputs[tag];

                            if (HIDItem.gamepad(pad)) {
                                HIDItem.active = true
                            } else {
                                HIDItem.active = false
                            }
                        }
                    }
                }
            }
        }
    },

    clearInputs: function() {
        for (var tag in HID.inputs) {
            var HIDItem = HID.inputs[tag];
            HIDItem.release = false;
        }
    },

    cleanInputs: function() {
        for (var tag in HID.inputs) {
            var HIDItem = HID.inputs[tag];
            HIDItem.active = false;
        }
    },

    log: function(text) {
        var tag = document.getElementById("log");
        tag.innerHTML = text;
    },

    keyDown: function(e) {
        var evtobj = window.event ? event : e;
        var unicode = evtobj.charCode ? evtobj.charCode : evtobj.keyCode;
        var actualkey = String.fromCharCode(unicode);

        for (var key in HID.inputs) {
            var HIDItem = HID.inputs[key];

            if (actualkey == HIDItem.letter) {
                HIDItem.active = true;
            }
        }

    },

    keyUp: function(e) {
        var evtobj = window.event ? event : e;
        var unicode = evtobj.charCode ? evtobj.charCode : evtobj.keyCode;
        var actualkey = String.fromCharCode(unicode);

        for (var key in HID.inputs) {
            var HIDItem = HID.inputs[key];

            if (actualkey == HIDItem.letter) {
                HIDItem.active = false;
            }
        }

        for (var key in HID.debugKeys) {
            var HIDItem = HID.debugKeys[key];

            if (actualkey == HIDItem.letter) {
                HIDItem.toggle();
            }
        }

    },

    setupTouchZone: function(_screen) {
        this.screen = _screen
        this.touchzone = this.screen.canvas
    },

    setupKeyboardListeners: function() {
        document.onkeydown = HID.keyDown;
        document.onkeyup = HID.keyUp;
    },

    handleTouchDown: function(evnt) {
        evnt.preventDefault();
        HID.processTouches(event.touches, "down");
    },

    handleTouchMove: function(evnt) {
        evnt.preventDefault();
        HID.processTouches(event.touches, "move");
    },

    handleTouchUp: function(evnt) {
        evnt.preventDefault();
        HID.processTouches(event.touches, "up");
    },

    processTouches: function(touches, kind) {
        //HID.log("type:" +kind+" seed: "+Date());
        for (var tag in HID.inputs) {

            var HIDItem = HID.inputs[tag];
            var touched = false;

            var offsetTop = this.screen.canvas.offsetTop,
                offsetLeft = this.screen.canvas.offsetLeft;
            scale = this.screen.currentWidth / this.screen.WIDTH;

            for (var i = 0; i < touches.length; i++) {

                var position = {
                    x: ((touches[i].pageX - offsetLeft) / scale),
                    y: ((touches[i].pageY - offsetTop) / scale)
                };

                if (position.x > HIDItem.mapX && position.y > this.offset + HIDItem.mapY) {
                    if (position.x < HIDItem.mapX + this.bsz && position.y < this.offset + HIDItem.mapY + this.bsz) {
                        touched = true;
                    }
                }

            }

            if (touched) {
                HIDItem.active = true;
                if (kind == "down") {
                    HIDItem.release = true;
                }
            } else {
                HIDItem.active = false;
            }

        }

        for (var menuTag in menus.allMenus) {
            var aMenu = menus.allMenus[menuTag]

            if (aMenu.enabled && !aMenu.wait) {
                var maxItems = aMenu.itemsLength

                for (var menuItemTag in aMenu.items) {
                    var menuItem = aMenu.items[menuItemTag];
                    var touched = false;

                    var offsetTop = this.screen.canvas.offsetTop,
                        offsetLeft = this.screen.canvas.offsetLeft;
                    scale = this.screen.currentWidth / this.screen.WIDTH;

                    for (var i = 0; i < touches.length; i++) {

                        var position = {
                            x: ((touches[i].pageX - offsetLeft) / scale),
                            y: ((touches[i].pageY - offsetTop) / scale)
                        };

                        if (position.x > (screen.GSTARTX + aMenu.drawx) && position.y > (screen.GSTARTY + aMenu.drawy + menuItem.itemy - 32)) {
                            if (position.x < (screen.GSTARTX + aMenu.drawx + aMenu.width) && position.y < (screen.GSTARTY + aMenu.drawy + menuItem.itemy)) {
                                touched = true;
                            }
                        }

                    }

                    if (touched) {
                        if (kind == "down" && menuItem.selected) {
                            HID.inputs["accept"].active = true
                            return;
                        }
                        for (var z = 0; z < maxItems; z += 1) {
                            aMenu.items[Object.keys(aMenu.items)[z]].selected = false
                        }
                        menuItem.selected = true
                        aMenu.selectedItem = menuItem
                    }

                }

            }
        }

    },

    setupListeners: function() {
        window.addEventListener('touchstart', HID.handleTouchDown, false);
        window.addEventListener('touchmove', HID.handleTouchMove, false);
        window.addEventListener('touchend', HID.handleTouchUp, false);
        window.addEventListener("gamepadconnected", function(e) {
            setTimeout(function() {
                if (typeof HID.gamepads === "undefined") {
                    HID.gamepads = [];
                }
                HID.gamepads.push(e.gamepad.index)
                actions.alert("gamepad connected!")
            }, 1200);
        });
        window.addEventListener("gamepaddisconnected", function(e) {
            HID.gamepads = [];
            HID.cleanInputs();
            HID.clearInputs();
            actions.alert("gamepad disconnected.")
        });

        //the following function checks if there is an already connected gamepad
        setTimeout(function() {
            if(navigator.getGamepads().length>0){
               var gamepads = navigator.getGamepads()
               for(var pad=0; pad<gamepads.length ; pad++){
                   if (!(typeof gamepads[pad] === "undefined")) {
                      HID.gamepads = [];
                  }
               }
               for(var pad=0; pad<gamepads.length ; pad++){
                   if (!(typeof gamepads[pad] === "undefined")) {
                      HID.gamepads.push(pad)
                      actions.alert("gamepad connected!")
                  }
               }
            }
        }, 1000);

    },

    setup: function(screentosetup) {
        this.setupTouchZone(screentosetup);
        this.setupListeners();
        this.setupKeyboardListeners();
    }

};

// MIT LICENSE
// Copyright (c) 2016 Érico Vieira Porto
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the
// Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// You can't claim ownership, use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell any software, images or
// documents that includes characters, assets, or story elements
// of the game distributed along with this engine.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
<commit_msg>fix typo in handle events<commit_after>// hid.js
//  This code represents the Human Interaction Device, which
// means things the user really interacts with.
//  Code related to the touch display, gamepad, keyboard, all
// this should be here. This function makes then the resulting
// of this interaction something the rest of the code can use.

var HID = {
    scale: 1,
    bsz: 96,
    offset: 416,
    axeNotNull: 0.4,
    inputs: {
        up: {
            letter: 'W',
            active: false,
            mapX: 96,
            mapY: 0,
            color: '#111111',
            gamepad: function(pad) {
               if (!(typeof pad === "undefined")){
                   return (pad.axes[1] < -HID.axeNotNull);
               } else {
                   return false
               }
            }
        },
        left: {
            letter: 'A',
            active: false,
            mapX: 0,
            mapY: 96,
            color: '#111111',
            gamepad: function(pad) {
                if (!(typeof pad === "undefined")){
                    return (pad.axes[0] < -HID.axeNotNull);
                } else {
                    return false
                }
            }
        },
        down: {
            letter: 'S',
            active: false,
            mapX: 96,
            mapY: 192,
            color: '#111111',
            gamepad: function(pad) {
                if (!(typeof pad === "undefined")){
                    return (pad.axes[1] > HID.axeNotNull);
                } else {
                    return false
                }
            }
        },
        right: {
            letter: 'D',
            active: false,
            mapX: 192,
            mapY: 96,
            color: '#111111',
            gamepad: function(pad) {
                if (!(typeof pad === "undefined")) {
                    return (pad.axes[0] > HID.axeNotNull);
                } else {
                    return false
                }
            }
        },
        accept: {
            letter: 'I',
            active: false,
            mapX: 304,
            mapY: 32,
            color: '#127826',
            gamepad: function(pad) {
                if (!(typeof pad === "undefined")) {
                    return pad.buttons[0].pressed;
                } else {
                    return false
                }
            }
        },
        cancel: {
            letter: 'J',
            active: false,
            mapX: 304,
            mapY: 160,
            color: '#ed1c24',
            gamepad: function(pad) {
                if (!(typeof pad === "undefined")){
                    return pad.buttons[1].pressed;
                } else {
                    return false
                }
            }
        }
    },
    debugKeys: {
        resize: {
            letter: '0',
            toggle: function() {
                screen.resize()
            }
        },
        layer1: {
            letter: '1',
            toggle: function() {
                debug.showLayer.layer1 = !debug.showLayer.layer1
                if (debug.showLayer.layer1) {
                    actions.alert("layer1 shown.")
                } else {
                    actions.alert("layer1 hidden.")
                }
            }
        },
        layer2: {
            letter: '2',
            toggle: function() {
                debug.showLayer.layer2 = !debug.showLayer.layer2
                if (debug.showLayer.layer2) {
                    actions.alert("layer2 shown.")
                } else {
                    actions.alert("layer2 hidden.")
                }
            }
        },
        layer3: {
            letter: '3',
            toggle: function() {
                debug.showLayer.layer3 = !debug.showLayer.layer3
                if (debug.showLayer.layer3) {
                    actions.alert("layer3 shown.")
                } else {
                    actions.alert("layer3 hidden.")
                }
            }
        },
        layer4: {
            letter: '4',
            toggle: function() {
                debug.showLayer.layer4 = !debug.showLayer.layer4
                if (debug.showLayer.layer4) {
                    actions.alert("layer4 shown.")
                } else {
                    actions.alert("layer4 hidden.")
                }
            }
        }
    },

    processGamepad: function() {
        if (!(typeof HID.gamepads === "undefined")) {

            if(HID.gamepads.length > 0){
                for (var i = 0; i < HID.gamepads.length; ++i) {
                    var pad = navigator.getGamepads()[HID.gamepads[i]];

                    if (!(typeof pad === "undefined")) {
                        for (var tag in HID.inputs) {
                            var HIDItem = HID.inputs[tag];

                            if (HIDItem.gamepad(pad)) {
                                HIDItem.active = true
                            } else {
                                HIDItem.active = false
                            }
                        }
                    }
                }
            }
        }
    },

    clearInputs: function() {
        for (var tag in HID.inputs) {
            var HIDItem = HID.inputs[tag];
            HIDItem.release = false;
        }
    },

    cleanInputs: function() {
        for (var tag in HID.inputs) {
            var HIDItem = HID.inputs[tag];
            HIDItem.active = false;
        }
    },

    log: function(text) {
        var tag = document.getElementById("log");
        tag.innerHTML = text;
    },

    keyDown: function(e) {
        var evtobj = window.event ? event : e;
        var unicode = evtobj.charCode ? evtobj.charCode : evtobj.keyCode;
        var actualkey = String.fromCharCode(unicode);

        for (var key in HID.inputs) {
            var HIDItem = HID.inputs[key];

            if (actualkey == HIDItem.letter) {
                HIDItem.active = true;
            }
        }

    },

    keyUp: function(e) {
        var evtobj = window.event ? event : e;
        var unicode = evtobj.charCode ? evtobj.charCode : evtobj.keyCode;
        var actualkey = String.fromCharCode(unicode);

        for (var key in HID.inputs) {
            var HIDItem = HID.inputs[key];

            if (actualkey == HIDItem.letter) {
                HIDItem.active = false;
            }
        }

        for (var key in HID.debugKeys) {
            var HIDItem = HID.debugKeys[key];

            if (actualkey == HIDItem.letter) {
                HIDItem.toggle();
            }
        }

    },

    setupTouchZone: function(_screen) {
        this.screen = _screen
        this.touchzone = this.screen.canvas
    },

    setupKeyboardListeners: function() {
        document.onkeydown = HID.keyDown;
        document.onkeyup = HID.keyUp;
    },

    handleTouchDown: function(evnt) {
        evnt.preventDefault();
        HID.processTouches(evnt.touches, "down");
    },

    handleTouchMove: function(evnt) {
        evnt.preventDefault();
        HID.processTouches(evnt.touches, "move");
    },

    handleTouchUp: function(evnt) {
        evnt.preventDefault();
        HID.processTouches(evnt.touches, "up");
    },

    processTouches: function(touches, kind) {
        //HID.log("type:" +kind+" seed: "+Date());
        for (var tag in HID.inputs) {

            var HIDItem = HID.inputs[tag];
            var touched = false;

            var offsetTop = this.screen.canvas.offsetTop,
                offsetLeft = this.screen.canvas.offsetLeft;
            scale = this.screen.currentWidth / this.screen.WIDTH;

            for (var i = 0; i < touches.length; i++) {

                var position = {
                    x: ((touches[i].pageX - offsetLeft) / scale),
                    y: ((touches[i].pageY - offsetTop) / scale)
                };

                if (position.x > HIDItem.mapX && position.y > this.offset + HIDItem.mapY) {
                    if (position.x < HIDItem.mapX + this.bsz && position.y < this.offset + HIDItem.mapY + this.bsz) {
                        touched = true;
                    }
                }

            }

            if (touched) {
                HIDItem.active = true;
                if (kind == "down") {
                    HIDItem.release = true;
                }
            } else {
                HIDItem.active = false;
            }

        }

        for (var menuTag in menus.allMenus) {
            var aMenu = menus.allMenus[menuTag]

            if (aMenu.enabled && !aMenu.wait) {
                var maxItems = aMenu.itemsLength

                for (var menuItemTag in aMenu.items) {
                    var menuItem = aMenu.items[menuItemTag];
                    var touched = false;

                    var offsetTop = this.screen.canvas.offsetTop,
                        offsetLeft = this.screen.canvas.offsetLeft;
                    scale = this.screen.currentWidth / this.screen.WIDTH;

                    for (var i = 0; i < touches.length; i++) {

                        var position = {
                            x: ((touches[i].pageX - offsetLeft) / scale),
                            y: ((touches[i].pageY - offsetTop) / scale)
                        };

                        if (position.x > (screen.GSTARTX + aMenu.drawx) && position.y > (screen.GSTARTY + aMenu.drawy + menuItem.itemy - 32)) {
                            if (position.x < (screen.GSTARTX + aMenu.drawx + aMenu.width) && position.y < (screen.GSTARTY + aMenu.drawy + menuItem.itemy)) {
                                touched = true;
                            }
                        }

                    }

                    if (touched) {
                        if (kind == "down" && menuItem.selected) {
                            HID.inputs["accept"].active = true
                            return;
                        }
                        for (var z = 0; z < maxItems; z += 1) {
                            aMenu.items[Object.keys(aMenu.items)[z]].selected = false
                        }
                        menuItem.selected = true
                        aMenu.selectedItem = menuItem
                    }

                }

            }
        }

    },

    setupListeners: function() {
        window.addEventListener('touchstart', HID.handleTouchDown, false);
        window.addEventListener('touchmove', HID.handleTouchMove, false);
        window.addEventListener('touchend', HID.handleTouchUp, false);
        window.addEventListener("gamepadconnected", function(e) {
            setTimeout(function() {
                if (typeof HID.gamepads === "undefined") {
                    HID.gamepads = [];
                }
                HID.gamepads.push(e.gamepad.index)
                actions.alert("gamepad connected!")
            }, 1200);
        });
        window.addEventListener("gamepaddisconnected", function(e) {
            HID.gamepads = [];
            HID.cleanInputs();
            HID.clearInputs();
            actions.alert("gamepad disconnected.")
        });

        //the following function checks if there is an already connected gamepad
        setTimeout(function() {
            if(navigator.getGamepads().length>0){
               var gamepads = navigator.getGamepads()
               for(var pad=0; pad<gamepads.length ; pad++){
                   if (!(typeof gamepads[pad] === "undefined")) {
                      HID.gamepads = [];
                  }
               }
               for(var pad=0; pad<gamepads.length ; pad++){
                   if (!(typeof gamepads[pad] === "undefined")) {
                      HID.gamepads.push(pad)
                      actions.alert("gamepad connected!")
                  }
               }
            }
        }, 1000);

    },

    setup: function(screentosetup) {
        this.setupTouchZone(screentosetup);
        this.setupListeners();
        this.setupKeyboardListeners();
    }

};

// MIT LICENSE
// Copyright (c) 2016 Érico Vieira Porto
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the
// Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// You can't claim ownership, use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell any software, images or
// documents that includes characters, assets, or story elements
// of the game distributed along with this engine.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
<|endoftext|>