14 changed files with 738 additions and 0 deletions
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<parent> |
||||
<artifactId>blade-ops</artifactId> |
||||
<groupId>org.springblade</groupId> |
||||
<version>2.2.0.RELEASE</version> |
||||
</parent> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<artifactId>blade-websocket</artifactId> |
||||
<name>${project.artifactId}</name> |
||||
<version>${bladex.project.version}</version> |
||||
<packaging>jar</packaging> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springblade</groupId> |
||||
<artifactId>blade-core-launch</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-websocket</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-amqp</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-reactor-netty</artifactId> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
|
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>com.spotify</groupId> |
||||
<artifactId>docker-maven-plugin</artifactId> |
||||
<version>${docker.plugin.version}</version> |
||||
<configuration> |
||||
<skipDockerBuild>true</skipDockerBuild> |
||||
</configuration> |
||||
</plugin> |
||||
<plugin> |
||||
<groupId>org.apache.maven.plugins</groupId> |
||||
<artifactId>maven-antrun-plugin</artifactId> |
||||
<executions> |
||||
<execution> |
||||
<phase>package</phase> |
||||
<goals> |
||||
<goal>run</goal> |
||||
</goals> |
||||
<configuration> |
||||
<tasks> |
||||
<!--suppress UnresolvedMavenProperty --> |
||||
<copy overwrite="true" |
||||
tofile="${session.executionRootDirectory}/target/${project.artifactId}.jar" |
||||
file="${project.build.directory}/${project.artifactId}.jar" /> |
||||
</tasks> |
||||
</configuration> |
||||
</execution> |
||||
</executions> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
|
||||
</project> |
@ -0,0 +1,18 @@
|
||||
package org.springblade.websocket; |
||||
|
||||
import org.springblade.core.launch.BladeApplication; |
||||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
|
||||
/** |
||||
* WebSocketApplication |
||||
* |
||||
* @author Chill |
||||
*/ |
||||
@SpringBootApplication |
||||
public class WebSocketApplication { |
||||
|
||||
public static void main(String[] args) { |
||||
BladeApplication.run("blade-websocket", WebSocketApplication.class, args); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,36 @@
|
||||
package org.springblade.websocket.config; |
||||
|
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry; |
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; |
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry; |
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; |
||||
|
||||
/** |
||||
* WebSocketConfig |
||||
* |
||||
* @author rajeevkumarsingh |
||||
*/ |
||||
@Configuration |
||||
@EnableWebSocketMessageBroker |
||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { |
||||
|
||||
@Override |
||||
public void registerStompEndpoints(StompEndpointRegistry registry) { |
||||
registry.addEndpoint("/ws").withSockJS(); |
||||
} |
||||
|
||||
@Override |
||||
public void configureMessageBroker(MessageBrokerRegistry registry) { |
||||
registry.setApplicationDestinationPrefixes("/app"); |
||||
// Enables a simple in-memory broker
|
||||
registry.enableSimpleBroker("/topic"); |
||||
|
||||
// Use this for enabling a Full featured broker like RabbitMQ
|
||||
/*registry.enableStompBrokerRelay("/topic") |
||||
.setRelayHost("localhost") |
||||
.setRelayPort(61613) |
||||
.setClientLogin("guest") |
||||
.setClientPasscode("guest");*/ |
||||
} |
||||
} |
@ -0,0 +1,33 @@
|
||||
package org.springblade.websocket.controller; |
||||
|
||||
import org.springblade.websocket.model.ChatMessage; |
||||
import org.springframework.messaging.handler.annotation.MessageMapping; |
||||
import org.springframework.messaging.handler.annotation.Payload; |
||||
import org.springframework.messaging.handler.annotation.SendTo; |
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor; |
||||
import org.springframework.stereotype.Controller; |
||||
|
||||
/** |
||||
* ChatController |
||||
* |
||||
* @author rajeevkumarsingh |
||||
*/ |
||||
@Controller |
||||
public class ChatController { |
||||
|
||||
@MessageMapping("/chat.sendMessage") |
||||
@SendTo("/topic/public") |
||||
public ChatMessage sendMessage(@Payload ChatMessage chatMessage) { |
||||
return chatMessage; |
||||
} |
||||
|
||||
@MessageMapping("/chat.addUser") |
||||
@SendTo("/topic/public") |
||||
public ChatMessage addUser(@Payload ChatMessage chatMessage, |
||||
SimpMessageHeaderAccessor headerAccessor) { |
||||
// Add username in web socket session
|
||||
headerAccessor.getSessionAttributes().put("username", chatMessage.getSender()); |
||||
return chatMessage; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,45 @@
|
||||
package org.springblade.websocket.event; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springblade.websocket.model.ChatMessage; |
||||
import org.springframework.context.event.EventListener; |
||||
import org.springframework.messaging.simp.SimpMessageSendingOperations; |
||||
import org.springframework.messaging.simp.stomp.StompHeaderAccessor; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.web.socket.messaging.SessionConnectedEvent; |
||||
import org.springframework.web.socket.messaging.SessionDisconnectEvent; |
||||
|
||||
/** |
||||
* WebSocketEventListener |
||||
* |
||||
* @author rajeevkumarsingh |
||||
*/ |
||||
@Slf4j |
||||
@Component |
||||
@AllArgsConstructor |
||||
public class WebSocketEventListener { |
||||
|
||||
private SimpMessageSendingOperations messagingTemplate; |
||||
|
||||
@EventListener |
||||
public void handleWebSocketConnectListener(SessionConnectedEvent event) { |
||||
log.info("Received a new web socket connection"); |
||||
} |
||||
|
||||
@EventListener |
||||
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) { |
||||
StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage()); |
||||
|
||||
String username = (String) headerAccessor.getSessionAttributes().get("username"); |
||||
if (username != null) { |
||||
log.info("User Disconnected : " + username); |
||||
|
||||
ChatMessage chatMessage = new ChatMessage(); |
||||
chatMessage.setType(ChatMessage.MessageType.LEAVE); |
||||
chatMessage.setSender(username); |
||||
|
||||
messagingTemplate.convertAndSend("/topic/public", chatMessage); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,32 @@
|
||||
package org.springblade.websocket.model; |
||||
|
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* ChatMessage |
||||
* |
||||
* @author rajeevkumarsingh |
||||
*/ |
||||
@Data |
||||
public class ChatMessage { |
||||
|
||||
public enum MessageType { |
||||
/** |
||||
* 聊天 |
||||
*/ |
||||
CHAT, |
||||
/** |
||||
* 加入 |
||||
*/ |
||||
JOIN, |
||||
/** |
||||
* 离开 |
||||
*/ |
||||
LEAVE |
||||
} |
||||
|
||||
private MessageType type; |
||||
private String content; |
||||
private String sender; |
||||
|
||||
} |
@ -0,0 +1,11 @@
|
||||
server: |
||||
port: 7006 |
||||
undertow: |
||||
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 |
||||
io-threads: 4 |
||||
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 |
||||
worker-threads: 20 |
||||
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 |
||||
buffer-size: 1024 |
||||
# 是否分配的直接内存 |
||||
direct-buffers: true |
@ -0,0 +1,8 @@
|
||||
${AnsiColor.BLUE} ______ _ _ ___ ___ |
||||
${AnsiColor.BLUE} | ___ \| | | | \ \ / / |
||||
${AnsiColor.BLUE} | |_/ /| | __ _ __| | ___ \ V / |
||||
${AnsiColor.BLUE} | ___ \| | / _` | / _` | / _ \ > < |
||||
${AnsiColor.BLUE} | |_/ /| || (_| || (_| || __/ / . \ |
||||
${AnsiColor.BLUE} \____/ |_| \__,_| \__,_| \___|/__/ \__\ |
||||
|
||||
${AnsiColor.BLUE}:: BladeX :: ${spring.application.name}:${AnsiColor.RED}${blade.env}${AnsiColor.BLUE} :: Running SpringBoot ${spring-boot.version} :: ${AnsiColor.BRIGHT_BLACK} |
@ -0,0 +1,298 @@
|
||||
* { |
||||
-webkit-box-sizing: border-box; |
||||
-moz-box-sizing: border-box; |
||||
box-sizing: border-box; |
||||
} |
||||
|
||||
html,body { |
||||
height: 100%; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
body { |
||||
margin: 0; |
||||
padding: 0; |
||||
font-weight: 400; |
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; |
||||
font-size: 1rem; |
||||
line-height: 1.58; |
||||
color: #333; |
||||
background-color: #f4f4f4; |
||||
height: 100%; |
||||
} |
||||
|
||||
body:before { |
||||
height: 50%; |
||||
width: 100%; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
background: #128ff2; |
||||
content: ""; |
||||
z-index: 0; |
||||
} |
||||
|
||||
.clearfix:after { |
||||
display: block; |
||||
content: ""; |
||||
clear: both; |
||||
} |
||||
|
||||
.hidden { |
||||
display: none; |
||||
} |
||||
|
||||
.form-control { |
||||
width: 100%; |
||||
min-height: 38px; |
||||
font-size: 15px; |
||||
border: 1px solid #c8c8c8; |
||||
} |
||||
|
||||
.form-group { |
||||
margin-bottom: 15px; |
||||
} |
||||
|
||||
input { |
||||
padding-left: 10px; |
||||
outline: none; |
||||
} |
||||
|
||||
h1, h2, h3, h4, h5, h6 { |
||||
margin-top: 20px; |
||||
margin-bottom: 20px; |
||||
} |
||||
|
||||
h1 { |
||||
font-size: 1.7em; |
||||
} |
||||
|
||||
a { |
||||
color: #128ff2; |
||||
} |
||||
|
||||
button { |
||||
box-shadow: none; |
||||
border: 1px solid transparent; |
||||
font-size: 14px; |
||||
outline: none; |
||||
line-height: 100%; |
||||
white-space: nowrap; |
||||
vertical-align: middle; |
||||
padding: 0.6rem 1rem; |
||||
border-radius: 2px; |
||||
transition: all 0.2s ease-in-out; |
||||
cursor: pointer; |
||||
min-height: 38px; |
||||
} |
||||
|
||||
button.default { |
||||
background-color: #e8e8e8; |
||||
color: #333; |
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12); |
||||
} |
||||
|
||||
button.primary { |
||||
background-color: #128ff2; |
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12); |
||||
color: #fff; |
||||
} |
||||
|
||||
button.accent { |
||||
background-color: #ff4743; |
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12); |
||||
color: #fff; |
||||
} |
||||
|
||||
#username-page { |
||||
text-align: center; |
||||
} |
||||
|
||||
.username-page-container { |
||||
background: #fff; |
||||
box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); |
||||
border-radius: 2px; |
||||
width: 100%; |
||||
max-width: 500px; |
||||
display: inline-block; |
||||
margin-top: 42px; |
||||
vertical-align: middle; |
||||
position: relative; |
||||
padding: 35px 55px 35px; |
||||
min-height: 250px; |
||||
position: absolute; |
||||
top: 50%; |
||||
left: 0; |
||||
right: 0; |
||||
margin: 0 auto; |
||||
margin-top: -160px; |
||||
} |
||||
|
||||
.username-page-container .username-submit { |
||||
margin-top: 10px; |
||||
} |
||||
|
||||
|
||||
#chat-page { |
||||
position: relative; |
||||
height: 100%; |
||||
} |
||||
|
||||
.chat-container { |
||||
max-width: 700px; |
||||
margin-left: auto; |
||||
margin-right: auto; |
||||
background-color: #fff; |
||||
box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); |
||||
margin-top: 30px; |
||||
height: calc(100% - 60px); |
||||
max-height: 600px; |
||||
position: relative; |
||||
} |
||||
|
||||
#chat-page ul { |
||||
list-style-type: none; |
||||
background-color: #FFF; |
||||
margin: 0; |
||||
overflow: auto; |
||||
overflow-y: scroll; |
||||
padding: 0 20px 0px 20px; |
||||
height: calc(100% - 150px); |
||||
} |
||||
|
||||
#chat-page #messageForm { |
||||
padding: 20px; |
||||
} |
||||
|
||||
#chat-page ul li { |
||||
line-height: 1.5rem; |
||||
padding: 10px 20px; |
||||
margin: 0; |
||||
border-bottom: 1px solid #f4f4f4; |
||||
} |
||||
|
||||
#chat-page ul li p { |
||||
margin: 0; |
||||
} |
||||
|
||||
#chat-page .event-message { |
||||
width: 100%; |
||||
text-align: center; |
||||
clear: both; |
||||
} |
||||
|
||||
#chat-page .event-message p { |
||||
color: #777; |
||||
font-size: 14px; |
||||
word-wrap: break-word; |
||||
} |
||||
|
||||
#chat-page .chat-message { |
||||
padding-left: 68px; |
||||
position: relative; |
||||
} |
||||
|
||||
#chat-page .chat-message i { |
||||
position: absolute; |
||||
width: 42px; |
||||
height: 42px; |
||||
overflow: hidden; |
||||
left: 10px; |
||||
display: inline-block; |
||||
vertical-align: middle; |
||||
font-size: 18px; |
||||
line-height: 42px; |
||||
color: #fff; |
||||
text-align: center; |
||||
border-radius: 50%; |
||||
font-style: normal; |
||||
text-transform: uppercase; |
||||
} |
||||
|
||||
#chat-page .chat-message span { |
||||
color: #333; |
||||
font-weight: 600; |
||||
} |
||||
|
||||
#chat-page .chat-message p { |
||||
color: #43464b; |
||||
} |
||||
|
||||
#messageForm .input-group input { |
||||
float: left; |
||||
width: calc(100% - 85px); |
||||
} |
||||
|
||||
#messageForm .input-group button { |
||||
float: left; |
||||
width: 80px; |
||||
height: 38px; |
||||
margin-left: 5px; |
||||
} |
||||
|
||||
.chat-header { |
||||
text-align: center; |
||||
padding: 15px; |
||||
border-bottom: 1px solid #ececec; |
||||
} |
||||
|
||||
.chat-header h2 { |
||||
margin: 0; |
||||
font-weight: 500; |
||||
} |
||||
|
||||
.connecting { |
||||
padding-top: 5px; |
||||
text-align: center; |
||||
color: #777; |
||||
position: absolute; |
||||
top: 65px; |
||||
width: 100%; |
||||
} |
||||
|
||||
|
||||
@media screen and (max-width: 730px) { |
||||
|
||||
.chat-container { |
||||
margin-left: 10px; |
||||
margin-right: 10px; |
||||
margin-top: 10px; |
||||
} |
||||
} |
||||
|
||||
@media screen and (max-width: 480px) { |
||||
.chat-container { |
||||
height: calc(100% - 30px); |
||||
} |
||||
|
||||
.username-page-container { |
||||
width: auto; |
||||
margin-left: 15px; |
||||
margin-right: 15px; |
||||
padding: 25px; |
||||
} |
||||
|
||||
#chat-page ul { |
||||
height: calc(100% - 120px); |
||||
} |
||||
|
||||
#messageForm .input-group button { |
||||
width: 65px; |
||||
} |
||||
|
||||
#messageForm .input-group input { |
||||
width: calc(100% - 70px); |
||||
} |
||||
|
||||
.chat-header { |
||||
padding: 10px; |
||||
} |
||||
|
||||
.connecting { |
||||
top: 60px; |
||||
} |
||||
|
||||
.chat-header h2 { |
||||
font-size: 1.1em; |
||||
} |
||||
} |
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0"> |
||||
<title>Spring Boot WebSocket Chat Application | CalliCoder</title> |
||||
<link rel="stylesheet" href="/css/main.css" /> |
||||
</head> |
||||
<body> |
||||
<noscript> |
||||
<h2>Sorry! Your browser doesn't support Javascript</h2> |
||||
</noscript> |
||||
|
||||
<div id="username-page"> |
||||
<div class="username-page-container"> |
||||
<h1 class="title">Type your username</h1> |
||||
<form id="usernameForm" name="usernameForm"> |
||||
<div class="form-group"> |
||||
<input type="text" id="name" placeholder="Username" autocomplete="off" class="form-control" /> |
||||
</div> |
||||
<div class="form-group"> |
||||
<button type="submit" class="accent username-submit">Start Chatting</button> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
|
||||
<div id="chat-page" class="hidden"> |
||||
<div class="chat-container"> |
||||
<div class="chat-header"> |
||||
<h2>Spring WebSocket Chat Demo</h2> |
||||
</div> |
||||
<div class="connecting"> |
||||
Connecting... |
||||
</div> |
||||
<ul id="messageArea"> |
||||
|
||||
</ul> |
||||
<form id="messageForm" name="messageForm" nameForm="messageForm"> |
||||
<div class="form-group"> |
||||
<div class="input-group clearfix"> |
||||
<input type="text" id="message" placeholder="Type a message..." autocomplete="off" class="form-control"/> |
||||
<button type="submit" class="primary">Send</button> |
||||
</div> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
|
||||
<script src="/js/sockjs.min.js"></script> |
||||
<script src="/js/stomp.min.js"></script> |
||||
<script src="/js/main.js"></script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,121 @@
|
||||
'use strict'; |
||||
|
||||
var usernamePage = document.querySelector('#username-page'); |
||||
var chatPage = document.querySelector('#chat-page'); |
||||
var usernameForm = document.querySelector('#usernameForm'); |
||||
var messageForm = document.querySelector('#messageForm'); |
||||
var messageInput = document.querySelector('#message'); |
||||
var messageArea = document.querySelector('#messageArea'); |
||||
var connectingElement = document.querySelector('.connecting'); |
||||
|
||||
var stompClient = null; |
||||
var username = null; |
||||
|
||||
var colors = [ |
||||
'#2196F3', '#32c787', '#00BCD4', '#ff5652', |
||||
'#ffc107', '#ff85af', '#FF9800', '#39bbb0' |
||||
]; |
||||
|
||||
function connect(event) { |
||||
username = document.querySelector('#name').value.trim(); |
||||
|
||||
if(username) { |
||||
usernamePage.classList.add('hidden'); |
||||
chatPage.classList.remove('hidden'); |
||||
|
||||
var socket = new SockJS('/ws'); |
||||
stompClient = Stomp.over(socket); |
||||
|
||||
stompClient.connect({}, onConnected, onError); |
||||
} |
||||
event.preventDefault(); |
||||
} |
||||
|
||||
|
||||
function onConnected() { |
||||
// Subscribe to the Public Topic
|
||||
stompClient.subscribe('/topic/public', onMessageReceived); |
||||
|
||||
// Tell your username to the server
|
||||
stompClient.send("/app/chat.addUser", |
||||
{}, |
||||
JSON.stringify({sender: username, type: 'JOIN'}) |
||||
) |
||||
|
||||
connectingElement.classList.add('hidden'); |
||||
} |
||||
|
||||
|
||||
function onError(error) { |
||||
connectingElement.textContent = 'Could not connect to WebSocket server. Please refresh this page to try again!'; |
||||
connectingElement.style.color = 'red'; |
||||
} |
||||
|
||||
|
||||
function sendMessage(event) { |
||||
var messageContent = messageInput.value.trim(); |
||||
|
||||
if(messageContent && stompClient) { |
||||
var chatMessage = { |
||||
sender: username, |
||||
content: messageInput.value, |
||||
type: 'CHAT' |
||||
}; |
||||
|
||||
stompClient.send("/app/chat.sendMessage", {}, JSON.stringify(chatMessage)); |
||||
messageInput.value = ''; |
||||
} |
||||
event.preventDefault(); |
||||
} |
||||
|
||||
|
||||
function onMessageReceived(payload) { |
||||
var message = JSON.parse(payload.body); |
||||
|
||||
var messageElement = document.createElement('li'); |
||||
|
||||
if(message.type === 'JOIN') { |
||||
messageElement.classList.add('event-message'); |
||||
message.content = message.sender + ' joined!'; |
||||
} else if (message.type === 'LEAVE') { |
||||
messageElement.classList.add('event-message'); |
||||
message.content = message.sender + ' left!'; |
||||
} else { |
||||
messageElement.classList.add('chat-message'); |
||||
|
||||
var avatarElement = document.createElement('i'); |
||||
var avatarText = document.createTextNode(message.sender[0]); |
||||
avatarElement.appendChild(avatarText); |
||||
avatarElement.style['background-color'] = getAvatarColor(message.sender); |
||||
|
||||
messageElement.appendChild(avatarElement); |
||||
|
||||
var usernameElement = document.createElement('span'); |
||||
var usernameText = document.createTextNode(message.sender); |
||||
usernameElement.appendChild(usernameText); |
||||
messageElement.appendChild(usernameElement); |
||||
} |
||||
|
||||
var textElement = document.createElement('p'); |
||||
var messageText = document.createTextNode(message.content); |
||||
textElement.appendChild(messageText); |
||||
|
||||
messageElement.appendChild(textElement); |
||||
|
||||
messageArea.appendChild(messageElement); |
||||
messageArea.scrollTop = messageArea.scrollHeight; |
||||
} |
||||
|
||||
|
||||
function getAvatarColor(messageSender) { |
||||
var hash = 0; |
||||
for (var i = 0; i < messageSender.length; i++) { |
||||
hash = 31 * hash + messageSender.charCodeAt(i); |
||||
} |
||||
|
||||
var index = Math.abs(hash % colors.length); |
||||
return colors[index]; |
||||
} |
||||
|
||||
usernameForm.addEventListener('submit', connect, true) |
||||
messageForm.addEventListener('submit', sendMessage, true) |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue