소스 파일 최초 업로드
This commit is contained in:
136
tomcat/webapps.dist/examples/websocket/chat.xhtml
Normal file
136
tomcat/webapps.dist/examples/websocket/chat.xhtml
Normal file
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Apache Tomcat WebSocket Examples: Chat</title>
|
||||
<style type="text/css"><![CDATA[
|
||||
input#chat {
|
||||
width: 410px
|
||||
}
|
||||
|
||||
#console-container {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
#console {
|
||||
border: 1px solid #CCCCCC;
|
||||
border-right-color: #999999;
|
||||
border-bottom-color: #999999;
|
||||
height: 170px;
|
||||
overflow-y: scroll;
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#console p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
]]></style>
|
||||
<script type="text/javascript"><![CDATA[
|
||||
"use strict";
|
||||
|
||||
var Chat = {};
|
||||
|
||||
Chat.socket = null;
|
||||
|
||||
Chat.connect = (function(host) {
|
||||
if ('WebSocket' in window) {
|
||||
Chat.socket = new WebSocket(host);
|
||||
} else if ('MozWebSocket' in window) {
|
||||
Chat.socket = new MozWebSocket(host);
|
||||
} else {
|
||||
Console.log('Error: WebSocket is not supported by this browser.');
|
||||
return;
|
||||
}
|
||||
|
||||
Chat.socket.onopen = function () {
|
||||
Console.log('Info: WebSocket connection opened.');
|
||||
document.getElementById('chat').onkeydown = function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
Chat.sendMessage();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Chat.socket.onclose = function () {
|
||||
document.getElementById('chat').onkeydown = null;
|
||||
Console.log('Info: WebSocket closed.');
|
||||
};
|
||||
|
||||
Chat.socket.onmessage = function (message) {
|
||||
Console.log(message.data);
|
||||
};
|
||||
});
|
||||
|
||||
Chat.initialize = function() {
|
||||
if (window.location.protocol == 'http:') {
|
||||
Chat.connect('ws://' + window.location.host + '/examples/websocket/chat');
|
||||
} else {
|
||||
Chat.connect('wss://' + window.location.host + '/examples/websocket/chat');
|
||||
}
|
||||
};
|
||||
|
||||
Chat.sendMessage = (function() {
|
||||
var message = document.getElementById('chat').value;
|
||||
if (message != '') {
|
||||
Chat.socket.send(message);
|
||||
document.getElementById('chat').value = '';
|
||||
}
|
||||
});
|
||||
|
||||
var Console = {};
|
||||
|
||||
Console.log = (function(message) {
|
||||
var console = document.getElementById('console');
|
||||
var p = document.createElement('p');
|
||||
p.style.wordWrap = 'break-word';
|
||||
p.innerHTML = message;
|
||||
console.appendChild(p);
|
||||
while (console.childNodes.length > 25) {
|
||||
console.removeChild(console.firstChild);
|
||||
}
|
||||
console.scrollTop = console.scrollHeight;
|
||||
});
|
||||
|
||||
Chat.initialize();
|
||||
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Remove elements with "noscript" class - <noscript> is not allowed in XHTML
|
||||
var noscripts = document.getElementsByClassName("noscript");
|
||||
for (var i = 0; i < noscripts.length; i++) {
|
||||
noscripts[i].parentNode.removeChild(noscripts[i]);
|
||||
}
|
||||
}, false);
|
||||
|
||||
]]></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="noscript"><h2 style="color: #ff0000">Seems your browser doesn't support JavaScript! Websockets rely on JavaScript being enabled. Please enable
|
||||
JavaScript and reload this page!</h2></div>
|
||||
<div>
|
||||
<p>
|
||||
<input type="text" placeholder="type and press enter to chat" id="chat" />
|
||||
</p>
|
||||
<div id="console-container">
|
||||
<div id="console"/>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
899
tomcat/webapps.dist/examples/websocket/drawboard.xhtml
Normal file
899
tomcat/webapps.dist/examples/websocket/drawboard.xhtml
Normal file
@@ -0,0 +1,899 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Apache Tomcat WebSocket Examples: Drawboard</title>
|
||||
<style type="text/css"><![CDATA[
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 11pt;
|
||||
background-color: #eeeeea;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#console-container {
|
||||
float: left;
|
||||
background-color: #fff;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
#console {
|
||||
font-size: 10pt;
|
||||
height: 600px;
|
||||
overflow-y: scroll;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
#console p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#drawContainer {
|
||||
float: left;
|
||||
display: none;
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
#drawContainer canvas {
|
||||
display: block;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none; /* Disable touch behaviors, like pan and zoom */
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
#labelContainer {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#drawContainer, #console-container {
|
||||
box-shadow: 0px 0px 8px 3px #bbb;
|
||||
border: 1px solid #CCCCCC;
|
||||
}
|
||||
|
||||
]]></style>
|
||||
<script type="text/javascript"><![CDATA[
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Remove elements with "noscript" class - <noscript> is not
|
||||
// allowed in XHTML
|
||||
var noscripts = document.getElementsByClassName("noscript");
|
||||
for (var i = 0; i < noscripts.length; i++) {
|
||||
noscripts[i].parentNode.removeChild(noscripts[i]);
|
||||
}
|
||||
|
||||
// Add script for expand content.
|
||||
var expandElements = document.getElementsByClassName("expand");
|
||||
for (var ixx = 0; ixx < expandElements.length; ixx++) {
|
||||
(function(el) {
|
||||
var expandContent = document.getElementById(el.getAttribute("data-content-id"));
|
||||
expandContent.style.display = "none";
|
||||
var arrow = document.createTextNode("◢ ");
|
||||
var arrowSpan = document.createElement("span");
|
||||
arrowSpan.appendChild(arrow);
|
||||
|
||||
var link = document.createElement("a");
|
||||
link.setAttribute("href", "#!");
|
||||
while (el.firstChild != null) {
|
||||
link.appendChild(el.removeChild(el.firstChild));
|
||||
}
|
||||
el.appendChild(arrowSpan);
|
||||
el.appendChild(link);
|
||||
|
||||
var textSpan = document.createElement("span");
|
||||
textSpan.setAttribute("style", "font-weight: normal;");
|
||||
textSpan.appendChild(document.createTextNode(" (click to expand)"));
|
||||
el.appendChild(textSpan);
|
||||
|
||||
|
||||
var visible = true;
|
||||
|
||||
var switchExpand = function() {
|
||||
visible = !visible;
|
||||
expandContent.style.display = visible ? "block" : "none";
|
||||
arrowSpan.style.color = visible ? "#000" : "#888";
|
||||
return false;
|
||||
};
|
||||
|
||||
link.onclick = switchExpand;
|
||||
switchExpand();
|
||||
|
||||
})(expandElements[ixx]);
|
||||
}
|
||||
|
||||
|
||||
var Console = {};
|
||||
|
||||
Console.log = (function() {
|
||||
var consoleContainer =
|
||||
document.getElementById("console-container");
|
||||
var console = document.createElement("div");
|
||||
console.setAttribute("id", "console");
|
||||
consoleContainer.appendChild(console);
|
||||
|
||||
return function(message) {
|
||||
var p = document.createElement('p');
|
||||
p.style.wordWrap = "break-word";
|
||||
p.appendChild(document.createTextNode(message));
|
||||
console.appendChild(p);
|
||||
while (console.childNodes.length > 25) {
|
||||
console.removeChild(console.firstChild);
|
||||
}
|
||||
console.scrollTop = console.scrollHeight;
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
function Room(drawContainer) {
|
||||
|
||||
/* A pausable event forwarder that can be used to pause and
|
||||
* resume handling of events (e.g. when we need to wait
|
||||
* for a Image's load event before we can process further
|
||||
* WebSocket messages).
|
||||
* The object's callFunction(func) should be called from an
|
||||
* event handler and give the function to handle the event as
|
||||
* argument.
|
||||
* Call pauseProcessing() to suspend event forwarding and
|
||||
* resumeProcessing() to resume it.
|
||||
*/
|
||||
function PausableEventForwarder() {
|
||||
|
||||
var pauseProcessing = false;
|
||||
// Queue for buffering functions to be called.
|
||||
var functionQueue = [];
|
||||
|
||||
this.callFunction = function(func) {
|
||||
// If message processing is paused, we push it
|
||||
// into the queue - otherwise we process it directly.
|
||||
if (pauseProcessing) {
|
||||
functionQueue.push(func);
|
||||
} else {
|
||||
func();
|
||||
}
|
||||
};
|
||||
|
||||
this.pauseProcessing = function() {
|
||||
pauseProcessing = true;
|
||||
};
|
||||
|
||||
this.resumeProcessing = function() {
|
||||
pauseProcessing = false;
|
||||
|
||||
// Process all queued functions until some handler calls
|
||||
// pauseProcessing() again.
|
||||
while (functionQueue.length > 0 && !pauseProcessing) {
|
||||
var func = functionQueue.pop();
|
||||
func();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// The WebSocket object.
|
||||
var socket;
|
||||
// ID of the timer which sends ping messages.
|
||||
var pingTimerId;
|
||||
|
||||
var isStarted = false;
|
||||
var playerCount = 0;
|
||||
|
||||
// An array of PathIdContainer objects that the server
|
||||
// did not yet handle.
|
||||
// They are ordered by id (ascending).
|
||||
var pathsNotHandled = [];
|
||||
|
||||
var nextMsgId = 1;
|
||||
|
||||
var canvasDisplay = document.createElement("canvas");
|
||||
var canvasBackground = document.createElement("canvas");
|
||||
var canvasServerImage = document.createElement("canvas");
|
||||
var canvasArray = [canvasDisplay, canvasBackground,
|
||||
canvasServerImage];
|
||||
canvasDisplay.addEventListener("mousedown", function(e) {
|
||||
// Prevent default mouse event to prevent browsers from marking text
|
||||
// (and Chrome from displaying the "text" cursor).
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
|
||||
var labelPlayerCount = document.createTextNode("0");
|
||||
var optionContainer = document.createElement("div");
|
||||
|
||||
|
||||
var canvasDisplayCtx = canvasDisplay.getContext("2d");
|
||||
var canvasBackgroundCtx = canvasBackground.getContext("2d");
|
||||
var canvasServerImageCtx = canvasServerImage.getContext("2d");
|
||||
var canvasMouseMoveHandler;
|
||||
var canvasMouseDownHandler;
|
||||
|
||||
var isActive = false;
|
||||
var mouseInWindow = false;
|
||||
var mouseDown = false;
|
||||
var currentMouseX = 0, currentMouseY = 0;
|
||||
var currentPreviewPath = null;
|
||||
|
||||
var availableColors = [];
|
||||
var currentColorIndex;
|
||||
var colorContainers;
|
||||
var previewTransparency = 0.65;
|
||||
|
||||
var availableThicknesses = [2, 3, 6, 10, 16, 28, 50];
|
||||
var currentThicknessIndex;
|
||||
var thicknessContainers;
|
||||
|
||||
var availableDrawTypes = [
|
||||
{ name: "Brush", id: 1, continuous: true },
|
||||
{ name: "Line", id: 2, continuous: false },
|
||||
{ name: "Rectangle", id: 3, continuous: false },
|
||||
{ name: "Ellipse", id: 4, continuous: false }
|
||||
];
|
||||
var currentDrawTypeIndex;
|
||||
var drawTypeContainers;
|
||||
|
||||
|
||||
var labelContainer = document.getElementById("labelContainer");
|
||||
var placeholder = document.createElement("div");
|
||||
placeholder.appendChild(document.createTextNode("Loading... "));
|
||||
var progressElem = document.createElement("progress");
|
||||
placeholder.appendChild(progressElem);
|
||||
|
||||
labelContainer.appendChild(placeholder);
|
||||
|
||||
function rgb(color) {
|
||||
return "rgba(" + color[0] + "," + color[1] + ","
|
||||
+ color[2] + "," + color[3] + ")";
|
||||
}
|
||||
|
||||
function PathIdContainer(path, id) {
|
||||
this.path = path;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
function Path(type, color, thickness, x1, y1, x2, y2) {
|
||||
this.type = type;
|
||||
this.color = color;
|
||||
this.thickness = thickness;
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.x2 = x2;
|
||||
this.y2 = y2;
|
||||
|
||||
function ellipse(ctx, x, y, w, h) {
|
||||
/* Drawing an ellipse cannot be done directly in a
|
||||
* CanvasRenderingContext2D - we need to use drawArc()
|
||||
* in conjunction with scaling the context so that we
|
||||
* get the needed proportion.
|
||||
*/
|
||||
ctx.save();
|
||||
|
||||
// Translate and scale the context so that we can draw
|
||||
// an arc at (0, 0) with a radius of 1.
|
||||
ctx.translate(x + w / 2, y + h / 2);
|
||||
ctx.scale(w / 2, h / 2);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, 1, 0, Math.PI * 2, false);
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
this.draw = function(ctx) {
|
||||
ctx.beginPath();
|
||||
ctx.lineCap = "round";
|
||||
ctx.lineWidth = thickness;
|
||||
var style = rgb(color);
|
||||
ctx.strokeStyle = style;
|
||||
|
||||
if (x1 == x2 && y1 == y2) {
|
||||
// Always draw as arc to meet the behavior
|
||||
// in Java2D.
|
||||
ctx.fillStyle = style;
|
||||
ctx.arc(x1, y1, thickness / 2.0, 0,
|
||||
Math.PI * 2.0, false);
|
||||
ctx.fill();
|
||||
} else {
|
||||
if (type == 1 || type == 2) {
|
||||
// Draw a line.
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
ctx.stroke();
|
||||
} else if (type == 3) {
|
||||
// Draw a rectangle.
|
||||
if (x1 == x2 || y1 == y2) {
|
||||
// Draw as line
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
ctx.stroke();
|
||||
} else {
|
||||
ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
} else if (type == 4) {
|
||||
// Draw an ellipse.
|
||||
ellipse(ctx, x1, y1, x2 - x1, y2 - y1);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function connect() {
|
||||
var host = (window.location.protocol == "https:"
|
||||
? "wss://" : "ws://") + window.location.host
|
||||
+ "/examples/websocket/drawboard";
|
||||
socket = new WebSocket(host);
|
||||
|
||||
/* Use a pausable event forwarder.
|
||||
* This is needed when we load an Image object with data
|
||||
* from a previous message, because we must wait until the
|
||||
* Image's load event it raised before we can use it (and
|
||||
* in the meantime the socket.message event could be
|
||||
* raised).
|
||||
* Therefore we need this pausable event handler to handle
|
||||
* e.g. socket.onmessage and socket.onclose.
|
||||
*/
|
||||
var eventForwarder = new PausableEventForwarder();
|
||||
|
||||
socket.onopen = function () {
|
||||
// Socket has opened. Now wait for the server to
|
||||
// send us the initial packet.
|
||||
Console.log("WebSocket connection opened.");
|
||||
|
||||
// Set up a timer for pong messages.
|
||||
pingTimerId = window.setInterval(function() {
|
||||
socket.send("0");
|
||||
}, 30000);
|
||||
};
|
||||
|
||||
socket.onclose = function () {
|
||||
eventForwarder.callFunction(function() {
|
||||
Console.log("WebSocket connection closed.");
|
||||
disableControls();
|
||||
|
||||
// Disable pong timer.
|
||||
window.clearInterval(pingTimerId);
|
||||
});
|
||||
};
|
||||
|
||||
// Handles an incoming Websocket message.
|
||||
var handleOnMessage = function(message) {
|
||||
|
||||
// Split joined message and process them
|
||||
// individually.
|
||||
var messages = message.data.split(";");
|
||||
for (var msgArrIdx = 0; msgArrIdx < messages.length;
|
||||
msgArrIdx++) {
|
||||
var msg = messages[msgArrIdx];
|
||||
var type = msg.substring(0, 1);
|
||||
|
||||
if (type == "0") {
|
||||
// Error message.
|
||||
var error = msg.substring(1);
|
||||
// Log it to the console and show an alert.
|
||||
Console.log("Error: " + error);
|
||||
alert(error);
|
||||
|
||||
} else {
|
||||
if (!isStarted) {
|
||||
if (type == "2") {
|
||||
// Initial message. It contains the
|
||||
// number of players.
|
||||
// After this message we will receive
|
||||
// a binary message containing the current
|
||||
// room image as PNG.
|
||||
playerCount = parseInt(msg.substring(1));
|
||||
|
||||
refreshPlayerCount();
|
||||
|
||||
// The next message will be a binary
|
||||
// message containing the room images
|
||||
// as PNG. Therefore we temporarily swap
|
||||
// the message handler.
|
||||
var originalHandler = handleOnMessage;
|
||||
handleOnMessage = function(message) {
|
||||
// First, we restore the original handler.
|
||||
handleOnMessage = originalHandler;
|
||||
|
||||
// Read the image.
|
||||
var blob = message.data;
|
||||
// Create new blob with correct MIME type.
|
||||
blob = new Blob([blob], {type : "image/png"});
|
||||
|
||||
var url = URL.createObjectURL(blob);
|
||||
|
||||
var img = new Image();
|
||||
|
||||
// We must wait until the onload event is
|
||||
// raised until we can draw the image onto
|
||||
// the canvas.
|
||||
// Therefore we need to pause the event
|
||||
// forwarder until the image is loaded.
|
||||
eventForwarder.pauseProcessing();
|
||||
|
||||
img.onload = function() {
|
||||
|
||||
// Release the object URL.
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
// Set the canvases to the correct size.
|
||||
for (var i = 0; i < canvasArray.length; i++) {
|
||||
canvasArray[i].width = img.width;
|
||||
canvasArray[i].height = img.height;
|
||||
}
|
||||
|
||||
// Now draw the image on the last canvas.
|
||||
canvasServerImageCtx.clearRect(0, 0,
|
||||
canvasServerImage.width,
|
||||
canvasServerImage.height);
|
||||
canvasServerImageCtx.drawImage(img, 0, 0);
|
||||
|
||||
// Draw it on the background canvas.
|
||||
canvasBackgroundCtx.drawImage(canvasServerImage,
|
||||
0, 0);
|
||||
|
||||
isStarted = true;
|
||||
startControls();
|
||||
|
||||
// Refresh the display canvas.
|
||||
refreshDisplayCanvas();
|
||||
|
||||
|
||||
// Finally, resume the event forwarder.
|
||||
eventForwarder.resumeProcessing();
|
||||
};
|
||||
|
||||
img.src = url;
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (type == "3") {
|
||||
// The number of players in this room changed.
|
||||
var playerAdded = msg.substring(1) == "+";
|
||||
playerCount += playerAdded ? 1 : -1;
|
||||
refreshPlayerCount();
|
||||
|
||||
Console.log("Player " + (playerAdded
|
||||
? "joined." : "left."));
|
||||
|
||||
} else if (type == "1") {
|
||||
// We received a new DrawMessage.
|
||||
var maxLastHandledId = -1;
|
||||
var drawMessages = msg.substring(1).split("|");
|
||||
for (var i = 0; i < drawMessages.length; i++) {
|
||||
var elements = drawMessages[i].split(",");
|
||||
var lastHandledId = parseInt(elements[0]);
|
||||
maxLastHandledId = Math.max(maxLastHandledId,
|
||||
lastHandledId);
|
||||
|
||||
var path = new Path(
|
||||
parseInt(elements[1]),
|
||||
[parseInt(elements[2]),
|
||||
parseInt(elements[3]),
|
||||
parseInt(elements[4]),
|
||||
parseInt(elements[5]) / 255.0],
|
||||
parseFloat(elements[6]),
|
||||
parseFloat(elements[7]),
|
||||
parseFloat(elements[8]),
|
||||
parseFloat(elements[9]),
|
||||
parseFloat(elements[10]));
|
||||
|
||||
// Draw the path onto the last canvas.
|
||||
path.draw(canvasServerImageCtx);
|
||||
}
|
||||
|
||||
// Draw the last canvas onto the background one.
|
||||
canvasBackgroundCtx.drawImage(canvasServerImage,
|
||||
0, 0);
|
||||
|
||||
// Now go through the pathsNotHandled array and
|
||||
// remove the paths that were already handled by
|
||||
// the server.
|
||||
while (pathsNotHandled.length > 0
|
||||
&& pathsNotHandled[0].id <= maxLastHandledId)
|
||||
pathsNotHandled.shift();
|
||||
|
||||
// Now me must draw the remaining paths onto
|
||||
// the background canvas.
|
||||
for (var i = 0; i < pathsNotHandled.length; i++) {
|
||||
pathsNotHandled[i].path.draw(canvasBackgroundCtx);
|
||||
}
|
||||
|
||||
refreshDisplayCanvas();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
socket.onmessage = function(message) {
|
||||
eventForwarder.callFunction(function() {
|
||||
handleOnMessage(message);
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
function refreshPlayerCount() {
|
||||
labelPlayerCount.nodeValue = String(playerCount);
|
||||
}
|
||||
|
||||
function refreshDisplayCanvas() {
|
||||
if (!isActive) { // Don't draw a cursor when not active.
|
||||
return;
|
||||
}
|
||||
|
||||
canvasDisplayCtx.drawImage(canvasBackground, 0, 0);
|
||||
if (currentPreviewPath != null) {
|
||||
// Draw the preview path.
|
||||
currentPreviewPath.draw(canvasDisplayCtx);
|
||||
|
||||
} else if (mouseInWindow && !mouseDown) {
|
||||
canvasDisplayCtx.beginPath();
|
||||
var color = availableColors[currentColorIndex].slice(0);
|
||||
color[3] = previewTransparency;
|
||||
canvasDisplayCtx.fillStyle = rgb(color);
|
||||
|
||||
canvasDisplayCtx.arc(currentMouseX, currentMouseY,
|
||||
availableThicknesses[currentThicknessIndex] / 2,
|
||||
0, Math.PI * 2.0, true);
|
||||
canvasDisplayCtx.fill();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function startControls() {
|
||||
isActive = true;
|
||||
|
||||
labelContainer.removeChild(placeholder);
|
||||
placeholder = undefined;
|
||||
|
||||
labelContainer.appendChild(
|
||||
document.createTextNode("Number of Players: "));
|
||||
labelContainer.appendChild(labelPlayerCount);
|
||||
|
||||
|
||||
drawContainer.style.display = "block";
|
||||
drawContainer.appendChild(canvasDisplay);
|
||||
|
||||
drawContainer.appendChild(optionContainer);
|
||||
|
||||
canvasMouseDownHandler = function(e) {
|
||||
if (e.button == 0) {
|
||||
currentMouseX = e.pageX - canvasDisplay.offsetLeft;
|
||||
currentMouseY = e.pageY - canvasDisplay.offsetTop;
|
||||
|
||||
mouseDown = true;
|
||||
canvasMouseMoveHandler(e);
|
||||
|
||||
} else if (mouseDown) {
|
||||
// Cancel drawing.
|
||||
mouseDown = false;
|
||||
currentPreviewPath = null;
|
||||
|
||||
currentMouseX = e.pageX - canvasDisplay.offsetLeft;
|
||||
currentMouseY = e.pageY - canvasDisplay.offsetTop;
|
||||
|
||||
refreshDisplayCanvas();
|
||||
}
|
||||
};
|
||||
canvasDisplay.addEventListener("mousedown", canvasMouseDownHandler, false);
|
||||
|
||||
canvasMouseMoveHandler = function(e) {
|
||||
var mouseX = e.pageX - canvasDisplay.offsetLeft;
|
||||
var mouseY = e.pageY - canvasDisplay.offsetTop;
|
||||
|
||||
if (mouseDown) {
|
||||
var drawType = availableDrawTypes[currentDrawTypeIndex];
|
||||
|
||||
if (drawType.continuous) {
|
||||
|
||||
var path = new Path(drawType.id,
|
||||
availableColors[currentColorIndex],
|
||||
availableThicknesses[currentThicknessIndex],
|
||||
currentMouseX, currentMouseY, mouseX,
|
||||
mouseY);
|
||||
// Draw it on the background canvas.
|
||||
path.draw(canvasBackgroundCtx);
|
||||
|
||||
// Send it to the sever.
|
||||
pushPath(path);
|
||||
|
||||
// Refresh old coordinates
|
||||
currentMouseX = mouseX;
|
||||
currentMouseY = mouseY;
|
||||
|
||||
} else {
|
||||
// Create a new preview path.
|
||||
var color = availableColors[currentColorIndex].slice(0);
|
||||
color[3] = previewTransparency;
|
||||
currentPreviewPath = new Path(drawType.id,
|
||||
color,
|
||||
availableThicknesses[currentThicknessIndex],
|
||||
currentMouseX, currentMouseY, mouseX,
|
||||
mouseY, false);
|
||||
}
|
||||
|
||||
refreshDisplayCanvas();
|
||||
} else {
|
||||
currentMouseX = mouseX;
|
||||
currentMouseY = mouseY;
|
||||
|
||||
if (mouseInWindow) {
|
||||
refreshDisplayCanvas();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
document.addEventListener("mousemove", canvasMouseMoveHandler, false);
|
||||
|
||||
document.addEventListener("mouseup", function(e) {
|
||||
if (e.button == 0) {
|
||||
if (mouseDown) {
|
||||
mouseDown = false;
|
||||
currentPreviewPath = null;
|
||||
|
||||
var mouseX = e.pageX - canvasDisplay.offsetLeft;
|
||||
var mouseY = e.pageY - canvasDisplay.offsetTop;
|
||||
var drawType = availableDrawTypes[currentDrawTypeIndex];
|
||||
|
||||
// If we are drawing a continuous path and the previous mouse coordinates are the same as
|
||||
// the new ones, there is no need to construct a new draw message as we don't need to
|
||||
// "terminate" a path as every path element contains both the start and the end point.
|
||||
if (!(drawType.continuous && mouseX == currentMouseX && mouseY == currentMouseY)) {
|
||||
var path = new Path(drawType.id, availableColors[currentColorIndex],
|
||||
availableThicknesses[currentThicknessIndex],
|
||||
currentMouseX, currentMouseY, mouseX,
|
||||
mouseY);
|
||||
// Draw it on the background canvas.
|
||||
path.draw(canvasBackgroundCtx);
|
||||
|
||||
// Send it to the sever.
|
||||
pushPath(path);
|
||||
|
||||
// Refresh old coordinates
|
||||
currentMouseX = mouseX;
|
||||
currentMouseY = mouseY;
|
||||
}
|
||||
|
||||
refreshDisplayCanvas();
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
|
||||
canvasDisplay.addEventListener("mouseout", function(e) {
|
||||
mouseInWindow = false;
|
||||
refreshDisplayCanvas();
|
||||
}, false);
|
||||
|
||||
canvasDisplay.addEventListener("mousemove", function(e) {
|
||||
if (!mouseInWindow) {
|
||||
mouseInWindow = true;
|
||||
refreshDisplayCanvas();
|
||||
}
|
||||
}, false);
|
||||
|
||||
|
||||
// Create color and thickness controls.
|
||||
var colorContainersBox = document.createElement("div");
|
||||
colorContainersBox.setAttribute("style",
|
||||
"margin: 4px; border: 1px solid #bbb; border-radius: 3px;");
|
||||
optionContainer.appendChild(colorContainersBox);
|
||||
|
||||
colorContainers = new Array(3 * 3 * 3);
|
||||
for (var i = 0; i < colorContainers.length; i++) {
|
||||
var colorContainer = colorContainers[i] =
|
||||
document.createElement("div");
|
||||
var color = availableColors[i] =
|
||||
[
|
||||
Math.floor((i % 3) * 255 / 2),
|
||||
Math.floor((Math.floor(i / 3) % 3) * 255 / 2),
|
||||
Math.floor((Math.floor(i / (3 * 3)) % 3) * 255 / 2),
|
||||
1.0
|
||||
];
|
||||
colorContainer.setAttribute("style",
|
||||
"margin: 3px; width: 18px; height: 18px; "
|
||||
+ "float: left; background-color: " + rgb(color));
|
||||
colorContainer.style.border = '2px solid #000';
|
||||
colorContainer.addEventListener("mousedown", (function(ix) {
|
||||
return function() {
|
||||
setColor(ix);
|
||||
};
|
||||
})(i), false);
|
||||
|
||||
colorContainersBox.appendChild(colorContainer);
|
||||
}
|
||||
|
||||
var divClearLeft = document.createElement("div");
|
||||
divClearLeft.setAttribute("style", "clear: left;");
|
||||
colorContainersBox.appendChild(divClearLeft);
|
||||
|
||||
|
||||
var drawTypeContainersBox = document.createElement("div");
|
||||
drawTypeContainersBox.setAttribute("style",
|
||||
"float: right; margin-right: 3px; margin-top: 1px;");
|
||||
optionContainer.appendChild(drawTypeContainersBox);
|
||||
|
||||
drawTypeContainers = new Array(availableDrawTypes.length);
|
||||
for (var i = 0; i < drawTypeContainers.length; i++) {
|
||||
var drawTypeContainer = drawTypeContainers[i] =
|
||||
document.createElement("div");
|
||||
drawTypeContainer.setAttribute("style",
|
||||
"text-align: center; margin: 3px; padding: 0 3px;"
|
||||
+ "height: 18px; float: left;");
|
||||
drawTypeContainer.style.border = "2px solid #000";
|
||||
drawTypeContainer.appendChild(document.createTextNode(
|
||||
String(availableDrawTypes[i].name)));
|
||||
drawTypeContainer.addEventListener("mousedown", (function(ix) {
|
||||
return function() {
|
||||
setDrawType(ix);
|
||||
};
|
||||
})(i), false);
|
||||
|
||||
drawTypeContainersBox.appendChild(drawTypeContainer);
|
||||
}
|
||||
|
||||
|
||||
var thicknessContainersBox = document.createElement("div");
|
||||
thicknessContainersBox.setAttribute("style",
|
||||
"margin: 3px; border: 1px solid #bbb; border-radius: 3px;");
|
||||
optionContainer.appendChild(thicknessContainersBox);
|
||||
|
||||
thicknessContainers = new Array(availableThicknesses.length);
|
||||
for (var i = 0; i < thicknessContainers.length; i++) {
|
||||
var thicknessContainer = thicknessContainers[i] =
|
||||
document.createElement("div");
|
||||
thicknessContainer.setAttribute("style",
|
||||
"text-align: center; margin: 3px; width: 18px; "
|
||||
+ "height: 18px; float: left;");
|
||||
thicknessContainer.style.border = "2px solid #000";
|
||||
thicknessContainer.appendChild(document.createTextNode(
|
||||
String(availableThicknesses[i])));
|
||||
thicknessContainer.addEventListener("mousedown", (function(ix) {
|
||||
return function() {
|
||||
setThickness(ix);
|
||||
};
|
||||
})(i), false);
|
||||
|
||||
thicknessContainersBox.appendChild(thicknessContainer);
|
||||
}
|
||||
|
||||
|
||||
divClearLeft = document.createElement("div");
|
||||
divClearLeft.setAttribute("style", "clear: left;");
|
||||
thicknessContainersBox.appendChild(divClearLeft);
|
||||
|
||||
|
||||
setColor(0);
|
||||
setThickness(0);
|
||||
setDrawType(0);
|
||||
|
||||
}
|
||||
|
||||
function disableControls() {
|
||||
document.removeEventListener("mousedown", canvasMouseDownHandler);
|
||||
document.removeEventListener("mousemove", canvasMouseMoveHandler);
|
||||
mouseInWindow = false;
|
||||
refreshDisplayCanvas();
|
||||
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
function pushPath(path) {
|
||||
|
||||
// Push it into the pathsNotHandled array.
|
||||
var container = new PathIdContainer(path, nextMsgId++);
|
||||
pathsNotHandled.push(container);
|
||||
|
||||
// Send the path to the server.
|
||||
var message = container.id + "|" + path.type + ","
|
||||
+ path.color[0] + "," + path.color[1] + ","
|
||||
+ path.color[2] + ","
|
||||
+ Math.round(path.color[3] * 255.0) + ","
|
||||
+ path.thickness + "," + path.x1 + ","
|
||||
+ path.y1 + "," + path.x2 + "," + path.y2;
|
||||
|
||||
socket.send("1" + message);
|
||||
}
|
||||
|
||||
function setThickness(thicknessIndex) {
|
||||
if (typeof currentThicknessIndex !== "undefined")
|
||||
thicknessContainers[currentThicknessIndex]
|
||||
.style.borderColor = "#000";
|
||||
currentThicknessIndex = thicknessIndex;
|
||||
thicknessContainers[currentThicknessIndex]
|
||||
.style.borderColor = "#d08";
|
||||
}
|
||||
|
||||
function setColor(colorIndex) {
|
||||
if (typeof currentColorIndex !== "undefined")
|
||||
colorContainers[currentColorIndex]
|
||||
.style.borderColor = "#000";
|
||||
currentColorIndex = colorIndex;
|
||||
colorContainers[currentColorIndex]
|
||||
.style.borderColor = "#d08";
|
||||
}
|
||||
|
||||
function setDrawType(drawTypeIndex) {
|
||||
if (typeof currentDrawTypeIndex !== "undefined")
|
||||
drawTypeContainers[currentDrawTypeIndex]
|
||||
.style.borderColor = "#000";
|
||||
currentDrawTypeIndex = drawTypeIndex;
|
||||
drawTypeContainers[currentDrawTypeIndex]
|
||||
.style.borderColor = "#d08";
|
||||
}
|
||||
|
||||
|
||||
connect();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Initialize the room
|
||||
var room = new Room(document.getElementById("drawContainer"));
|
||||
|
||||
|
||||
}, false);
|
||||
|
||||
})();
|
||||
]]></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="noscript"><div style="color: #ff0000; font-size: 16pt;">Seems your browser doesn't support JavaScript! Websockets rely on JavaScript being enabled. Please enable
|
||||
JavaScript and reload this page!</div></div>
|
||||
<div id="labelContainer"/>
|
||||
<div id="drawContainer"/>
|
||||
<div id="console-container"/>
|
||||
<div style="clear: left;"/>
|
||||
|
||||
<h1 class="expand" data-content-id="expandContent" style="font-size: 1.3em;"
|
||||
>About Drawboard WebSocket Example</h1>
|
||||
<div id="expandContent">
|
||||
<p>
|
||||
This drawboard is a page where you can draw with your mouse or touch input
|
||||
(using different colors) and everybody else which has the page open will
|
||||
<em>immediately</em> see what you are drawing.<br/>
|
||||
If someone opens the page later, they will get the current room image (so they
|
||||
can see what was already drawn by other people).
|
||||
</p>
|
||||
<p>
|
||||
It uses asynchronous sending of messages so that it doesn't need separate threads
|
||||
for each client to send messages.<br/>
|
||||
Each "Room" (where the drawing happens) uses a ReentrantLock to synchronize access
|
||||
(currently, only a single Room is implemented).
|
||||
</p>
|
||||
<p>
|
||||
When you open the page, first you will receive a binary websocket message containing
|
||||
the current room image as PNG image. After that, you will receive string messages
|
||||
that contain the drawing actions (line from x1,y1 to x2,y2).<br/>
|
||||
<small>Note that it currently only uses simple string messages instead of JSON because
|
||||
I did not want to introduce a dependency on a JSON lib.</small>
|
||||
</p>
|
||||
<p>
|
||||
It uses synchronization mechanisms to ensure that the final image will look the same
|
||||
for every user, regardless of what their network latency/speed is – e.g. if two user
|
||||
draw at the same time on the same place, the server will decide which line was the
|
||||
first one, and that will be reflected on every client.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
184
tomcat/webapps.dist/examples/websocket/echo.xhtml
Normal file
184
tomcat/webapps.dist/examples/websocket/echo.xhtml
Normal file
@@ -0,0 +1,184 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Apache Tomcat WebSocket Examples: Echo</title>
|
||||
<style type="text/css"><![CDATA[
|
||||
#connect-container {
|
||||
float: left;
|
||||
width: 400px
|
||||
}
|
||||
|
||||
#connect-container div {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#console-container {
|
||||
float: left;
|
||||
margin-left: 15px;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
#console {
|
||||
border: 1px solid #CCCCCC;
|
||||
border-right-color: #999999;
|
||||
border-bottom-color: #999999;
|
||||
height: 170px;
|
||||
overflow-y: scroll;
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#console p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
]]></style>
|
||||
<script type="text/javascript"><![CDATA[
|
||||
"use strict";
|
||||
|
||||
var ws = null;
|
||||
|
||||
function setConnected(connected) {
|
||||
document.getElementById('connect').disabled = connected;
|
||||
document.getElementById('disconnect').disabled = !connected;
|
||||
document.getElementById('echo').disabled = !connected;
|
||||
}
|
||||
|
||||
function connect() {
|
||||
var target = document.getElementById('target').value;
|
||||
if (target == '') {
|
||||
alert('Please select server side connection implementation.');
|
||||
return;
|
||||
}
|
||||
if ('WebSocket' in window) {
|
||||
ws = new WebSocket(target);
|
||||
} else if ('MozWebSocket' in window) {
|
||||
ws = new MozWebSocket(target);
|
||||
} else {
|
||||
alert('WebSocket is not supported by this browser.');
|
||||
return;
|
||||
}
|
||||
ws.onopen = function () {
|
||||
setConnected(true);
|
||||
log('Info: WebSocket connection opened.');
|
||||
};
|
||||
ws.onmessage = function (event) {
|
||||
log('Received: ' + event.data);
|
||||
};
|
||||
ws.onclose = function (event) {
|
||||
setConnected(false);
|
||||
log('Info: WebSocket connection closed, Code: ' + event.code + (event.reason == "" ? "" : ", Reason: " + event.reason));
|
||||
};
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (ws != null) {
|
||||
ws.close();
|
||||
ws = null;
|
||||
}
|
||||
setConnected(false);
|
||||
}
|
||||
|
||||
function echo() {
|
||||
if (ws != null) {
|
||||
var message = document.getElementById('message').value;
|
||||
log('Sent: ' + message);
|
||||
ws.send(message);
|
||||
} else {
|
||||
alert('WebSocket connection not established, please connect.');
|
||||
}
|
||||
}
|
||||
|
||||
function updateTarget(target) {
|
||||
if (window.location.protocol == 'http:') {
|
||||
document.getElementById('target').value = 'ws://' + window.location.host + target;
|
||||
} else {
|
||||
document.getElementById('target').value = 'wss://' + window.location.host + target;
|
||||
}
|
||||
}
|
||||
|
||||
function log(message) {
|
||||
var console = document.getElementById('console');
|
||||
var p = document.createElement('p');
|
||||
p.style.wordWrap = 'break-word';
|
||||
p.appendChild(document.createTextNode(message));
|
||||
console.appendChild(p);
|
||||
while (console.childNodes.length > 25) {
|
||||
console.removeChild(console.firstChild);
|
||||
}
|
||||
console.scrollTop = console.scrollHeight;
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Remove elements with "noscript" class - <noscript> is not allowed in XHTML
|
||||
var noscripts = document.getElementsByClassName("noscript");
|
||||
for (var i = 0; i < noscripts.length; i++) {
|
||||
noscripts[i].parentNode.removeChild(noscripts[i]);
|
||||
}
|
||||
}, false);
|
||||
]]></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="noscript"><h2 style="color: #ff0000">Seems your browser doesn't support JavaScript! Websockets rely on JavaScript being enabled. Please enable
|
||||
JavaScript and reload this page!</h2></div>
|
||||
<div>
|
||||
<div id="connect-container">
|
||||
<div>
|
||||
<span>Connect to service implemented using:</span>
|
||||
<br/>
|
||||
<!-- echo example using new programmatic API on the server side -->
|
||||
<input id="radio1" type="radio" name="group1" value="/examples/websocket/echoProgrammatic"
|
||||
onclick="updateTarget(this.value);"/> <label for="radio1">programmatic API</label>
|
||||
<br/>
|
||||
<!-- echo example using new annotation API on the server side -->
|
||||
<input id="radio2" type="radio" name="group1" value="/examples/websocket/echoAnnotation"
|
||||
onclick="updateTarget(this.value);"/> <label for="radio2">annotation API (basic)</label>
|
||||
<br/>
|
||||
<!-- echo example using new annotation API on the server side -->
|
||||
<input id="radio3" type="radio" name="group1" value="/examples/websocket/echoStreamAnnotation"
|
||||
onclick="updateTarget(this.value);"/> <label for="radio3">annotation API (stream)</label>
|
||||
<br/>
|
||||
<!-- echo example using new annotation API on the server side -->
|
||||
<!-- Disabled by default -->
|
||||
<!--
|
||||
<input id="radio4" type="radio" name="group1" value="/examples/websocket/echoAsyncAnnotation"
|
||||
onclick="updateTarget(this.value);"/> <label for="radio4">annotation API (async)</label>
|
||||
-->
|
||||
</div>
|
||||
<div>
|
||||
<input id="target" type="text" size="40" style="width: 350px"/>
|
||||
</div>
|
||||
<div>
|
||||
<button id="connect" onclick="connect();">Connect</button>
|
||||
<button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
|
||||
</div>
|
||||
<div>
|
||||
<textarea id="message" style="width: 350px">Here is a message!</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<button id="echo" onclick="echo();" disabled="disabled">Echo message</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="console-container">
|
||||
<div id="console"/>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
32
tomcat/webapps.dist/examples/websocket/index.xhtml
Normal file
32
tomcat/webapps.dist/examples/websocket/index.xhtml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Apache Tomcat WebSocket Examples</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Apache Tomcat WebSocket Examples</h1>
|
||||
<ul>
|
||||
<li><a href="echo.xhtml">Echo example</a></li>
|
||||
<li><a href="chat.xhtml">Chat example</a></li>
|
||||
<li><a href="snake.xhtml">Multiplayer snake example</a></li>
|
||||
<li><a href="drawboard.xhtml">Multiplayer drawboard example</a></li>
|
||||
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
266
tomcat/webapps.dist/examples/websocket/snake.xhtml
Normal file
266
tomcat/webapps.dist/examples/websocket/snake.xhtml
Normal file
@@ -0,0 +1,266 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Apache Tomcat WebSocket Examples: Multiplayer Snake</title>
|
||||
<style type="text/css"><![CDATA[
|
||||
#playground {
|
||||
width: 640px;
|
||||
height: 480px;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
#console-container {
|
||||
float: left;
|
||||
margin-left: 15px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#console {
|
||||
border: 1px solid #CCCCCC;
|
||||
border-right-color: #999999;
|
||||
border-bottom-color: #999999;
|
||||
height: 480px;
|
||||
overflow-y: scroll;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#console p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="noscript"><h2 style="color: #ff0000">Seems your browser doesn't support JavaScript! Websockets rely on JavaScript being enabled. Please enable
|
||||
JavaScript and reload this page!</h2></div>
|
||||
<div style="float: left">
|
||||
<canvas id="playground" width="640" height="480"/>
|
||||
</div>
|
||||
<div id="console-container">
|
||||
<div id="console"/>
|
||||
</div>
|
||||
<script type="text/javascript"><![CDATA[
|
||||
"use strict";
|
||||
|
||||
var Game = {};
|
||||
|
||||
Game.fps = 30;
|
||||
Game.socket = null;
|
||||
Game.nextFrame = null;
|
||||
Game.interval = null;
|
||||
Game.direction = 'none';
|
||||
Game.gridSize = 10;
|
||||
|
||||
function Snake() {
|
||||
this.snakeBody = [];
|
||||
this.color = null;
|
||||
}
|
||||
|
||||
Snake.prototype.draw = function(context) {
|
||||
for (var id in this.snakeBody) {
|
||||
context.fillStyle = this.color;
|
||||
context.fillRect(this.snakeBody[id].x, this.snakeBody[id].y, Game.gridSize, Game.gridSize);
|
||||
}
|
||||
};
|
||||
|
||||
Game.initialize = function() {
|
||||
this.entities = [];
|
||||
var canvas = document.getElementById('playground');
|
||||
if (!canvas.getContext) {
|
||||
Console.log('Error: 2d canvas not supported by this browser.');
|
||||
return;
|
||||
}
|
||||
this.context = canvas.getContext('2d');
|
||||
window.addEventListener('keydown', function (e) {
|
||||
var code = e.keyCode;
|
||||
if (code > 36 && code < 41) {
|
||||
switch (code) {
|
||||
case 37:
|
||||
if (Game.direction != 'east') Game.setDirection('west');
|
||||
break;
|
||||
case 38:
|
||||
if (Game.direction != 'south') Game.setDirection('north');
|
||||
break;
|
||||
case 39:
|
||||
if (Game.direction != 'west') Game.setDirection('east');
|
||||
break;
|
||||
case 40:
|
||||
if (Game.direction != 'north') Game.setDirection('south');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
if (window.location.protocol == 'http:') {
|
||||
Game.connect('ws://' + window.location.host + '/examples/websocket/snake');
|
||||
} else {
|
||||
Game.connect('wss://' + window.location.host + '/examples/websocket/snake');
|
||||
}
|
||||
};
|
||||
|
||||
Game.setDirection = function(direction) {
|
||||
Game.direction = direction;
|
||||
Game.socket.send(direction);
|
||||
Console.log('Sent: Direction ' + direction);
|
||||
};
|
||||
|
||||
Game.startGameLoop = function() {
|
||||
if (window.webkitRequestAnimationFrame) {
|
||||
Game.nextFrame = function () {
|
||||
webkitRequestAnimationFrame(Game.run);
|
||||
};
|
||||
} else if (window.mozRequestAnimationFrame) {
|
||||
Game.nextFrame = function () {
|
||||
mozRequestAnimationFrame(Game.run);
|
||||
};
|
||||
} else {
|
||||
Game.interval = setInterval(Game.run, 1000 / Game.fps);
|
||||
}
|
||||
if (Game.nextFrame != null) {
|
||||
Game.nextFrame();
|
||||
}
|
||||
};
|
||||
|
||||
Game.stopGameLoop = function () {
|
||||
Game.nextFrame = null;
|
||||
if (Game.interval != null) {
|
||||
clearInterval(Game.interval);
|
||||
}
|
||||
};
|
||||
|
||||
Game.draw = function() {
|
||||
this.context.clearRect(0, 0, 640, 480);
|
||||
for (var id in this.entities) {
|
||||
this.entities[id].draw(this.context);
|
||||
}
|
||||
};
|
||||
|
||||
Game.addSnake = function(id, color) {
|
||||
Game.entities[id] = new Snake();
|
||||
Game.entities[id].color = color;
|
||||
};
|
||||
|
||||
Game.updateSnake = function(id, snakeBody) {
|
||||
if (typeof Game.entities[id] != "undefined") {
|
||||
Game.entities[id].snakeBody = snakeBody;
|
||||
}
|
||||
};
|
||||
|
||||
Game.removeSnake = function(id) {
|
||||
Game.entities[id] = null;
|
||||
// Force GC.
|
||||
delete Game.entities[id];
|
||||
};
|
||||
|
||||
Game.run = (function() {
|
||||
var skipTicks = 1000 / Game.fps, nextGameTick = (new Date).getTime();
|
||||
|
||||
return function() {
|
||||
while ((new Date).getTime() > nextGameTick) {
|
||||
nextGameTick += skipTicks;
|
||||
}
|
||||
Game.draw();
|
||||
if (Game.nextFrame != null) {
|
||||
Game.nextFrame();
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
Game.connect = (function(host) {
|
||||
if ('WebSocket' in window) {
|
||||
Game.socket = new WebSocket(host);
|
||||
} else if ('MozWebSocket' in window) {
|
||||
Game.socket = new MozWebSocket(host);
|
||||
} else {
|
||||
Console.log('Error: WebSocket is not supported by this browser.');
|
||||
return;
|
||||
}
|
||||
|
||||
Game.socket.onopen = function () {
|
||||
// Socket open.. start the game loop.
|
||||
Console.log('Info: WebSocket connection opened.');
|
||||
Console.log('Info: Press an arrow key to begin.');
|
||||
Game.startGameLoop();
|
||||
setInterval(function() {
|
||||
// Prevent server read timeout.
|
||||
Game.socket.send('ping');
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
Game.socket.onclose = function () {
|
||||
Console.log('Info: WebSocket closed.');
|
||||
Game.stopGameLoop();
|
||||
};
|
||||
|
||||
Game.socket.onmessage = function (message) {
|
||||
var packet = JSON.parse(message.data);
|
||||
switch (packet.type) {
|
||||
case 'update':
|
||||
for (var i = 0; i < packet.data.length; i++) {
|
||||
Game.updateSnake(packet.data[i].id, packet.data[i].body);
|
||||
}
|
||||
break;
|
||||
case 'join':
|
||||
for (var j = 0; j < packet.data.length; j++) {
|
||||
Game.addSnake(packet.data[j].id, packet.data[j].color);
|
||||
}
|
||||
break;
|
||||
case 'leave':
|
||||
Game.removeSnake(packet.id);
|
||||
break;
|
||||
case 'dead':
|
||||
Console.log('Info: Your snake is dead, bad luck!');
|
||||
Game.direction = 'none';
|
||||
break;
|
||||
case 'kill':
|
||||
Console.log('Info: Head shot!');
|
||||
break;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
var Console = {};
|
||||
|
||||
Console.log = (function(message) {
|
||||
var console = document.getElementById('console');
|
||||
var p = document.createElement('p');
|
||||
p.style.wordWrap = 'break-word';
|
||||
p.innerHTML = message;
|
||||
console.appendChild(p);
|
||||
while (console.childNodes.length > 25) {
|
||||
console.removeChild(console.firstChild);
|
||||
}
|
||||
console.scrollTop = console.scrollHeight;
|
||||
});
|
||||
|
||||
Game.initialize();
|
||||
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Remove elements with "noscript" class - <noscript> is not allowed in XHTML
|
||||
var noscripts = document.getElementsByClassName("noscript");
|
||||
for (var i = 0; i < noscripts.length; i++) {
|
||||
noscripts[i].parentNode.removeChild(noscripts[i]);
|
||||
}
|
||||
}, false);
|
||||
|
||||
]]></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user