├── .gitignore ├── resources ├── Logs.png ├── run_start.gif └── multiplayer.gif ├── views ├── 404.html └── index.html ├── public ├── css │ └── client.css └── js │ └── socket.io.js ├── LICENSE ├── package.json ├── README.md ├── server.js └── src ├── index.js ├── scene.js └── fpscontrols.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /resources/Logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juniorxsound/THREE.Multiplayer/HEAD/resources/Logs.png -------------------------------------------------------------------------------- /resources/run_start.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juniorxsound/THREE.Multiplayer/HEAD/resources/run_start.gif -------------------------------------------------------------------------------- /resources/multiplayer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juniorxsound/THREE.Multiplayer/HEAD/resources/multiplayer.gif -------------------------------------------------------------------------------- /views/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Page not found 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Page not found

17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /public/css/client.css: -------------------------------------------------------------------------------- 1 | body{ 2 | padding: 0 !important; 3 | margin: auto !important; 4 | overflow-x: hidden; 5 | overflow-y: scroll; 6 | } 7 | .dashboard{ 8 | text-align: center; 9 | font-size: 10pt; 10 | color: gray; 11 | } 12 | #gui_overlays{ 13 | position: absolute; 14 | top: 0; left: 0; 15 | width: 100%; 16 | font-size: 20pt; 17 | color: white; 18 | } 19 | #information{ 20 | position: absolute; 21 | top: 20px; right: 20px; 22 | } 23 | #contribute{ 24 | position: absolute; 25 | top: 65px; right: 20px; 26 | } 27 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Three.js Multiplayer Client 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Or Fleisher 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "death_gl", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "start": "concurrently \"nodemon server.js\" \"watchify src/index.js -o public/js/bundle.js -v\"", 8 | "build": "browserify src/index.js | uglifyjs -mc warnings=false > public/js/bundle.min.js", 9 | "test": "npm run build" 10 | }, 11 | "author": "Or Fleisher ", 12 | "license": "MIT", 13 | "dependencies": { 14 | "ejs": "^2.5.7", 15 | "event-emitter-es6": "^1.1.5", 16 | "express": "^4.16.2", 17 | "gsap": "^1.20.3", 18 | "http": "0.0.0", 19 | "socket.io": "^2.0.4", 20 | "three": "^0.89.0" 21 | }, 22 | "devDependencies": { 23 | "babel-core": "^6.26.0", 24 | "babel-preset-env": "^1.6.1", 25 | "babelify": "^8.0.0", 26 | "browserify": "^15.2.0", 27 | "concurrently": "^3.5.1", 28 | "glslify": "^6.1.0", 29 | "nodemon": "^1.14.12", 30 | "uglify-js": "^3.3.9", 31 | "watchify": "^3.10.0" 32 | }, 33 | "browserify": { 34 | "transform": [ 35 | [ 36 | "babelify", 37 | { 38 | "presets": [ 39 | "env" 40 | ] 41 | } 42 | ], 43 | "glslify" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THREE Multiplayer 2 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 3 | 4 | > Looking for a React three fiber multiplayer template? check out [R3F.Multiplayer](https://github.com/juniorxsound/R3F.Multiplayer) 5 | 6 | My boilerplate Node.js server and client setup for Three.js multiplayer projects using Socket.io 7 | - [Installation](#installation) 8 | - [Usage](#usage) 9 | - [Pushing to Heroku](#pushing-to-heroku) 10 | 11 | ![Multiplayer](https://github.com/juniorxsound/THREE-Multiplayer/blob/master/resources/multiplayer.gif) 12 | 13 | ## Installation 14 | 1. Clone the repo, e.g ```git clone https://github.com/juniorxsound/THREE-Multiplayer.git``` 15 | 1. Run ```npm install``` to install all the dependencies 16 | 17 | > Tested on macOS 10.13.3 using Node.js v8.9.3 and npm v5.6.0 18 | 19 | ## Usage 20 | Use ```npm run start``` to start the server and bundler 21 | 22 | ![NPM](https://github.com/juniorxsound/THREE-Multiplayer/blob/master/resources/run_start.gif) 23 | 24 | The start script launches: 25 | - ```nodemon``` Which restarts the server on every change (port: 1989) 26 | - ```watchify``` Which bundles the client code from ```src/``` on every change to ```./public/js/bundle.js``` 27 | 28 | ![Server Log](https://github.com/juniorxsound/THREE-Multiplayer/blob/master/resources/Logs.png) 29 | 30 | On connection each client recives it's uniqe ID and on every movement broadcasts to all the other clients all the locations of everyone connected 31 | ```js 32 | { 33 | 'some-user-id': { 34 | position: [0.4, 1.4, -0.5], 35 | rotation: [0, 0, 0] 36 | } 37 | } 38 | ``` 39 | 40 | You can also run ```npm run build``` to bundle and minify the client code to ```./public/js/bundle.min.js``` 41 | 42 | Browserify is setup to transform both ES6 Javascript and ```glslify``` for GLSL shader bundling ([example](https://github.com/juniorxsound/DepthKit.js) of a project that uses ```glslify```) 43 | 44 | ## Pushing to Heroku 45 | [This is a detailed tutorial](https://devcenter.heroku.com/articles/getting-started-with-nodejs#introduction) of how to push your code to Heroku through Github to serve your experience publicly 46 | 47 | > Special thanks to [Dror Ayalon](https://github.com/dodiku) 48 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | //////EXPRESS//////// 4 | const express = require('express'); 5 | const app = express(); 6 | 7 | ////////HTTP///////// 8 | const http = require('http').createServer(app); 9 | 10 | //Port and server setup 11 | const port = process.env.PORT || 1989; 12 | 13 | //Server 14 | const server = app.listen(port); 15 | 16 | //Console the port 17 | console.log('Server is running localhost on port: ' + port ); 18 | 19 | /////SOCKET.IO/////// 20 | const io = require('socket.io').listen(server); 21 | 22 | ////////EJS////////// 23 | const ejs = require('ejs'); 24 | 25 | //Setup the views folder 26 | app.set("views", __dirname + '/views'); 27 | 28 | //Setup ejs, so I can write HTML(: 29 | app.engine('.html', ejs.__express); 30 | app.set('view-engine', 'html'); 31 | 32 | //Setup the public client folder 33 | app.use(express.static(__dirname + '/public')); 34 | 35 | let clients = {} 36 | 37 | //Socket setup 38 | io.on('connection', client=>{ 39 | 40 | console.log('User ' + client.id + ' connected, there are ' + io.engine.clientsCount + ' clients connected'); 41 | 42 | //Add a new client indexed by his id 43 | clients[client.id] = { 44 | position: [0, 0, 0], 45 | rotation: [0, 0, 0] 46 | } 47 | 48 | //Make sure to send the client it's ID 49 | client.emit('introduction', client.id, io.engine.clientsCount, Object.keys(clients)); 50 | 51 | //Update everyone that the number of users has changed 52 | io.sockets.emit('newUserConnected', io.engine.clientsCount, client.id, Object.keys(clients)); 53 | 54 | client.on('move', (pos)=>{ 55 | 56 | clients[client.id].position = pos; 57 | io.sockets.emit('userPositions', clients); 58 | 59 | }); 60 | 61 | //Handle the disconnection 62 | client.on('disconnect', ()=>{ 63 | 64 | //Delete this client from the object 65 | delete clients[client.id]; 66 | 67 | io.sockets.emit('userDisconnected', io.engine.clientsCount, client.id, Object.keys(clients)); 68 | 69 | console.log('User ' + client.id + ' dissconeted, there are ' + io.engine.clientsCount + ' clients connected'); 70 | 71 | }); 72 | 73 | }); 74 | 75 | ///////////////////// 76 | //////ROUTER///////// 77 | ///////////////////// 78 | 79 | //Client view 80 | app.get('/', (req, res) => { 81 | 82 | res.render('index.html'); 83 | 84 | }); 85 | 86 | //404 view 87 | app.get('/*', (req, res) => { 88 | 89 | res.render('404.html'); 90 | 91 | }); 92 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Scene from './scene'; 2 | import * as THREE from 'three'; 3 | 4 | //A socket.io instance 5 | const socket = io(); 6 | 7 | //One WebGL context to rule them all ! 8 | let glScene = new Scene(); 9 | let id; 10 | let instances = []; 11 | let clients = new Object(); 12 | 13 | glScene.on('userMoved', ()=>{ 14 | socket.emit('move', [glScene.camera.position.x, glScene.camera.position.y, glScene.camera.position.z]); 15 | }); 16 | 17 | //On connection server sends the client his ID 18 | socket.on('introduction', (_id, _clientNum, _ids)=>{ 19 | 20 | for(let i = 0; i < _ids.length; i++){ 21 | if(_ids[i] != _id){ 22 | clients[_ids[i]] = { 23 | mesh: new THREE.Mesh( 24 | new THREE.BoxGeometry(1,1,1), 25 | new THREE.MeshNormalMaterial() 26 | ) 27 | } 28 | 29 | //Add initial users to the scene 30 | glScene.scene.add(clients[_ids[i]].mesh); 31 | } 32 | } 33 | 34 | console.log(clients); 35 | 36 | id = _id; 37 | console.log('My ID is: ' + id); 38 | 39 | }); 40 | 41 | socket.on('newUserConnected', (clientCount, _id, _ids)=>{ 42 | console.log(clientCount + ' clients connected'); 43 | let alreadyHasUser = false; 44 | for(let i = 0; i < Object.keys(clients).length; i++){ 45 | if(Object.keys(clients)[i] == _id){ 46 | alreadyHasUser = true; 47 | break; 48 | } 49 | } 50 | if(_id != id && !alreadyHasUser){ 51 | console.log('A new user connected with the id: ' + _id); 52 | clients[_id] = { 53 | mesh: new THREE.Mesh( 54 | new THREE.BoxGeometry(1,1,1), 55 | new THREE.MeshNormalMaterial() 56 | ) 57 | } 58 | 59 | //Add initial users to the scene 60 | glScene.scene.add(clients[_id].mesh); 61 | } 62 | 63 | }); 64 | 65 | socket.on('userDisconnected', (clientCount, _id, _ids)=>{ 66 | //Update the data from the server 67 | document.getElementById('numUsers').textContent = clientCount; 68 | 69 | if(_id != id){ 70 | console.log('A user disconnected with the id: ' + _id); 71 | glScene.scene.remove(clients[_id].mesh); 72 | delete clients[_id]; 73 | } 74 | }); 75 | 76 | socket.on('connect', ()=>{}); 77 | 78 | //Update when one of the users moves in space 79 | socket.on('userPositions', _clientProps =>{ 80 | // console.log('Positions of all users are ', _clientProps, id); 81 | // console.log(Object.keys(_clientProps)[0] == id); 82 | for(let i = 0; i < Object.keys(_clientProps).length; i++){ 83 | if(Object.keys(_clientProps)[i] != id){ 84 | 85 | //Store the values 86 | let oldPos = clients[Object.keys(_clientProps)[i]].mesh.position; 87 | let newPos = _clientProps[Object.keys(_clientProps)[i]].position; 88 | 89 | //Create a vector 3 and lerp the new values with the old values 90 | let lerpedPos = new THREE.Vector3(); 91 | lerpedPos.x = THREE.Math.lerp(oldPos.x, newPos[0], 0.3); 92 | lerpedPos.y = THREE.Math.lerp(oldPos.y, newPos[1], 0.3); 93 | lerpedPos.z = THREE.Math.lerp(oldPos.z, newPos[2], 0.3); 94 | 95 | //Set the position 96 | clients[Object.keys(_clientProps)[i]].mesh.position.set(lerpedPos.x, lerpedPos.y, lerpedPos.z); 97 | } 98 | } 99 | }); 100 | -------------------------------------------------------------------------------- /src/scene.js: -------------------------------------------------------------------------------- 1 | //Three.js 2 | import * as THREE from 'three'; 3 | 4 | import FirstPersonControls from './fpscontrols'; 5 | FirstPersonControls(THREE); 6 | 7 | // Event emitter implementation for ES6 8 | import EventEmitter from 'event-emitter-es6'; 9 | 10 | class Scene extends EventEmitter { 11 | constructor(domElement = document.getElementById('gl_context'), 12 | _width = window.innerWidth, 13 | _height = window.innerHeight, 14 | hasControls = true, 15 | clearColor = 'black'){ 16 | 17 | //Since we extend EventEmitter we need to instance it from here 18 | super(); 19 | 20 | //THREE scene 21 | this.scene = new THREE.Scene(); 22 | 23 | //Utility 24 | this.width = _width; 25 | this.height = _height; 26 | 27 | //THREE Camera 28 | this.camera = new THREE.PerspectiveCamera(50, this.width / this.height, 0.1, 1000); 29 | 30 | //THREE WebGL renderer 31 | this.renderer = new THREE.WebGLRenderer({ 32 | antialiasing: true 33 | }); 34 | 35 | this.renderer.setClearColor(new THREE.Color(clearColor)); 36 | 37 | this.renderer.setSize(this.width, this.height); 38 | 39 | //Push the canvas to the DOM 40 | domElement.append(this.renderer.domElement); 41 | 42 | if(hasControls){ 43 | this.controls = new THREE.FirstPersonControls(this.camera, this.renderer.domElement); 44 | this.controls.lookSpeed = 0.05; 45 | } 46 | 47 | //Setup event listeners for events and handle the states 48 | window.addEventListener('resize', e => this.onWindowResize(e), false); 49 | domElement.addEventListener('mouseenter', e => this.onEnterCanvas(e), false); 50 | domElement.addEventListener('mouseleave', e => this.onLeaveCanvas(e), false); 51 | window.addEventListener('keydown', e => this.onKeyDown(e), false); 52 | 53 | this.helperGrid = new THREE.GridHelper( 10, 10 ); 54 | this.helperGrid.position.y = -0.5; 55 | this.scene.add(this.helperGrid); 56 | this.clock = new THREE.Clock(); 57 | 58 | this.update(); 59 | 60 | } 61 | 62 | drawUsers(positions, id){ 63 | for(let i = 0; i < Object.keys(positions).length; i++){ 64 | if(Object.keys(positions)[i] != id){ 65 | this.users[i].position.set(positions[Object.keys(positions)[i]].position[0], 66 | positions[Object.keys(positions)[i]].position[1], 67 | positions[Object.keys(positions)[i]].position[2]); 68 | } 69 | } 70 | } 71 | 72 | update(){ 73 | requestAnimationFrame(() => this.update()); 74 | this.controls.update(this.clock.getDelta()); 75 | this.controls.target = new THREE.Vector3(0,0,0); 76 | this.render(); 77 | } 78 | 79 | render() { 80 | this.renderer.render(this.scene, this.camera); 81 | } 82 | 83 | onWindowResize(e) { 84 | this.width = window.innerWidth; 85 | this.height = Math.floor(window.innerHeight - (window.innerHeight * 0.3)); 86 | this.camera.aspect = this.width / this.height; 87 | this.camera.updateProjectionMatrix(); 88 | this.renderer.setSize(this.width, this.height); 89 | } 90 | 91 | onLeaveCanvas(e){ 92 | this.controls.enabled = false; 93 | } 94 | onEnterCanvas(e){ 95 | this.controls.enabled = true; 96 | } 97 | onKeyDown(e){ 98 | this.emit('userMoved'); 99 | } 100 | } 101 | 102 | export default Scene; 103 | -------------------------------------------------------------------------------- /src/fpscontrols.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author alteredq / http://alteredqualia.com/ 4 | * @author paulirish / http://paulirish.com/ 5 | */ 6 | module.exports = (THREE) =>{ 7 | 8 | THREE.FirstPersonControls = function ( object, domElement ) { 9 | 10 | this.object = object; 11 | this.target = new THREE.Vector3( 0, 0, 0 ); 12 | 13 | this.domElement = ( domElement !== undefined ) ? domElement : document; 14 | 15 | this.enabled = true; 16 | 17 | this.movementSpeed = 1.0; 18 | this.lookSpeed = 0.005; 19 | 20 | this.lookVertical = true; 21 | this.autoForward = false; 22 | 23 | this.activeLook = true; 24 | 25 | this.heightSpeed = false; 26 | this.heightCoef = 1.0; 27 | this.heightMin = 0.0; 28 | this.heightMax = 1.0; 29 | 30 | this.constrainVertical = false; 31 | this.verticalMin = 0; 32 | this.verticalMax = Math.PI; 33 | 34 | this.autoSpeedFactor = 0.0; 35 | 36 | this.mouseX = 0; 37 | this.mouseY = 0; 38 | 39 | this.lat = 0; 40 | this.lon = 0; 41 | this.phi = 0; 42 | this.theta = 0; 43 | 44 | this.moveForward = false; 45 | this.moveBackward = false; 46 | this.moveLeft = false; 47 | this.moveRight = false; 48 | 49 | this.mouseDragOn = false; 50 | 51 | this.viewHalfX = 0; 52 | this.viewHalfY = 0; 53 | 54 | this.lockYPos = true; 55 | 56 | if ( this.domElement !== document ) { 57 | 58 | this.domElement.setAttribute( 'tabindex', - 1 ); 59 | 60 | } 61 | 62 | // 63 | 64 | this.handleResize = function () { 65 | 66 | if ( this.domElement === document ) { 67 | 68 | this.viewHalfX = window.innerWidth / 2; 69 | this.viewHalfY = window.innerHeight / 2; 70 | 71 | } else { 72 | 73 | this.viewHalfX = this.domElement.offsetWidth / 2; 74 | this.viewHalfY = this.domElement.offsetHeight / 2; 75 | 76 | } 77 | 78 | }; 79 | 80 | this.onMouseDown = function ( event ) { 81 | 82 | if ( this.domElement !== document ) { 83 | 84 | this.domElement.focus(); 85 | 86 | } 87 | 88 | event.preventDefault(); 89 | event.stopPropagation(); 90 | 91 | if ( this.activeLook ) { 92 | 93 | switch ( event.button ) { 94 | 95 | case 0: this.moveForward = true; break; 96 | case 2: this.moveBackward = true; break; 97 | 98 | } 99 | 100 | } 101 | 102 | this.mouseDragOn = true; 103 | 104 | }; 105 | 106 | this.onMouseUp = function ( event ) { 107 | 108 | event.preventDefault(); 109 | event.stopPropagation(); 110 | 111 | if ( this.activeLook ) { 112 | 113 | switch ( event.button ) { 114 | 115 | case 0: this.moveForward = false; break; 116 | case 2: this.moveBackward = false; break; 117 | 118 | } 119 | 120 | } 121 | 122 | this.mouseDragOn = false; 123 | 124 | }; 125 | 126 | this.onMouseMove = function ( event ) { 127 | 128 | if ( this.domElement === document ) { 129 | 130 | this.mouseX = event.pageX - this.viewHalfX; 131 | this.mouseY = event.pageY - this.viewHalfY; 132 | 133 | } else { 134 | 135 | this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX; 136 | this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY; 137 | 138 | } 139 | 140 | }; 141 | 142 | this.onKeyDown = function ( event ) { 143 | 144 | //event.preventDefault(); 145 | 146 | switch ( event.keyCode ) { 147 | 148 | case 38: /*up*/ 149 | case 87: /*W*/ this.moveForward = true; break; 150 | 151 | case 37: /*left*/ 152 | case 65: /*A*/ this.moveLeft = true; break; 153 | 154 | case 40: /*down*/ 155 | case 83: /*S*/ this.moveBackward = true; break; 156 | 157 | case 39: /*right*/ 158 | case 68: /*D*/ this.moveRight = true; break; 159 | 160 | case 82: /*R*/ this.moveUp = true; break; 161 | case 70: /*F*/ this.moveDown = true; break; 162 | 163 | } 164 | 165 | }; 166 | 167 | this.onKeyUp = function ( event ) { 168 | 169 | switch ( event.keyCode ) { 170 | 171 | case 38: /*up*/ 172 | case 87: /*W*/ this.moveForward = false; break; 173 | 174 | case 37: /*left*/ 175 | case 65: /*A*/ this.moveLeft = false; break; 176 | 177 | case 40: /*down*/ 178 | case 83: /*S*/ this.moveBackward = false; break; 179 | 180 | case 39: /*right*/ 181 | case 68: /*D*/ this.moveRight = false; break; 182 | 183 | case 82: /*R*/ this.moveUp = false; break; 184 | case 70: /*F*/ this.moveDown = false; break; 185 | 186 | } 187 | 188 | }; 189 | 190 | this.update = function( delta ) { 191 | 192 | if ( this.enabled === false ) return; 193 | 194 | if ( this.heightSpeed ) { 195 | 196 | var y = THREE.Math.clamp( this.object.position.y, this.heightMin, this.heightMax ); 197 | var heightDelta = y - this.heightMin; 198 | 199 | this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef ); 200 | 201 | } else { 202 | 203 | this.autoSpeedFactor = 0.0; 204 | 205 | } 206 | 207 | var actualMoveSpeed = delta * this.movementSpeed; 208 | 209 | if ( this.moveForward || ( this.autoForward && ! this.moveBackward ) ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) ); 210 | if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed ); 211 | 212 | if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed ); 213 | if ( this.moveRight ) this.object.translateX( actualMoveSpeed ); 214 | 215 | if ( this.moveUp ) this.object.translateY( actualMoveSpeed ); 216 | if ( this.moveDown ) this.object.translateY( - actualMoveSpeed ); 217 | 218 | var actualLookSpeed = delta * this.lookSpeed; 219 | 220 | if ( ! this.activeLook ) { 221 | 222 | actualLookSpeed = 0; 223 | 224 | } 225 | 226 | var verticalLookRatio = 1; 227 | 228 | if ( this.constrainVertical ) { 229 | 230 | verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin ); 231 | 232 | } 233 | 234 | this.lon += this.mouseX * actualLookSpeed; 235 | if ( this.lookVertical ) this.lat -= this.mouseY * actualLookSpeed * verticalLookRatio; 236 | 237 | this.lat = Math.max( - 85, Math.min( 85, this.lat ) ); 238 | this.phi = THREE.Math.degToRad( 90 - this.lat ); 239 | 240 | this.theta = THREE.Math.degToRad( this.lon ); 241 | 242 | if ( this.constrainVertical ) { 243 | 244 | this.phi = THREE.Math.mapLinear( this.phi, 0, Math.PI, this.verticalMin, this.verticalMax ); 245 | 246 | } 247 | 248 | var targetPosition = this.target, 249 | position = this.object.position; 250 | 251 | targetPosition.x = position.x + 100 * Math.sin( this.phi ) * Math.cos( this.theta ); 252 | targetPosition.y = position.y + 100 * Math.cos( this.phi ); 253 | targetPosition.z = position.z + 100 * Math.sin( this.phi ) * Math.sin( this.theta ); 254 | 255 | this.object.lookAt( targetPosition ); 256 | if(this.lockYPos) this.object.position.y = 0; 257 | 258 | }; 259 | 260 | function contextmenu( event ) { 261 | 262 | event.preventDefault(); 263 | 264 | } 265 | 266 | this.dispose = function() { 267 | 268 | this.domElement.removeEventListener( 'contextmenu', contextmenu, false ); 269 | this.domElement.removeEventListener( 'mousedown', _onMouseDown, false ); 270 | this.domElement.removeEventListener( 'mousemove', _onMouseMove, false ); 271 | this.domElement.removeEventListener( 'mouseup', _onMouseUp, false ); 272 | 273 | window.removeEventListener( 'keydown', _onKeyDown, false ); 274 | window.removeEventListener( 'keyup', _onKeyUp, false ); 275 | 276 | }; 277 | 278 | var _onMouseMove = bind( this, this.onMouseMove ); 279 | var _onMouseDown = bind( this, this.onMouseDown ); 280 | var _onMouseUp = bind( this, this.onMouseUp ); 281 | var _onKeyDown = bind( this, this.onKeyDown ); 282 | var _onKeyUp = bind( this, this.onKeyUp ); 283 | 284 | this.domElement.addEventListener( 'contextmenu', contextmenu, false ); 285 | this.domElement.addEventListener( 'mousemove', _onMouseMove, false ); 286 | this.domElement.addEventListener( 'mousedown', _onMouseDown, false ); 287 | this.domElement.addEventListener( 'mouseup', _onMouseUp, false ); 288 | 289 | window.addEventListener( 'keydown', _onKeyDown, false ); 290 | window.addEventListener( 'keyup', _onKeyUp, false ); 291 | 292 | function bind( scope, fn ) { 293 | 294 | return function () { 295 | 296 | fn.apply( scope, arguments ); 297 | 298 | }; 299 | 300 | } 301 | 302 | this.handleResize(); 303 | 304 | }; 305 | 306 | } 307 | -------------------------------------------------------------------------------- /public/js/socket.io.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.io=e():t.io=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return t[r].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t,e){"object"===("undefined"==typeof t?"undefined":o(t))&&(e=t,t=void 0),e=e||{};var n,r=i(t),s=r.source,u=r.id,h=r.path,f=p[u]&&h in p[u].nsps,l=e.forceNew||e["force new connection"]||!1===e.multiplex||f;return l?(c("ignoring socket cache for %s",s),n=a(s,e)):(p[u]||(c("new io instance for %s",s),p[u]=a(s,e)),n=p[u]),r.query&&!e.query&&(e.query=r.query),n.socket(r.path,e)}var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=n(1),s=n(7),a=n(13),c=n(3)("socket.io-client");t.exports=e=r;var p=e.managers={};e.protocol=s.protocol,e.connect=r,e.Manager=n(13),e.Socket=n(37)},function(t,e,n){(function(e){"use strict";function r(t,n){var r=t;n=n||e.location,null==t&&(t=n.protocol+"//"+n.host),"string"==typeof t&&("/"===t.charAt(0)&&(t="/"===t.charAt(1)?n.protocol+t:n.host+t),/^(https?|wss?):\/\//.test(t)||(i("protocol-less url %s",t),t="undefined"!=typeof n?n.protocol+"//"+t:"https://"+t),i("parse %s",t),r=o(t)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";var s=r.host.indexOf(":")!==-1,a=s?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+a+":"+r.port,r.href=r.protocol+"://"+a+(n&&n.port===r.port?"":":"+r.port),r}var o=n(2),i=n(3)("socket.io-client:url");t.exports=r}).call(e,function(){return this}())},function(t,e){var n=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,r=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];t.exports=function(t){var e=t,o=t.indexOf("["),i=t.indexOf("]");o!=-1&&i!=-1&&(t=t.substring(0,o)+t.substring(o,i).replace(/:/g,";")+t.substring(i,t.length));for(var s=n.exec(t||""),a={},c=14;c--;)a[r[c]]=s[c]||"";return o!=-1&&i!=-1&&(a.source=e,a.host=a.host.substring(1,a.host.length-1).replace(/;/g,":"),a.authority=a.authority.replace("[","").replace("]","").replace(/;/g,":"),a.ipv6uri=!0),a}},function(t,e,n){(function(r){function o(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type)||("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))}function i(t){var n=this.useColors;if(t[0]=(n?"%c":"")+this.namespace+(n?" %c":" ")+t[0]+(n?"%c ":" ")+"+"+e.humanize(this.diff),n){var r="color: "+this.color;t.splice(1,0,r,"color: inherit");var o=0,i=0;t[0].replace(/%[a-zA-Z%]/g,function(t){"%%"!==t&&(o++,"%c"===t&&(i=o))}),t.splice(i,0,r)}}function s(){return"object"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function a(t){try{null==t?e.storage.removeItem("debug"):e.storage.debug=t}catch(n){}}function c(){var t;try{t=e.storage.debug}catch(n){}return!t&&"undefined"!=typeof r&&"env"in r&&(t=r.env.DEBUG),t}function p(){try{return window.localStorage}catch(t){}}e=t.exports=n(5),e.log=s,e.formatArgs=i,e.save=a,e.load=c,e.useColors=o,e.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:p(),e.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],e.formatters.j=function(t){try{return JSON.stringify(t)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}},e.enable(c())}).call(e,n(4))},function(t,e){function n(){throw new Error("setTimeout has not been defined")}function r(){throw new Error("clearTimeout has not been defined")}function o(t){if(u===setTimeout)return setTimeout(t,0);if((u===n||!u)&&setTimeout)return u=setTimeout,setTimeout(t,0);try{return u(t,0)}catch(e){try{return u.call(null,t,0)}catch(e){return u.call(this,t,0)}}}function i(t){if(h===clearTimeout)return clearTimeout(t);if((h===r||!h)&&clearTimeout)return h=clearTimeout,clearTimeout(t);try{return h(t)}catch(e){try{return h.call(null,t)}catch(e){return h.call(this,t)}}}function s(){y&&l&&(y=!1,l.length?d=l.concat(d):m=-1,d.length&&a())}function a(){if(!y){var t=o(s);y=!0;for(var e=d.length;e;){for(l=d,d=[];++m1)for(var n=1;n100)){var e=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(t);if(e){var n=parseFloat(e[1]),r=(e[2]||"ms").toLowerCase();switch(r){case"years":case"year":case"yrs":case"yr":case"y":return n*u;case"days":case"day":case"d":return n*p;case"hours":case"hour":case"hrs":case"hr":case"h":return n*c;case"minutes":case"minute":case"mins":case"min":case"m":return n*a;case"seconds":case"second":case"secs":case"sec":case"s":return n*s;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return n;default:return}}}}function r(t){return t>=p?Math.round(t/p)+"d":t>=c?Math.round(t/c)+"h":t>=a?Math.round(t/a)+"m":t>=s?Math.round(t/s)+"s":t+"ms"}function o(t){return i(t,p,"day")||i(t,c,"hour")||i(t,a,"minute")||i(t,s,"second")||t+" ms"}function i(t,e,n){if(!(t0)return n(t);if("number"===i&&isNaN(t)===!1)return e["long"]?o(t):r(t);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(t))}},function(t,e,n){function r(){}function o(t){var n=""+t.type;return e.BINARY_EVENT!==t.type&&e.BINARY_ACK!==t.type||(n+=t.attachments+"-"),t.nsp&&"/"!==t.nsp&&(n+=t.nsp+","),null!=t.id&&(n+=t.id),null!=t.data&&(n+=JSON.stringify(t.data)),h("encoded %j as %s",t,n),n}function i(t,e){function n(t){var n=d.deconstructPacket(t),r=o(n.packet),i=n.buffers;i.unshift(r),e(i)}d.removeBlobs(t,n)}function s(){this.reconstructor=null}function a(t){var n=0,r={type:Number(t.charAt(0))};if(null==e.types[r.type])return u();if(e.BINARY_EVENT===r.type||e.BINARY_ACK===r.type){for(var o="";"-"!==t.charAt(++n)&&(o+=t.charAt(n),n!=t.length););if(o!=Number(o)||"-"!==t.charAt(n))throw new Error("Illegal attachments");r.attachments=Number(o)}if("/"===t.charAt(n+1))for(r.nsp="";++n;){var i=t.charAt(n);if(","===i)break;if(r.nsp+=i,n===t.length)break}else r.nsp="/";var s=t.charAt(n+1);if(""!==s&&Number(s)==s){for(r.id="";++n;){var i=t.charAt(n);if(null==i||Number(i)!=i){--n;break}if(r.id+=t.charAt(n),n===t.length)break}r.id=Number(r.id)}return t.charAt(++n)&&(r=c(r,t.substr(n))),h("decoded %s as %j",t,r),r}function c(t,e){try{t.data=JSON.parse(e)}catch(n){return u()}return t}function p(t){this.reconPack=t,this.buffers=[]}function u(){return{type:e.ERROR,data:"parser error"}}var h=n(3)("socket.io-parser"),f=n(8),l=n(9),d=n(11),y=n(12);e.protocol=4,e.types=["CONNECT","DISCONNECT","EVENT","ACK","ERROR","BINARY_EVENT","BINARY_ACK"],e.CONNECT=0,e.DISCONNECT=1,e.EVENT=2,e.ACK=3,e.ERROR=4,e.BINARY_EVENT=5,e.BINARY_ACK=6,e.Encoder=r,e.Decoder=s,r.prototype.encode=function(t,n){if(t.type!==e.EVENT&&t.type!==e.ACK||!l(t.data)||(t.type=t.type===e.EVENT?e.BINARY_EVENT:e.BINARY_ACK),h("encoding packet %j",t),e.BINARY_EVENT===t.type||e.BINARY_ACK===t.type)i(t,n);else{var r=o(t);n([r])}},f(s.prototype),s.prototype.add=function(t){var n;if("string"==typeof t)n=a(t),e.BINARY_EVENT===n.type||e.BINARY_ACK===n.type?(this.reconstructor=new p(n),0===this.reconstructor.reconPack.attachments&&this.emit("decoded",n)):this.emit("decoded",n);else{if(!y(t)&&!t.base64)throw new Error("Unknown type: "+t);if(!this.reconstructor)throw new Error("got binary data when not reconstructing a packet");n=this.reconstructor.takeBinaryData(t),n&&(this.reconstructor=null,this.emit("decoded",n))}},s.prototype.destroy=function(){this.reconstructor&&this.reconstructor.finishedReconstruction()},p.prototype.takeBinaryData=function(t){if(this.buffers.push(t),this.buffers.length===this.reconPack.attachments){var e=d.reconstructPacket(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null},p.prototype.finishedReconstruction=function(){this.reconPack=null,this.buffers=[]}},function(t,e,n){function r(t){if(t)return o(t)}function o(t){for(var e in r.prototype)t[e]=r.prototype[e];return t}t.exports=r,r.prototype.on=r.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},r.prototype.once=function(t,e){function n(){this.off(t,n),e.apply(this,arguments)}return n.fn=e,this.on(t,n),this},r.prototype.off=r.prototype.removeListener=r.prototype.removeAllListeners=r.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n=this._callbacks["$"+t];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var r,o=0;o0&&!this.encoding){var t=this.packetBuffer.shift();this.packet(t)}},r.prototype.cleanup=function(){h("cleanup");for(var t=this.subs.length,e=0;e=this._reconnectionAttempts)h("reconnect failed"),this.backoff.reset(),this.emitAll("reconnect_failed"),this.reconnecting=!1;else{var e=this.backoff.duration();h("will wait %dms before reconnect attempt",e),this.reconnecting=!0;var n=setTimeout(function(){t.skipReconnect||(h("attempting reconnect"),t.emitAll("reconnect_attempt",t.backoff.attempts),t.emitAll("reconnecting",t.backoff.attempts),t.skipReconnect||t.open(function(e){e?(h("reconnect attempt error"),t.reconnecting=!1,t.reconnect(),t.emitAll("reconnect_error",e.data)):(h("reconnect success"),t.onreconnect())}))},e);this.subs.push({destroy:function(){clearTimeout(n)}})}},r.prototype.onreconnect=function(){var t=this.backoff.attempts;this.reconnecting=!1,this.backoff.reset(),this.updateSocketIds(),this.emitAll("reconnect",t)}},function(t,e,n){t.exports=n(15),t.exports.parser=n(22)},function(t,e,n){(function(e){function r(t,n){if(!(this instanceof r))return new r(t,n);n=n||{},t&&"object"==typeof t&&(n=t,t=null),t?(t=u(t),n.hostname=t.host,n.secure="https"===t.protocol||"wss"===t.protocol,n.port=t.port,t.query&&(n.query=t.query)):n.host&&(n.hostname=u(n.host).host),this.secure=null!=n.secure?n.secure:e.location&&"https:"===location.protocol,n.hostname&&!n.port&&(n.port=this.secure?"443":"80"),this.agent=n.agent||!1,this.hostname=n.hostname||(e.location?location.hostname:"localhost"),this.port=n.port||(e.location&&location.port?location.port:this.secure?443:80),this.query=n.query||{},"string"==typeof this.query&&(this.query=h.decode(this.query)),this.upgrade=!1!==n.upgrade,this.path=(n.path||"/engine.io").replace(/\/$/,"")+"/",this.forceJSONP=!!n.forceJSONP,this.jsonp=!1!==n.jsonp,this.forceBase64=!!n.forceBase64,this.enablesXDR=!!n.enablesXDR,this.timestampParam=n.timestampParam||"t",this.timestampRequests=n.timestampRequests,this.transports=n.transports||["polling","websocket"],this.transportOptions=n.transportOptions||{},this.readyState="",this.writeBuffer=[],this.prevBufferLen=0,this.policyPort=n.policyPort||843,this.rememberUpgrade=n.rememberUpgrade||!1,this.binaryType=null,this.onlyBinaryUpgrades=n.onlyBinaryUpgrades,this.perMessageDeflate=!1!==n.perMessageDeflate&&(n.perMessageDeflate||{}),!0===this.perMessageDeflate&&(this.perMessageDeflate={}),this.perMessageDeflate&&null==this.perMessageDeflate.threshold&&(this.perMessageDeflate.threshold=1024),this.pfx=n.pfx||null,this.key=n.key||null,this.passphrase=n.passphrase||null,this.cert=n.cert||null,this.ca=n.ca||null,this.ciphers=n.ciphers||null,this.rejectUnauthorized=void 0===n.rejectUnauthorized||n.rejectUnauthorized,this.forceNode=!!n.forceNode;var o="object"==typeof e&&e;o.global===o&&(n.extraHeaders&&Object.keys(n.extraHeaders).length>0&&(this.extraHeaders=n.extraHeaders),n.localAddress&&(this.localAddress=n.localAddress)),this.id=null,this.upgrades=null,this.pingInterval=null,this.pingTimeout=null,this.pingIntervalTimer=null,this.pingTimeoutTimer=null,this.open()}function o(t){var e={};for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}var i=n(16),s=n(8),a=n(3)("engine.io-client:socket"),c=n(36),p=n(22),u=n(2),h=n(30);t.exports=r,r.priorWebsocketSuccess=!1,s(r.prototype),r.protocol=p.protocol,r.Socket=r,r.Transport=n(21),r.transports=n(16),r.parser=n(22),r.prototype.createTransport=function(t){a('creating transport "%s"',t);var e=o(this.query);e.EIO=p.protocol,e.transport=t;var n=this.transportOptions[t]||{};this.id&&(e.sid=this.id);var r=new i[t]({query:e,socket:this,agent:n.agent||this.agent,hostname:n.hostname||this.hostname,port:n.port||this.port,secure:n.secure||this.secure,path:n.path||this.path,forceJSONP:n.forceJSONP||this.forceJSONP,jsonp:n.jsonp||this.jsonp,forceBase64:n.forceBase64||this.forceBase64,enablesXDR:n.enablesXDR||this.enablesXDR,timestampRequests:n.timestampRequests||this.timestampRequests,timestampParam:n.timestampParam||this.timestampParam,policyPort:n.policyPort||this.policyPort,pfx:n.pfx||this.pfx,key:n.key||this.key,passphrase:n.passphrase||this.passphrase,cert:n.cert||this.cert,ca:n.ca||this.ca,ciphers:n.ciphers||this.ciphers,rejectUnauthorized:n.rejectUnauthorized||this.rejectUnauthorized,perMessageDeflate:n.perMessageDeflate||this.perMessageDeflate,extraHeaders:n.extraHeaders||this.extraHeaders,forceNode:n.forceNode||this.forceNode,localAddress:n.localAddress||this.localAddress,requestTimeout:n.requestTimeout||this.requestTimeout,protocols:n.protocols||void 0});return r},r.prototype.open=function(){var t;if(this.rememberUpgrade&&r.priorWebsocketSuccess&&this.transports.indexOf("websocket")!==-1)t="websocket";else{if(0===this.transports.length){var e=this;return void setTimeout(function(){e.emit("error","No transports available")},0)}t=this.transports[0]}this.readyState="opening";try{t=this.createTransport(t)}catch(n){return this.transports.shift(),void this.open()}t.open(),this.setTransport(t)},r.prototype.setTransport=function(t){a("setting transport %s",t.name);var e=this;this.transport&&(a("clearing existing transport %s",this.transport.name),this.transport.removeAllListeners()),this.transport=t,t.on("drain",function(){e.onDrain()}).on("packet",function(t){e.onPacket(t)}).on("error",function(t){e.onError(t)}).on("close",function(){e.onClose("transport close")})},r.prototype.probe=function(t){function e(){if(f.onlyBinaryUpgrades){var e=!this.supportsBinary&&f.transport.supportsBinary;h=h||e}h||(a('probe transport "%s" opened',t),u.send([{type:"ping",data:"probe"}]),u.once("packet",function(e){if(!h)if("pong"===e.type&&"probe"===e.data){if(a('probe transport "%s" pong',t),f.upgrading=!0,f.emit("upgrading",u),!u)return;r.priorWebsocketSuccess="websocket"===u.name,a('pausing current transport "%s"',f.transport.name),f.transport.pause(function(){h||"closed"!==f.readyState&&(a("changing transport and sending upgrade packet"),p(),f.setTransport(u),u.send([{type:"upgrade"}]),f.emit("upgrade",u),u=null,f.upgrading=!1,f.flush())})}else{a('probe transport "%s" failed',t);var n=new Error("probe error");n.transport=u.name,f.emit("upgradeError",n)}}))}function n(){h||(h=!0,p(),u.close(),u=null)}function o(e){var r=new Error("probe error: "+e);r.transport=u.name,n(),a('probe transport "%s" failed because of error: %s',t,e),f.emit("upgradeError",r)}function i(){o("transport closed")}function s(){o("socket closed")}function c(t){u&&t.name!==u.name&&(a('"%s" works - aborting "%s"',t.name,u.name),n())}function p(){u.removeListener("open",e),u.removeListener("error",o),u.removeListener("close",i),f.removeListener("close",s),f.removeListener("upgrading",c)}a('probing transport "%s"',t);var u=this.createTransport(t,{probe:1}),h=!1,f=this;r.priorWebsocketSuccess=!1,u.once("open",e),u.once("error",o),u.once("close",i),this.once("close",s),this.once("upgrading",c),u.open()},r.prototype.onOpen=function(){if(a("socket open"),this.readyState="open",r.priorWebsocketSuccess="websocket"===this.transport.name,this.emit("open"),this.flush(),"open"===this.readyState&&this.upgrade&&this.transport.pause){a("starting upgrade probes");for(var t=0,e=this.upgrades.length;t1?{type:b[o],data:t.substring(1)}:{type:b[o]}:w}var i=new Uint8Array(t),o=i[0],s=f(t,1);return k&&"blob"===n&&(s=new k([s])),{type:b[o],data:s}},e.decodeBase64Packet=function(t,e){var n=b[t.charAt(0)];if(!p)return{type:n,data:{base64:!0,data:t.substr(1)}};var r=p.decode(t.substr(1));return"blob"===e&&k&&(r=new k([r])),{type:n,data:r}},e.encodePayload=function(t,n,r){function o(t){return t.length+":"+t}function i(t,r){e.encodePacket(t,!!s&&n,!1,function(t){r(null,o(t))})}"function"==typeof n&&(r=n,n=null);var s=h(t);return n&&s?k&&!g?e.encodePayloadAsBlob(t,r):e.encodePayloadAsArrayBuffer(t,r):t.length?void c(t,i,function(t,e){return r(e.join(""))}):r("0:")},e.decodePayload=function(t,n,r){if("string"!=typeof t)return e.decodePayloadAsBinary(t,n,r);"function"==typeof n&&(r=n,n=null);var o;if(""===t)return r(w,0,1);for(var i,s,a="",c=0,p=t.length;c0;){for(var s=new Uint8Array(o),a=0===s[0],c="",p=1;255!==s[p];p++){if(c.length>310)return r(w,0,1);c+=s[p]}o=f(o,2+c.length),c=parseInt(c);var u=f(o,0,c);if(a)try{u=String.fromCharCode.apply(null,new Uint8Array(u))}catch(h){var l=new Uint8Array(u);u="";for(var p=0;pr&&(n=r),e>=r||e>=n||0===r)return new ArrayBuffer(0);for(var o=new Uint8Array(t),i=new Uint8Array(n-e),s=e,a=0;s=55296&&e<=56319&&o65535&&(e-=65536,o+=w(e>>>10&1023|55296),e=56320|1023&e),o+=w(e);return o}function c(t,e){if(t>=55296&&t<=57343){if(e)throw Error("Lone surrogate U+"+t.toString(16).toUpperCase()+" is not a scalar value");return!1}return!0}function p(t,e){return w(t>>e&63|128)}function u(t,e){if(0==(4294967168&t))return w(t);var n="";return 0==(4294965248&t)?n=w(t>>6&31|192):0==(4294901760&t)?(c(t,e)||(t=65533),n=w(t>>12&15|224),n+=p(t,6)):0==(4292870144&t)&&(n=w(t>>18&7|240),n+=p(t,12),n+=p(t,6)),n+=w(63&t|128)}function h(t,e){e=e||{};for(var n,r=!1!==e.strict,o=s(t),i=o.length,a=-1,c="";++a=v)throw Error("Invalid byte index");var t=255&g[b];if(b++,128==(192&t))return 63&t;throw Error("Invalid continuation byte")}function l(t){var e,n,r,o,i;if(b>v)throw Error("Invalid byte index");if(b==v)return!1;if(e=255&g[b],b++,0==(128&e))return e;if(192==(224&e)){if(n=f(),i=(31&e)<<6|n,i>=128)return i;throw Error("Invalid continuation byte")}if(224==(240&e)){if(n=f(),r=f(),i=(15&e)<<12|n<<6|r,i>=2048)return c(i,t)?i:65533;throw Error("Invalid continuation byte")}if(240==(248&e)&&(n=f(),r=f(),o=f(),i=(7&e)<<18|n<<12|r<<6|o,i>=65536&&i<=1114111))return i;throw Error("Invalid UTF-8 detected")}function d(t,e){e=e||{};var n=!1!==e.strict;g=s(t),v=g.length,b=0;for(var r,o=[];(r=l(n))!==!1;)o.push(r);return a(o)}var y="object"==typeof e&&e,m=("object"==typeof t&&t&&t.exports==y&&t,"object"==typeof o&&o);m.global!==m&&m.window!==m||(i=m);var g,v,b,w=String.fromCharCode,k={version:"2.1.2",encode:h,decode:d};r=function(){return k}.call(e,n,e,t),!(void 0!==r&&(t.exports=r))}(this)}).call(e,n(27)(t),function(){return this}())},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},function(t,e){!function(){"use strict";for(var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n=new Uint8Array(256),r=0;r>2],i+=t[(3&r[n])<<4|r[n+1]>>4],i+=t[(15&r[n+1])<<2|r[n+2]>>6],i+=t[63&r[n+2]];return o%3===2?i=i.substring(0,i.length-1)+"=":o%3===1&&(i=i.substring(0,i.length-2)+"=="),i},e.decode=function(t){var e,r,o,i,s,a=.75*t.length,c=t.length,p=0;"="===t[t.length-1]&&(a--,"="===t[t.length-2]&&a--);var u=new ArrayBuffer(a),h=new Uint8Array(u);for(e=0;e>4,h[p++]=(15&o)<<4|i>>2,h[p++]=(3&i)<<6|63&s;return u}}()},function(t,e){(function(e){function n(t){for(var e=0;e0);return e}function r(t){var e=0;for(u=0;u';i=document.createElement(e)}catch(t){i=document.createElement("iframe"),i.name=o.iframeId,i.src="javascript:0"}i.id=o.iframeId,o.form.appendChild(i),o.iframe=i}var o=this;if(!this.form){var i,s=document.createElement("form"),a=document.createElement("textarea"),u=this.iframeId="eio_iframe_"+this.index;s.className="socketio",s.style.position="absolute",s.style.top="-1000px",s.style.left="-1000px",s.target=u,s.method="POST",s.setAttribute("accept-charset","utf-8"),a.name="d",s.appendChild(a),document.body.appendChild(s),this.form=s,this.area=a}this.form.action=this.uri(),r(),t=t.replace(p,"\\\n"),this.area.value=t.replace(c,"\\n");try{this.form.submit()}catch(h){}this.iframe.attachEvent?this.iframe.onreadystatechange=function(){"complete"===o.iframe.readyState&&n()}:this.iframe.onload=n}}).call(e,function(){return this}())},function(t,e,n){(function(e){function r(t){var e=t&&t.forceBase64;e&&(this.supportsBinary=!1),this.perMessageDeflate=t.perMessageDeflate,this.usingBrowserWebSocket=h&&!t.forceNode,this.protocols=t.protocols,this.usingBrowserWebSocket||(l=o),i.call(this,t)}var o,i=n(21),s=n(22),a=n(30),c=n(31),p=n(32),u=n(3)("engine.io-client:websocket"),h=e.WebSocket||e.MozWebSocket;if("undefined"==typeof window)try{o=n(35)}catch(f){}var l=h;l||"undefined"!=typeof window||(l=o),t.exports=r,c(r,i),r.prototype.name="websocket",r.prototype.supportsBinary=!0,r.prototype.doOpen=function(){if(this.check()){var t=this.uri(),e=this.protocols,n={agent:this.agent,perMessageDeflate:this.perMessageDeflate};n.pfx=this.pfx,n.key=this.key,n.passphrase=this.passphrase,n.cert=this.cert,n.ca=this.ca,n.ciphers=this.ciphers,n.rejectUnauthorized=this.rejectUnauthorized,this.extraHeaders&&(n.headers=this.extraHeaders),this.localAddress&&(n.localAddress=this.localAddress);try{this.ws=this.usingBrowserWebSocket?e?new l(t,e):new l(t):new l(t,e,n)}catch(r){return this.emit("error",r)}void 0===this.ws.binaryType&&(this.supportsBinary=!1),this.ws.supports&&this.ws.supports.binary?(this.supportsBinary=!0,this.ws.binaryType="nodebuffer"):this.ws.binaryType="arraybuffer",this.addEventListeners()}},r.prototype.addEventListeners=function(){var t=this;this.ws.onopen=function(){t.onOpen()},this.ws.onclose=function(){t.onClose()},this.ws.onmessage=function(e){t.onData(e.data)},this.ws.onerror=function(e){t.onError("websocket error",e)}},r.prototype.write=function(t){function n(){r.emit("flush"),setTimeout(function(){r.writable=!0,r.emit("drain")},0)}var r=this;this.writable=!1;for(var o=t.length,i=0,a=o;i0&&t.jitter<=1?t.jitter:0,this.attempts=0}t.exports=n,n.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),n=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-n:t+n}return 0|Math.min(t,this.max)},n.prototype.reset=function(){this.attempts=0},n.prototype.setMin=function(t){this.ms=t},n.prototype.setMax=function(t){this.max=t},n.prototype.setJitter=function(t){this.jitter=t}}])}); 3 | //# sourceMappingURL=socket.io.js.map --------------------------------------------------------------------------------