升级Tomcat版本 apache-tomcat-7.0.77
diff --git a/tomcat-cas/webapps/examples/websocket/chat.xhtml b/tomcat-cas/webapps/examples/websocket/chat.xhtml
new file mode 100644
index 0000000..5e31dab
--- /dev/null
+++ b/tomcat-cas/webapps/examples/websocket/chat.xhtml
@@ -0,0 +1,134 @@
+<?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="application/javascript"><![CDATA[

+        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>
\ No newline at end of file
diff --git a/tomcat-cas/webapps/examples/websocket/drawboard.xhtml b/tomcat-cas/webapps/examples/websocket/drawboard.xhtml
new file mode 100644
index 0000000..342bef4
--- /dev/null
+++ b/tomcat-cas/webapps/examples/websocket/drawboard.xhtml
@@ -0,0 +1,897 @@
+<?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="application/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, lastInChain) {

+                    this.type = type;

+                    this.color = color;

+                    this.thickness = thickness;

+                    this.x1 = x1;

+                    this.y1 = y1;

+                    this.x2 = x2;

+                    this.y2 = y2;

+                    this.lastInChain = lastInChain;

+

+                    function ellipse(ctx, x, y, w, h) {

+                        /* Drawing a 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 a 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]),

+                                                    elements[11] != "0");

+

+                                            // 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 curser 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, false);

+                                // 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];

+

+                                var path = new Path(drawType.id, availableColors[currentColorIndex],

+                                        availableThicknesses[currentThicknessIndex],

+                                        currentMouseX, currentMouseY, mouseX,

+                                        mouseY, true);

+                                // 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 + ","

+                            + (path.lastInChain ? "1" : "0");

+

+                    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 (this needs NIO or APR connector to be used).<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>
\ No newline at end of file
diff --git a/tomcat-cas/webapps/examples/websocket/echo.xhtml b/tomcat-cas/webapps/examples/websocket/echo.xhtml
new file mode 100644
index 0000000..6153bff
--- /dev/null
+++ b/tomcat-cas/webapps/examples/websocket/echo.xhtml
@@ -0,0 +1,169 @@
+<?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="application/javascript"><![CDATA[

+        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>

+            <!-- 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>

+            <!-- 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</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>
\ No newline at end of file
diff --git a/tomcat-cas/webapps/examples/websocket/index.xhtml b/tomcat-cas/webapps/examples/websocket/index.xhtml
new file mode 100644
index 0000000..2b8d8cd
--- /dev/null
+++ b/tomcat-cas/webapps/examples/websocket/index.xhtml
@@ -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>
\ No newline at end of file
diff --git a/tomcat-cas/webapps/examples/websocket/snake.xhtml b/tomcat-cas/webapps/examples/websocket/snake.xhtml
new file mode 100644
index 0000000..86d1b3c
--- /dev/null
+++ b/tomcat-cas/webapps/examples/websocket/snake.xhtml
@@ -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="application/javascript"><![CDATA[

+

+        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 = [];

+            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) {

+                // _Potential_ security hole, consider using json lib to parse data in production.

+                var packet = eval('(' + 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>
\ No newline at end of file