├── .gitignore ├── .settings ├── org.eclipse.wst.jsdt.ui.superType.name ├── org.eclipse.wst.validation.prefs ├── org.eclipse.wst.jsdt.ui.superType.container ├── org.eclipse.m2e.core.prefs ├── org.eclipse.core.resources.prefs ├── org.eclipse.wst.common.project.facet.core.xml ├── org.eclipse.jdt.core.prefs ├── .jsdtscope └── org.eclipse.wst.common.component ├── src └── main │ ├── webapp │ ├── asserts │ │ ├── fonts │ │ │ ├── icomoon.eot │ │ │ ├── icomoon.ttf │ │ │ ├── icomoon.woff │ │ │ ├── chatfonts.eot │ │ │ ├── chatfonts.ttf │ │ │ ├── chatfonts.woff │ │ │ ├── icomoon.svg │ │ │ └── chatfonts.svg │ │ ├── js │ │ │ ├── chat-ui.js │ │ │ ├── chat-login.js │ │ │ ├── chat-register.js │ │ │ └── chat-websocket.js │ │ ├── css │ │ │ ├── chat-register.css │ │ │ ├── chat-public.css │ │ │ ├── chat-login.css │ │ │ └── chat-main.css │ │ ├── style.css │ │ └── selection.json │ ├── WEB-INF │ │ └── web.xml │ ├── login.jsp │ ├── register.jsp │ └── chat.jsp │ ├── java │ └── com │ │ └── chatroom │ │ ├── interfaces │ │ ├── IJsonSeriserialize.java │ │ └── IQueriable.java │ │ ├── persiststorage │ │ ├── DBInfo.java │ │ ├── DataBase.java │ │ └── ExcuteQueryObject.java │ │ ├── servlet │ │ ├── JsonServlet.java │ │ ├── LogoutServlet.java │ │ ├── InformationServlet.java │ │ ├── UserListServlet.java │ │ ├── UserValidation.java │ │ ├── CloseChatServlet.java │ │ ├── LoginServlet.java │ │ ├── SingleChatServlet.java │ │ └── RegisterServlet.java │ │ ├── utils │ │ ├── MD5Utils.java │ │ └── ResponseInformation.java │ │ ├── business │ │ └── UserBusiness.java │ │ ├── models │ │ ├── User.java │ │ └── ChatMessage.java │ │ ├── dbhelper │ │ └── UserHelper.java │ │ └── controller │ │ └── ChatController.java │ └── resources │ ├── sqls │ └── createUserTable.sql │ └── DesignDiagrams │ ├── packagesDiagrams.mgp │ ├── InteractionDiagrams.mgi │ └── ClassDiagram.mgc ├── README.md ├── .project ├── .classpath └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | /target/* 3 | /target/ 4 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.jsdt.ui.superType.name: -------------------------------------------------------------------------------- 1 | Window -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.validation.prefs: -------------------------------------------------------------------------------- 1 | disabled=06target 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.jsdt.ui.superType.container: -------------------------------------------------------------------------------- 1 | org.eclipse.wst.jsdt.launching.baseBrowserLibrary -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zipzou/chat-online/HEAD/src/main/webapp/asserts/fonts/icomoon.eot -------------------------------------------------------------------------------- /src/main/webapp/asserts/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zipzou/chat-online/HEAD/src/main/webapp/asserts/fonts/icomoon.ttf -------------------------------------------------------------------------------- /src/main/webapp/asserts/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zipzou/chat-online/HEAD/src/main/webapp/asserts/fonts/icomoon.woff -------------------------------------------------------------------------------- /src/main/webapp/asserts/fonts/chatfonts.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zipzou/chat-online/HEAD/src/main/webapp/asserts/fonts/chatfonts.eot -------------------------------------------------------------------------------- /src/main/webapp/asserts/fonts/chatfonts.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zipzou/chat-online/HEAD/src/main/webapp/asserts/fonts/chatfonts.ttf -------------------------------------------------------------------------------- /src/main/webapp/asserts/fonts/chatfonts.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zipzou/chat-online/HEAD/src/main/webapp/asserts/fonts/chatfonts.woff -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java/com/chatroom/persiststorage/DBInfo.java=UTF-8 3 | encoding/=UTF-8 4 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/interfaces/IJsonSeriserialize.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.interfaces; 2 | 3 | import org.json.JSONObject; 4 | 5 | public interface IJsonSeriserialize { 6 | public JSONObject toJson(); 7 | public void readFromJson(JSONObject json); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/sqls/createUserTable.sql: -------------------------------------------------------------------------------- 1 | create database dbchatroom;/*创建数据库*/ 2 | 3 | --创建用户表 4 | create table ChatUser(username CHAR(20) PRIMARY KEY, password CHAR(64) NOT NULL, nickname VARCHAR(20), sex CHAR(2)); 5 | 6 | create view ViewUserToChat as select username, password, nickname, sex from ChatUser; 7 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.project.facet.core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatOnline 2 | 使用WebSocket及JAVA搭建的在线聊天室系统,包含服务端和客户端代码 3 | 4 | ## 改进 5 | 6 | + 修正了数据库的错误 7 | + 修正了WebSocket服务器错误 8 | 9 | *其他相关Issue,请查看[Issue](https://github.com/Frank17/ChatOnline/issues),或自行提Issue,或发送email至:[frankdeveloper@126.com](mailto://frankdeveloper@126.com)* 10 | 11 | > 若您为发布版,请将js中的IP地址改为对应的IP或域名 12 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/interfaces/IQueriable.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.interfaces; 2 | 3 | import java.util.Map; 4 | 5 | public interface IQueriable { 6 | public Map[] select(Object destObj); 7 | public boolean update(Object oldObj, Object newObj); 8 | public int delete(Object destObj); 9 | public boolean insert(Object oringinObj); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/js/chat-ui.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | 5 | function inputFocus(elem){ 6 | var parentElem = elem.parentElement 7 | parentElem.className = parentElem.className.replace(/\s*invalide-input\s*/, ' ') 8 | parentElem.className += ' input-focused '; 9 | } 10 | 11 | function inputBlur(elem){ 12 | var parentElem = elem.parentElement 13 | parentElem.className = parentElem.className.replace(/\s*input-focused\s*/, ' '); 14 | } -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 4 | org.eclipse.jdt.core.compiler.compliance=1.7 5 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 6 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 7 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 8 | org.eclipse.jdt.core.compiler.source=1.7 9 | -------------------------------------------------------------------------------- /.settings/.jsdtscope: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/persiststorage/DBInfo.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.persiststorage; 2 | 3 | public class DBInfo { 4 | private final String url = "jdbc:mysql://10.255.94.133:3306/dbchatroom?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT"; 5 | private final String username = "frank"; 6 | private final String password = "zouzhipeng"; 7 | private final String driver = "com.mysql.cj.jdbc.Driver"; 8 | public String getUrl() { 9 | return url; 10 | } 11 | public String getUsername() { 12 | return username; 13 | } 14 | public String getPassword() { 15 | return password; 16 | } 17 | public String getDriver() { 18 | return driver; 19 | } 20 | public DBInfo() { 21 | super(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.component: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/servlet/JsonServlet.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.servlet; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletRequest; 9 | 10 | public abstract class JsonServlet extends HttpServlet { 11 | 12 | /** 13 | * 14 | */ 15 | private static final long serialVersionUID = 8799894392674144146L; 16 | 17 | public String ReadFromStream(HttpServletRequest request) { 18 | try { 19 | BufferedReader bufferedReader = request.getReader(); 20 | char []tmpbuf = new char[2 * 1024]; 21 | StringBuffer buffer = new StringBuffer(); 22 | while (bufferedReader.read(tmpbuf) != -1) { 23 | buffer.append(tmpbuf); 24 | } 25 | return buffer.toString(); 26 | } catch (IOException e) { 27 | e.printStackTrace(); 28 | return null; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/utils/MD5Utils.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.utils; 2 | 3 | import java.security.MessageDigest; 4 | 5 | public class MD5Utils { 6 | public static String md5Encode(String inStr) throws Exception { 7 | MessageDigest md5 = null; 8 | try { 9 | md5 = MessageDigest.getInstance("MD5"); 10 | } catch (Exception e) { 11 | System.out.println(e.toString()); 12 | e.printStackTrace(); 13 | return ""; 14 | } 15 | 16 | byte[] byteArray = inStr.getBytes("UTF-8"); 17 | byte[] md5Bytes = md5.digest(byteArray); 18 | StringBuffer hexValue = new StringBuffer(); 19 | for (int i = 0; i < md5Bytes.length; i++) { 20 | int val = ((int) md5Bytes[i]) & 0xff; 21 | if (val < 16) { 22 | hexValue.append("0"); 23 | } 24 | hexValue.append(Integer.toHexString(val)); 25 | } 26 | return hexValue.toString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/utils/ResponseInformation.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.utils; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | public class ResponseInformation { 7 | public static String getSuccessInformation(){ 8 | JSONObject jo = new JSONObject(); 9 | try { 10 | jo.put("status", "success"); 11 | } catch (JSONException e) { 12 | e.printStackTrace(); 13 | } 14 | return jo.toString(); 15 | } 16 | 17 | public static String getErrorInformation(String reason) { 18 | JSONObject jo = new JSONObject(); 19 | try { 20 | jo.put("status", "error"); 21 | jo.put("reason", reason); 22 | return jo.toString(); 23 | } catch (Exception e) { 24 | e.printStackTrace(); 25 | } 26 | return jo.toString(); 27 | } 28 | 29 | public static String getErrorInformation(Exception ex) { 30 | JSONObject jo = new JSONObject(); 31 | try { 32 | jo.put("status", "success"); 33 | jo.put("reason", ex.getMessage()); 34 | } catch (Exception e) { 35 | e.getSuppressed(); 36 | } 37 | return jo.toString(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/servlet/LogoutServlet.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.servlet; 2 | 3 | import java.io.IOException; 4 | import javax.servlet.ServletException; 5 | import javax.servlet.annotation.WebServlet; 6 | import javax.servlet.http.HttpServlet; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | /** 11 | * Servlet implementation class LogoutServlet 12 | */ 13 | @WebServlet("/logout") 14 | public class LogoutServlet extends JsonServlet { 15 | private static final long serialVersionUID = 1L; 16 | 17 | /** 18 | * @see HttpServlet#HttpServlet() 19 | */ 20 | public LogoutServlet() { 21 | super(); 22 | // TODO Auto-generated constructor stub 23 | } 24 | 25 | /** 26 | * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 27 | */ 28 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 29 | request.getSession().removeAttribute(LoginServlet.LOGINED_USER_SESSION_ATTR); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/persiststorage/DataBase.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.persiststorage; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.SQLException; 6 | 7 | public class DataBase { 8 | static DBInfo dbinfo = new DBInfo(); 9 | Connection sqlConnection = null; 10 | public DataBase() { 11 | super(); 12 | } 13 | public boolean openConnection() { 14 | try { 15 | Class.forName(dbinfo.getDriver()); 16 | sqlConnection = DriverManager.getConnection(dbinfo.getUrl(), dbinfo.getUsername(), dbinfo.getPassword()); 17 | if (null == sqlConnection) 18 | return false; 19 | return true; 20 | } catch (Exception e) { 21 | System.err.println(e); 22 | return false; 23 | } 24 | } 25 | 26 | public boolean closeConnection() { 27 | if (null != sqlConnection){ 28 | try { 29 | sqlConnection.close(); 30 | } catch (SQLException e) { 31 | // TODO Auto-generated catch block 32 | e.printStackTrace(); 33 | return false; 34 | } 35 | return true; 36 | } 37 | return true; 38 | } 39 | public Connection getSqlConnection() { 40 | return sqlConnection; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | chat-online-maven 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.wst.common.project.facet.core.builder 15 | 16 | 17 | 18 | 19 | org.eclipse.wst.validation.validationbuilder 20 | 21 | 22 | 23 | 24 | org.eclipse.m2e.core.maven2Builder 25 | 26 | 27 | 28 | 29 | 30 | org.eclipse.jem.workbench.JavaEMFNature 31 | org.eclipse.wst.common.modulecore.ModuleCoreNature 32 | org.eclipse.jdt.core.javanature 33 | org.eclipse.m2e.core.maven2Nature 34 | org.eclipse.wst.common.project.facet.core.nature 35 | org.eclipse.wst.jsdt.core.jsNature 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ChatRoom 4 | 5 | login.html 6 | login.htm 7 | login.jsp 8 | default.html 9 | default.htm 10 | default.jsp 11 | 12 | 13 | RegisterPage 14 | /register.jsp 15 | 16 | 17 | LoginPage 18 | /login.jsp 19 | 20 | 21 | ChatPage 22 | /chat.jsp 23 | 24 | 25 | RegisterPage 26 | /Register 27 | 28 | 29 | ChatPage 30 | /Chat 31 | 32 | LoginPage/Login 33 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/js/chat-login.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | $(document).ready(function(){ 5 | $('#register').click(function(){ 6 | window.location.href = 'Register'; 7 | }) 8 | }) 9 | function onLogin(){ 10 | var username = $('#username').val(); 11 | var password = $('#password').val(); 12 | var user = { 13 | username: username, 14 | password: password, 15 | } 16 | $.ajax({ 17 | url: 'login', 18 | type: 'POST', 19 | data: JSON.stringify(user), 20 | dataType: 'JSON', 21 | contentType: 'application/json;charset=utf-8', 22 | success: function(msg) { 23 | if (msg.status != null && msg.status == "success") { 24 | window.location.href = "Chat" 25 | }else { 26 | alert(msg.reason) 27 | } 28 | }, 29 | error :function(msg) { 30 | console.log(msg) 31 | } 32 | }) 33 | } 34 | 35 | function checkUser(){ 36 | var username = $('#username').val() 37 | if (username == null || username.trim().length <= 0) { 38 | var elem = $('#username')[0].parentElement 39 | elem.className += ' invalide-input ' 40 | $('#username')[0].focus() 41 | return false 42 | } 43 | var password = $('#password').val() 44 | if (password == null || password.trim().length <= 0) { 45 | var elem = $('#password')[0].parentElement 46 | elem.className += ' invalide-input ' 47 | $('#password')[0].focus() 48 | return false 49 | } 50 | onLogin() 51 | return false; 52 | } -------------------------------------------------------------------------------- /src/main/java/com/chatroom/business/UserBusiness.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.business; 2 | 3 | import com.chatroom.dbhelper.UserHelper; 4 | import com.chatroom.models.User; 5 | 6 | public class UserBusiness { 7 | 8 | public UserBusiness() { 9 | super(); 10 | } 11 | 12 | public boolean authenticateUser(User user) { 13 | UserHelper helper = new UserHelper(); 14 | return helper.userExists(user); 15 | } 16 | 17 | public boolean authenticateUser(String username, String password) { 18 | User user = new User(); 19 | user.setUsername(username); 20 | user.setPassword(password); 21 | return new UserHelper().userExists(user); 22 | } 23 | 24 | public boolean registerUser(User user) { 25 | return new UserHelper().addUser(user); 26 | } 27 | 28 | public User queryUserWithUserName(String username) { 29 | Object[] obj = new UserHelper().queryUserWithName(username); 30 | if (obj != null && obj.length > 0) { 31 | if (obj[0] instanceof User) { 32 | return (User)obj[0]; 33 | } 34 | } 35 | return null; 36 | } 37 | 38 | public User[] queryUserWithUserNick(String nickname) { 39 | return new UserHelper().queryUserWithNick(nickname); 40 | } 41 | 42 | public String queryUserName(String username) { 43 | return null; 44 | } 45 | 46 | public String queryUserNick(String username) { 47 | return null; 48 | } 49 | 50 | public String queryUserPassword(String username) { 51 | return null; 52 | } 53 | 54 | public String queryUserSex(String username) { 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/resources/DesignDiagrams/packagesDiagrams.mgp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.chatroom 5 | chat-online-maven 6 | war 7 | 0.0.1-SNAPSHOT 8 | chat-online-maven Maven Webapp 9 | http://maven.apache.org 10 | 11 | 12 | junit 13 | junit 14 | 4.12 15 | test 16 | 17 | 18 | 19 | 20 | org.json 21 | json 22 | 20160810 23 | 24 | 25 | 26 | mysql 27 | mysql-connector-java 28 | 6.0.6 29 | 30 | 31 | 32 | ChatOnline 33 | compile 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-compiler-plugin 38 | 39 | 1.7 40 | 1.7 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/servlet/InformationServlet.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.servlet; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.annotation.WebServlet; 8 | import javax.servlet.http.HttpServlet; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | import org.json.JSONObject; 13 | 14 | import com.chatroom.business.UserBusiness; 15 | import com.chatroom.models.User; 16 | 17 | /** 18 | * Servlet implementation class InformationServlet 19 | */ 20 | @WebServlet("/information") 21 | public class InformationServlet extends JsonServlet { 22 | private static final long serialVersionUID = 1L; 23 | 24 | /** 25 | * @see HttpServlet#HttpServlet() 26 | */ 27 | public InformationServlet() { 28 | super(); 29 | 30 | } 31 | 32 | /** 33 | * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 34 | */ 35 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 36 | request.setCharacterEncoding("UTF-8"); 37 | response.setCharacterEncoding("UTF-8"); 38 | response.setContentType("application/json;charset=utf-8"); 39 | String username = request.getParameter("username"); 40 | try { 41 | User resultUser = new UserBusiness().queryUserWithUserName(username); 42 | resultUser.setPassword(null); 43 | PrintWriter writer = response.getWriter(); 44 | writer.println(resultUser.toJson().toString()); 45 | writer.close(); 46 | } catch (Exception e) { 47 | // TODO: handle exception 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/css/chat-register.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8"; 2 | 3 | .fm-register { 4 | width: 100%; 5 | margin-top: 120px; 6 | } 7 | 8 | .register-area { 9 | margin-top: 40px; 10 | } 11 | 12 | .fm-register .fm-group { 13 | width: 100%; 14 | display: block; 15 | height: 48px; 16 | border: 1px solid #999; 17 | margin-bottom: 40px; 18 | } 19 | 20 | .fm-register .fm-group a[class^='icon-'] { 21 | width: 48px; 22 | height: 48px; 23 | font-size: 24px; 24 | border-right: 1px solid #ccc; 25 | background: #f1edea; 26 | display: block; 27 | float: left; 28 | line-height: 48px; 29 | text-align: center; 30 | color: #33b3a1; 31 | } 32 | 33 | .fm-register .fm-group a.status-indicator { 34 | font-size: 24px; 35 | color: #16e363; 36 | margin-left: 8px; 37 | display: none; 38 | } 39 | 40 | .fm-register .fm-group a.show-indicator { 41 | display: inline; 42 | } 43 | 44 | .fm-register .fm-group input { 45 | height: 100%; 46 | width: calc(90% - 60px); 47 | width: -webkit-calc(90% - 60px); 48 | width: -moz-calc(90% - 60px); 49 | font-size: 1.1em; 50 | font-family: "Consolas", "微软雅黑", "Arial", "Heiti-TC"; 51 | padding-left: 12px; 52 | transition: box-shadow 0.18 ease-in-out; 53 | 54 | -webkit-transition: box-shadow 0.18 ease-in-out; 55 | -moz-transition: box-shadow 0.18 ease-in-out; 56 | border-radius: 0; 57 | -webkit-border-radius: 0; 58 | -moz-border-radius: 0; 59 | } 60 | 61 | .input-focused { 62 | box-shadow: 0px 0px 10px rgba(47, 197, 176, 0.85); 63 | } 64 | 65 | .operate-grid { 66 | width: 100%; 67 | margin-bottom: 48px; 68 | } 69 | 70 | .btn-primary { 71 | width: 100%; 72 | display: block; 73 | margin-top: 16px; 74 | } 75 | 76 | a.notify { 77 | margin-top: 16px; 78 | display: inline-block; 79 | color: #999; 80 | font-size: 12px; 81 | } -------------------------------------------------------------------------------- /src/main/webapp/asserts/css/chat-public.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8"; 2 | 3 | *{ 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | body { 9 | width: 100%; 10 | min-width: 300px; 11 | } 12 | 13 | .container { 14 | max-width: 600px; 15 | margin: 0 auto; 16 | margin-bottom: 32px; 17 | } 18 | 19 | input[type='button'], input[type='text'], input[type='password'], input[type='submit'] { 20 | -webkit-appearance: none; 21 | -moz-appearance: none; 22 | -ms-appearance: none; 23 | appearance: none; 24 | outline: none; 25 | border: none; 26 | -webkit-appearance:none; /*去除系统默认的样式*/ 27 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); /* 点击高亮的颜色*/ 28 | -webkit-border-radius: 0; 29 | } 30 | 31 | ul li { 32 | list-style: none; 33 | } 34 | 35 | 36 | .btn { 37 | width: 400px; 38 | height: 50px; 39 | background: #999; 40 | margin: 0 auto; 41 | display: block; 42 | color: #eee; 43 | font-size: 1.6em; 44 | transition: background 0.15s ease-in-out; 45 | -webkit-transition: background 0.15s ease-in-out; 46 | -moz-transition: background 0.15s ease-in-out; 47 | font-family: "Consolas", "微软雅黑"; 48 | } 49 | 50 | .btn-primary { 51 | background: #33b3a1; 52 | } 53 | 54 | .btn-primary:hover { 55 | background: #2ecbb5; 56 | } 57 | 58 | .btn-warning { 59 | background: #ea6060; 60 | } 61 | 62 | .btn-warning:hover { 63 | background: #f75a5a; 64 | } 65 | 66 | @media only screen 67 | and (min-device-width : 320px) 68 | and (max-device-width : 568px) 69 | and (orientation : portrait) {/* STYLES */ 70 | .container { 71 | max-width: 260px; 72 | margin: 0 auto; 73 | } 74 | } 75 | 76 | @media only screen 77 | and (min-device-width : 320px) 78 | and (max-device-width : 568px) 79 | and (orientation : landscape) { /* STYLES */ 80 | .container { 81 | max-width: 500px; 82 | margin: 0 auto; 83 | } 84 | } 85 | 86 | @media only screen and (min-width: 569px) and (max-width: 1366px){ 87 | .container { 88 | max-width: 600px; 89 | margin: 0 auto; 90 | } 91 | } -------------------------------------------------------------------------------- /src/main/java/com/chatroom/servlet/UserListServlet.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.servlet; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | import java.util.Set; 6 | 7 | import javax.servlet.ServletException; 8 | import javax.servlet.annotation.WebServlet; 9 | import javax.servlet.http.HttpServlet; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | import org.json.JSONArray; 14 | 15 | import com.chatroom.business.UserBusiness; 16 | import com.chatroom.controller.ChatController; 17 | import com.chatroom.models.User; 18 | import com.chatroom.utils.ResponseInformation; 19 | 20 | /** 21 | * Servlet implementation class UserListServlet 22 | */ 23 | @WebServlet("/userlist") 24 | public class UserListServlet extends HttpServlet { 25 | private static final long serialVersionUID = 1L; 26 | 27 | /** 28 | * @see HttpServlet#HttpServlet() 29 | */ 30 | public UserListServlet() { 31 | super(); 32 | // TODO Auto-generated constructor stub 33 | } 34 | 35 | /** 36 | * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 37 | */ 38 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 39 | Set usersOnline = ChatController.getConnectedUsers(); 40 | JSONArray allUsers = new JSONArray(); 41 | for (ChatController chatController : usersOnline) { 42 | User user = new UserBusiness().queryUserWithUserName(chatController.getUser().getUsername()); 43 | if (null != user) { 44 | user.setPassword(null); 45 | allUsers.put(user.toJson()); 46 | } 47 | } 48 | response.setCharacterEncoding("UTF-8"); 49 | response.setContentType("application/json;charset=utf-8"); 50 | PrintWriter writer = response.getWriter(); 51 | try { 52 | writer.println(allUsers.toString()); 53 | } catch (Exception e) { 54 | writer.println(ResponseInformation.getErrorInformation("系统发生异常!")); 55 | }finally { 56 | writer.close(); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/webapp/login.jsp: -------------------------------------------------------------------------------- 1 | <%@page import="com.chatroom.servlet.LoginServlet"%> 2 | <%@ page language="java" contentType="text/html; charset=UTF-8" 3 | pageEncoding="UTF-8"%> 4 | 5 | 6 | 7 | <% Object user = session.getAttribute(LoginServlet.LOGINED_USER_SESSION_ATTR); 8 | if (null != user){ 9 | response.sendRedirect("Chat"); 10 | }%> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 用户登录 22 | 23 | 24 |
25 | 39 |
40 | 41 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'chatfonts'; 3 | src: url('fonts/chatfonts.eot?r47v29'); 4 | src: url('fonts/chatfonts.eot?r47v29#iefix') format('embedded-opentype'), 5 | url('fonts/chatfonts.ttf?r47v29') format('truetype'), 6 | url('fonts/chatfonts.woff?r47v29') format('woff'), 7 | url('fonts/chatfonts.svg?r47v29#chatfonts') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | [class^="icon-"], [class*=" icon-"] { 13 | /* use !important to prevent issues with browser extensions that change fonts */ 14 | font-family: 'chatfonts' !important; 15 | speak: none; 16 | font-style: normal; 17 | font-weight: normal; 18 | font-variant: normal; 19 | text-transform: none; 20 | line-height: 1; 21 | 22 | /* Better Font Rendering =========== */ 23 | -webkit-font-smoothing: antialiased; 24 | -moz-osx-font-smoothing: grayscale; 25 | } 26 | 27 | .icon-disableaudio:before { 28 | content: "\e912"; 29 | } 30 | .icon-image:before { 31 | content: "\e913"; 32 | } 33 | .icon-enableaudio:before { 34 | content: "\e914"; 35 | } 36 | .icon-up:before { 37 | content: "\e911"; 38 | } 39 | .icon-face:before { 40 | content: "\e910"; 41 | } 42 | .icon-multichat:before { 43 | content: "\e90b"; 44 | } 45 | .icon-edit:before { 46 | content: "\e90c"; 47 | } 48 | .icon-nickname:before { 49 | content: "\e90d"; 50 | } 51 | .icon-singlechat:before { 52 | content: "\e90e"; 53 | } 54 | .icon-yes:before { 55 | content: "\e90f"; 56 | } 57 | .icon-password:before { 58 | content: "\e909"; 59 | } 60 | .icon-user:before { 61 | content: "\e90a"; 62 | } 63 | .icon-follow:before { 64 | content: "\e908"; 65 | } 66 | .icon-cancel:before { 67 | content: "\e907"; 68 | } 69 | .icon-location:before { 70 | content: "\e901"; 71 | } 72 | .icon-next:before { 73 | content: "\e902"; 74 | } 75 | .icon-previous:before { 76 | content: "\e903"; 77 | } 78 | .icon-search:before { 79 | content: "\e904"; 80 | } 81 | .icon-trash:before { 82 | content: "\e905"; 83 | } 84 | .icon-delete:before { 85 | content: "\e906"; 86 | } 87 | .icon-plus:before { 88 | content: "\e900"; 89 | } 90 | 91 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/servlet/UserValidation.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.servlet; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.annotation.WebServlet; 8 | import javax.servlet.http.HttpServlet; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | import org.json.JSONObject; 13 | 14 | import com.chatroom.business.UserBusiness; 15 | import com.chatroom.models.User; 16 | import com.chatroom.utils.ResponseInformation; 17 | 18 | /** 19 | * Servlet implementation class UseValidation 20 | */ 21 | @WebServlet(name = "UseValidationServlet", urlPatterns = { "/uservalidation" }) 22 | public class UserValidation extends JsonServlet { 23 | private static final long serialVersionUID = 1L; 24 | 25 | /** 26 | * @see HttpServlet#HttpServlet() 27 | */ 28 | public UserValidation() { 29 | super(); 30 | // TODO Auto-generated constructor stub 31 | } 32 | 33 | /** 34 | * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 35 | */ 36 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 37 | response.setCharacterEncoding("UTF-8"); 38 | response.setCharacterEncoding("UTF-8"); 39 | response.setContentType("application/json;charset=UTF-8"); 40 | String postData = ReadFromStream(request); 41 | try { 42 | JSONObject userJson = new JSONObject(postData); 43 | User user = new User(); 44 | user.readFromJson(userJson); 45 | if (null == new UserBusiness().queryUserWithUserName(user.getUsername())) { 46 | PrintWriter writer = response.getWriter(); 47 | writer.write(ResponseInformation.getSuccessInformation()); 48 | writer.close(); 49 | return; 50 | }else { 51 | PrintWriter writer = response.getWriter(); 52 | writer.println(ResponseInformation.getErrorInformation("用户名已存在!")); 53 | writer.close(); 54 | return; 55 | } 56 | } catch (Exception e) { 57 | e.printStackTrace(); 58 | PrintWriter writer = response.getWriter(); 59 | writer.println(ResponseInformation.getErrorInformation("系统异常!")); 60 | writer.close(); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/webapp/register.jsp: -------------------------------------------------------------------------------- 1 | <%@page import="com.chatroom.models.User"%> 2 | <%@page import="com.chatroom.servlet.RegisterServlet"%> 3 | <%@ page language="java" contentType="text/html; charset=UTF-8" 4 | pageEncoding="UTF-8"%> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 聊天室 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 27 | *请填写每一项 28 |
29 |
30 |
31 |
32 | 33 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/servlet/CloseChatServlet.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.servlet; 2 | 3 | import java.io.IOException; 4 | import java.util.Iterator; 5 | import java.util.Set; 6 | 7 | import javax.servlet.ServletException; 8 | import javax.servlet.annotation.WebServlet; 9 | import javax.servlet.http.HttpServlet; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | import org.json.JSONObject; 14 | 15 | import com.chatroom.controller.ChatController; 16 | import com.chatroom.models.User; 17 | 18 | /** 19 | * Servlet implementation class CloseChatServlet 20 | */ 21 | @WebServlet("/closechat") 22 | public class CloseChatServlet extends JsonServlet { 23 | private static final long serialVersionUID = 1L; 24 | 25 | /** 26 | * @see HttpServlet#HttpServlet() 27 | */ 28 | public CloseChatServlet() { 29 | super(); 30 | // TODO Auto-generated constructor stub 31 | } 32 | 33 | /** 34 | * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 35 | */ 36 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 37 | String connectionData = ReadFromStream(request); 38 | try { 39 | JSONObject json = new JSONObject(connectionData); 40 | JSONObject fromUser = new JSONObject(json.get("from"));// 发起者 41 | JSONObject destUser = new JSONObject(json.get("to"));// 接收者 42 | User from = new User(); 43 | from.readFromJson(fromUser); 44 | 45 | User to = new User(); 46 | to.readFromJson(destUser); 47 | 48 | Set connected = ChatController.getConnectedUsers(); 49 | Iterator iterator = connected.iterator(); 50 | ChatController fromController = null; 51 | ChatController destController = null; 52 | while (iterator.hasNext()) { 53 | ChatController tmpController = iterator.next(); 54 | if (tmpController.getUser().getUsername().equals(from.getUsername())) { 55 | fromController = tmpController; 56 | } 57 | if (tmpController.getUser().getUsername().equals(to.getUsername())) { 58 | destController = tmpController; 59 | } 60 | if (null != fromController && null != destController) { 61 | break; 62 | } 63 | } 64 | fromController.setChatingUser(null); 65 | destController.setChatingUser(null); 66 | // 设置聊天对象 67 | } catch (Exception e) { 68 | // TODO: handle exception 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/css/chat-login.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8"; 2 | 3 | .login-area { 4 | width: 100%; 5 | margin: 0 auto; 6 | margin-top: 40px; 7 | margin-bottom: 32px; 8 | } 9 | .login-title { 10 | width: 100%; 11 | text-align: center; 12 | display: inline-block; 13 | } 14 | 15 | .login-title-content { 16 | 17 | } 18 | .fm-login { 19 | margin-top: 120px; 20 | } 21 | 22 | .fm-login .fm-group { 23 | width: 100%; 24 | display: block; 25 | height: 48px; 26 | border: 1px solid #999; 27 | margin-bottom: 40px; 28 | } 29 | 30 | .fm-login .fm-group a { 31 | width: 48px; 32 | height: 48px; 33 | font-size: 24px; 34 | border-right: 1px solid #ccc; 35 | background: #f1edea; 36 | display: block; 37 | float: left; 38 | line-height: 48px; 39 | text-align: center; 40 | color: #33b3a1; 41 | } 42 | 43 | .fm-login .fm-group input { 44 | height: 100%; 45 | width: calc(90% - 60px); 46 | width: -webkit-calc(90% - 60px); 47 | width: -moz-calc(90% - 60px); 48 | font-size: 1.1em; 49 | font-family: "Consolas", "微软雅黑", "Arial", "Heiti-TC"; 50 | padding-left: 12px; 51 | transition: box-shadow 0.18 ease-in-out; 52 | 53 | -webkit-transition: box-shadow 0.18 ease-in-out; 54 | -moz-transition: box-shadow 0.18 ease-in-out; 55 | border-radius: 0; 56 | -webkit-border-radius: 0; 57 | -moz-border-radius: 0; 58 | } 59 | input:focus { 60 | -webkit-tap-highlight-color: transparent; 61 | -webkit-user-modify: read-white-plaintext-only; 62 | } 63 | 64 | .input-focused { 65 | box-shadow: 0px 0px 10px rgba(47, 197, 176, 0.85); 66 | } 67 | 68 | .invalide-input { 69 | border: 1px solid #ed2323; 70 | } 71 | 72 | .btn-primary { 73 | margin-top: 32px; 74 | width: 100%; 75 | } 76 | 77 | .fm-login .bottom-content { 78 | width: 100%; 79 | } 80 | 81 | .link-register { 82 | width: 100%; 83 | display: block; 84 | text-align: right; 85 | font-size: 12px; 86 | color: #999; 87 | margin-top: 16px; 88 | } 89 | 90 | .link-register a[nth-child(2)] { 91 | 92 | } 93 | 94 | a { 95 | text-decoration: none; 96 | color: #999; 97 | } 98 | 99 | a:LINK { 100 | color: #999; 101 | } 102 | 103 | .linker { 104 | width: 100%; 105 | height: 46px; 106 | background: #538bb6; 107 | margin-top: 20px; 108 | text-align: center; 109 | border-bottom-left-radius: 3px; 110 | border-bottom-right-radius: 3px; 111 | } 112 | 113 | .linker a { 114 | color: #fff; 115 | line-height: 46px; 116 | font-size: 15px; 117 | text-align: center; 118 | font-family: "Consolas", "微软雅黑"; 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/servlet/LoginServlet.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.servlet; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.io.PrintWriter; 6 | import java.nio.charset.Charset; 7 | 8 | import javax.servlet.ServletException; 9 | import javax.servlet.annotation.WebServlet; 10 | import javax.servlet.http.HttpServlet; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | import org.json.JSONObject; 15 | 16 | import com.chatroom.business.UserBusiness; 17 | import com.chatroom.models.User; 18 | import com.chatroom.persiststorage.ExcuteQueryObject; 19 | import com.chatroom.utils.MD5Utils; 20 | import com.chatroom.utils.ResponseInformation; 21 | 22 | /** 23 | * Servlet implementation class LoginServlet 24 | */ 25 | @WebServlet("/login") 26 | public class LoginServlet extends JsonServlet { 27 | private static final long serialVersionUID = 1L; 28 | public static final String LOGINED_USER_SESSION_ATTR = "logined_user"; 29 | 30 | /** 31 | * @see HttpServlet#HttpServlet() 32 | */ 33 | public LoginServlet() { 34 | super(); 35 | // TODO Auto-generated constructor stub 36 | } 37 | 38 | /** 39 | * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 40 | */ 41 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 42 | response.setContentType("application/json;charset=utf-8"); 43 | response.setCharacterEncoding("UTF-8"); 44 | //OutputStream output = response.getOutputStream(); 45 | String loginData = ReadFromStream(request); 46 | try { 47 | JSONObject loginUserJson = new JSONObject(loginData); 48 | User user = new User(); 49 | user.readFromJson(loginUserJson); 50 | user.setPassword(MD5Utils.md5Encode(user.getPassword())); 51 | if (new UserBusiness().authenticateUser(user)){ 52 | //output.write(ResponseInformation.getSuccessInformation().getBytes(Charset.forName("utf-8"))); 53 | //加SESSION 54 | PrintWriter writer = response.getWriter(); 55 | writer.println(ResponseInformation.getSuccessInformation()); 56 | writer.close(); 57 | User loginedUser = new UserBusiness().queryUserWithUserName(user.getUsername()); 58 | System.out.println(loginedUser.getUsername()); 59 | request.getSession().setAttribute(LOGINED_USER_SESSION_ATTR, loginedUser); 60 | }else { 61 | PrintWriter writer = response.getWriter(); 62 | writer.println(ResponseInformation.getErrorInformation("用户名或密码错误!")); 63 | writer.close(); 64 | } 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/servlet/SingleChatServlet.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.servlet; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | import java.util.Iterator; 6 | import java.util.Set; 7 | 8 | import javax.servlet.ServletException; 9 | import javax.servlet.annotation.WebServlet; 10 | import javax.servlet.http.HttpServlet; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | import org.json.JSONObject; 15 | 16 | import com.chatroom.controller.ChatController; 17 | import com.chatroom.models.User; 18 | import com.chatroom.utils.ResponseInformation; 19 | 20 | /** 21 | * Servlet implementation class SingleChatServlet 22 | */ 23 | @WebServlet("/singlechat") 24 | public class SingleChatServlet extends JsonServlet { 25 | private static final long serialVersionUID = 1L; 26 | 27 | /** 28 | * @see HttpServlet#HttpServlet() 29 | */ 30 | public SingleChatServlet() { 31 | super(); 32 | // TODO Auto-generated constructor stub 33 | } 34 | 35 | /** 36 | * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 37 | */ 38 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 39 | request.setCharacterEncoding("UTF-8"); 40 | response.setCharacterEncoding("UTF-8"); 41 | response.setContentType("application/json;charset=utf-8"); 42 | String connectionData = ReadFromStream(request); 43 | try { 44 | JSONObject json = new JSONObject(connectionData); 45 | JSONObject fromUser = new JSONObject(json.getString("from"));// 鍙戣捣鑰� 46 | JSONObject destUser = new JSONObject(json.getString("to"));// 鎺ユ敹鑰� 47 | User from = new User(); 48 | from.readFromJson(fromUser); 49 | 50 | User to = new User(); 51 | to.readFromJson(destUser); 52 | 53 | Set connected = ChatController.getConnectedUsers(); 54 | Iterator iterator = connected.iterator(); 55 | ChatController fromController = null; 56 | ChatController destController = null; 57 | while (iterator.hasNext()) { 58 | ChatController tmpController = iterator.next(); 59 | if (tmpController.getUser().getUsername().equals(from.getUsername())) { 60 | fromController = tmpController; 61 | } 62 | if (tmpController.getUser().getUsername().equals(to.getUsername())) { 63 | destController = tmpController; 64 | } 65 | if (null != fromController && null != destController) { 66 | break; 67 | } 68 | } 69 | fromController.setChatingUser(to); 70 | destController.setChatingUser(from); 71 | PrintWriter writer = response.getWriter(); 72 | writer.println(ResponseInformation.getSuccessInformation()); 73 | writer.close(); 74 | } catch (Exception e) { 75 | // TODO: handle exception 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/servlet/RegisterServlet.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.servlet; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.PrintWriter; 6 | 7 | import javax.servlet.ServletException; 8 | import javax.servlet.annotation.WebServlet; 9 | import javax.servlet.http.HttpServlet; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | import org.json.JSONException; 14 | import org.json.JSONObject; 15 | 16 | import com.chatroom.business.UserBusiness; 17 | import com.chatroom.models.User; 18 | import com.chatroom.utils.MD5Utils; 19 | import com.chatroom.utils.ResponseInformation; 20 | 21 | /** 22 | * Servlet implementation class RegisterServlet 23 | */ 24 | @WebServlet("/register") 25 | public class RegisterServlet extends JsonServlet { 26 | private static final long serialVersionUID = 1L; 27 | public static final String LOGINED_USER_SESSION_ATTR = "logined_user"; 28 | 29 | /** 30 | * @see HttpServlet#HttpServlet() 31 | */ 32 | public RegisterServlet() { 33 | super(); 34 | } 35 | 36 | /** 37 | * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 38 | */ 39 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 40 | response.setContentType("application/json;charset=utf-8"); 41 | response.setCharacterEncoding("UTF-8"); 42 | PrintWriter writer = response.getWriter(); 43 | String postData = ReadFromStream(request); 44 | if (null == postData) { 45 | String responseStr = ResponseInformation.getErrorInformation("非法的数据请求!"); 46 | writer.println(responseStr); 47 | writer.close(); 48 | return; 49 | } 50 | try { 51 | JSONObject userJson = new JSONObject(postData); 52 | User user = new User(); 53 | user.readFromJson(userJson); 54 | try { 55 | user.setPassword(MD5Utils.md5Encode(user.getPassword())); 56 | User checkedUser = new UserBusiness().queryUserWithUserName(user.getUsername()); 57 | if (null != checkedUser) { 58 | // 用户名存在 59 | writer.println(ResponseInformation.getErrorInformation("用户名已存在")); 60 | writer.close(); 61 | return; 62 | } 63 | } catch (Exception e) { 64 | e.printStackTrace(); 65 | } 66 | if (new UserBusiness().registerUser(user)){ 67 | writer.println(ResponseInformation.getSuccessInformation()); 68 | // 加session 69 | request.getSession().setAttribute(LOGINED_USER_SESSION_ATTR, user); 70 | writer.close(); 71 | return; 72 | }else { 73 | writer.println(ResponseInformation.getErrorInformation("未知的错误")); 74 | writer.close(); 75 | return; 76 | } 77 | } catch (JSONException e) { 78 | e.printStackTrace(); 79 | writer.println(ResponseInformation.getErrorInformation("系统异常错误!")); 80 | writer.close(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/models/User.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.models; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | import com.chatroom.interfaces.IJsonSeriserialize; 7 | 8 | public class User implements IJsonSeriserialize { 9 | private String username; // 用户名 10 | private String password; // 用户密码 11 | private String nickname; // 昵称 12 | private String sex; // 性别 13 | public static final String USERNAME = "username"; 14 | public static final String PASSWORD = "password"; 15 | public static final String NICKNAME = "nickname"; 16 | public static final String SEX = "sex"; 17 | public String getUsername() { 18 | return username.trim(); 19 | } 20 | public void setUsername(String username) { 21 | this.username = username; 22 | } 23 | public String getPassword() { 24 | return null == password ? null : password.trim(); 25 | } 26 | public void setPassword(String password) { 27 | this.password = password; 28 | } 29 | public String getNickname() { 30 | return null == nickname ? null : nickname.trim(); 31 | } 32 | public void setNickname(String nickname) { 33 | this.nickname = nickname; 34 | } 35 | public String getSex() { 36 | return null == sex ? null : sex.trim(); 37 | } 38 | public void setSex(String sex) { 39 | this.sex = sex; 40 | } 41 | public User() { 42 | super(); 43 | } 44 | public User(String username, String password, String nickname, String sex) { 45 | super(); 46 | this.username = username; 47 | this.password = password; 48 | this.nickname = nickname; 49 | this.sex = sex; 50 | } 51 | @Override 52 | public String toString() { 53 | return "User [username=" + username + ", password=" + password + ", nickname=" + nickname + ", sex=" + sex 54 | + "]"; 55 | } 56 | 57 | /* (non-Javadoc) 58 | * @see com.chatroom.interfaces.IJsonSeriserialize#toJson() 59 | */ 60 | public JSONObject toJson() { 61 | JSONObject jo = new JSONObject(); 62 | try { 63 | jo.put(USERNAME, username); 64 | jo.put(SEX, sex); 65 | jo.put(NICKNAME, nickname); 66 | return jo; 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | } 70 | return null; 71 | } 72 | /* (non-Javadoc) 73 | * @see com.chatroom.interfaces.IJsonSeriserialize#readFromJson(org.json.JSONObject) 74 | */ 75 | public void readFromJson(JSONObject json) { 76 | if (json.has(USERNAME)) { 77 | try { 78 | this.username = json.getString(USERNAME); 79 | } catch (JSONException e) { 80 | e.printStackTrace(); 81 | } 82 | } 83 | if (json.has(NICKNAME)) { 84 | try { 85 | this.nickname = json.getString(NICKNAME); 86 | } catch (Exception e) { 87 | e.printStackTrace(); 88 | } 89 | } 90 | if (json.has(PASSWORD)) { 91 | try { 92 | this.password = json.getString(PASSWORD); 93 | } catch (Exception e) { 94 | e.printStackTrace(); 95 | } 96 | } 97 | if (json.has(SEX)) { 98 | try { 99 | this.sex = json.getString(SEX); 100 | } catch (Exception e) { 101 | e.printStackTrace(); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/models/ChatMessage.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.models; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | import com.chatroom.interfaces.IJsonSeriserialize; 7 | 8 | public class ChatMessage implements IJsonSeriserialize{ 9 | private String from; 10 | private String to; 11 | private Object messageContent; 12 | private String messageType; 13 | private String fromNick; 14 | public static final String FROM = "from"; 15 | public static final String TO = "to"; 16 | public static final String MESSSAGE_CONTENT = "messageContent"; 17 | public static final String MESSAGE_TYPE = "messageType"; 18 | public static final String FROM_NICKNAME = "fromNick"; 19 | 20 | public ChatMessage() { 21 | super(); 22 | } 23 | 24 | public ChatMessage(String from, String to, Object messageContent) { 25 | super(); 26 | this.from = from; 27 | this.to = to; 28 | this.messageContent = messageContent; 29 | } 30 | 31 | public String getFrom() { 32 | return from == null ? null : from.trim(); 33 | } 34 | public void setFrom(String from) { 35 | this.from = from; 36 | } 37 | public String getTo() { 38 | return to == null ? null : to.trim(); 39 | } 40 | public void setTo(String to) { 41 | this.to = to; 42 | } 43 | public Object getMessageContent() { 44 | return messageContent; 45 | } 46 | public void setMessageContent(Object messageContent) { 47 | this.messageContent = messageContent; 48 | } 49 | 50 | public String getMessageType() { 51 | return messageType == null ? null : messageType.trim(); 52 | } 53 | 54 | public void setMessageType(String messageType) { 55 | this.messageType = messageType; 56 | } 57 | 58 | public String getFromNick() { 59 | return null == fromNick ? null : fromNick.trim(); 60 | } 61 | 62 | public void setFromNick(String fromNick) { 63 | this.fromNick = fromNick; 64 | } 65 | 66 | 67 | /* (non-Javadoc) 68 | * @see com.chatroom.interfaces.IJsonSeriserialize#toJson() 69 | */ 70 | public JSONObject toJson() { 71 | JSONObject json = new JSONObject(); 72 | try { 73 | json.put(FROM, from); 74 | json.put(TO, to); 75 | json.put(MESSSAGE_CONTENT, messageContent); 76 | json.put(MESSAGE_TYPE, messageType); 77 | json.put(FROM_NICKNAME, fromNick); 78 | } catch (JSONException e) { 79 | e.printStackTrace(); 80 | } 81 | return json; 82 | } 83 | 84 | /* (non-Javadoc) 85 | * @see com.chatroom.interfaces.IJsonSeriserialize#readFromJson(org.json.JSONObject) 86 | */ 87 | public void readFromJson(JSONObject json) { 88 | try { 89 | if (json.has(FROM)) { 90 | this.from = json.getString(FROM); 91 | } 92 | if (json.has(TO)) { 93 | this.to = json.getString(TO); 94 | } 95 | if (json.has(MESSSAGE_CONTENT)) { 96 | this.messageContent = json.getString(MESSSAGE_CONTENT); 97 | } 98 | if (json.has(MESSAGE_TYPE)) { 99 | this.messageType = json.getString(MESSAGE_TYPE); 100 | } 101 | if (json.has(FROM_NICKNAME)){ 102 | this.fromNick = json.getString(FROM_NICKNAME); 103 | } 104 | } catch (Exception e) { 105 | e.printStackTrace(); 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/webapp/chat.jsp: -------------------------------------------------------------------------------- 1 | <%@page import="com.chatroom.servlet.RegisterServlet"%> 2 | <%@page import="com.chatroom.models.User"%> 3 | <%@ page language="java" contentType="text/html; charset=UTF-8" 4 | pageEncoding="UTF-8"%> 5 | 6 | <% 7 | Object sessionObj = request.getSession().getAttribute(RegisterServlet.LOGINED_USER_SESSION_ATTR); 8 | User loginedUser = null; 9 | if (null != sessionObj) { 10 | if (sessionObj instanceof User) 11 | loginedUser = (User)sessionObj; 12 | }else { 13 | // 重定向 14 | request.getRequestDispatcher("login.jsp").forward(request, response); 15 | } 16 | %> 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 35 | 36 | <%=loginedUser.getNickname() %>, 欢迎进入聊天室 37 | 38 | 39 | 40 |
41 |
42 | 群聊中... 43 |
44 |
45 | 46 |
47 |
48 | 49 | 在线列表 50 | 51 |
52 |
53 |
    54 | 55 |
56 |
57 |
58 |
59 |
60 |
    61 | 62 |
63 | 64 |
65 |
66 |
    67 | 68 |
  • 69 |
70 |
71 |
72 |
73 | 74 | 75 |
76 | 77 |
    78 |
  • 79 |
  • 80 |
81 |
82 |
83 | 84 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/js/chat-register.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | 5 | var existUser = false 6 | 7 | function onRegister(){ 8 | var username = $('#username').val(); 9 | var password = $('#password').val(); 10 | var nickname = $('#nickname').val() 11 | var user = { 12 | username: username, 13 | password: password, 14 | sex: '男', 15 | nickname: nickname, 16 | } 17 | $.ajax({ 18 | url: 'register', 19 | type: 'POST', 20 | data: JSON.stringify(user), 21 | dataType: 'JSON', 22 | contentType: 'application/json;charset=utf-8', 23 | success: function(msg) { 24 | if (msg.status != null && msg.status == "success") { 25 | window.location.href = 'Chat' 26 | }else { 27 | alert ('注册失败!' + '原因可能是:\n' + msg.reason) 28 | } 29 | }, 30 | error :function(msg) { 31 | console.log(msg) 32 | } 33 | }) 34 | } 35 | 36 | function invalideUser(elem){ 37 | inputBlur(elem) 38 | checkUserExists() 39 | } 40 | 41 | function invalideNickname(elem){ 42 | inputBlur(elem) 43 | checkNickname() 44 | } 45 | 46 | function invalidePassword(elem) { 47 | inputBlur(elem) 48 | checkPassword() 49 | } 50 | 51 | function userRefocus(elem){ 52 | inputFocus(elem) 53 | var classname = $('.username-status').attr('class') 54 | classname = classname.replace(/\s*show-indicator\s*/, '') 55 | $('.username-status').attr('class', classname) 56 | } 57 | 58 | function nicknameRefocus(elem){ 59 | inputFocus(elem) 60 | var classname = $('.nickname-status').attr('class') 61 | classname = classname.replace(/\s*show-indicator\s*/, '') 62 | $('.nickname-status').attr('class', classname) 63 | } 64 | 65 | function passwordRefocus(elem){ 66 | inputFocus(elem) 67 | var classname = $('.password-status').attr('class') 68 | classname = classname.replace(/\s*show-indicator\s*/, '') 69 | $('.password-status').attr('class', classname) 70 | } 71 | 72 | function checkNickname (){ 73 | var nickname = $('#nickname').val() 74 | if (null != nickname && nickname.trim().length > 3) { 75 | var classname = $('.nickname-status').attr('class') 76 | classname += ' show-indicator '; 77 | $('.nickname-status').attr('class', classname) 78 | } 79 | } 80 | 81 | function checkPassword(){ 82 | var nickname = $('#password').val() 83 | if (null != nickname && nickname.trim().length > 6) { 84 | var classname = $('.password-status').attr('class') 85 | classname += ' show-indicator '; 86 | $('.password-status').attr('class', classname) 87 | } 88 | } 89 | 90 | function checkInput() { 91 | var usernmae = $('#username').val() 92 | if (usernmae == null || usernmae.trim().length <= 0) { 93 | alert('输入用户名') 94 | $('#username')[0].focus() 95 | return false 96 | } 97 | var password = $('#password').val() 98 | if (null == password || password.trim().length <= 0) { 99 | $('#password')[0].focus() 100 | alert('输入密码,以登录系统!') 101 | return false 102 | } 103 | var nickname = $('#nickname').val() 104 | if (null == nickname || nickname.trim().length <= 0) { 105 | $('#nickname')[0].focus() 106 | alert('填写昵称,让更多人认识你') 107 | return false 108 | } 109 | // 注册 110 | if (existUser) { 111 | alert('用户名已经存在!') 112 | } 113 | onRegister() 114 | return false 115 | } 116 | 117 | function checkUserExists() { 118 | var username = $('#username').val(); 119 | $('#register').attr('disabled', 'disabled') 120 | if (null != username && username.trim().length >= 5) { 121 | $.ajax({ 122 | url: 'uservalidation', 123 | type: 'POST', 124 | data: JSON.stringify({username:username}), 125 | dataType: "json", 126 | contentType: "application/json;charset=utf-8", 127 | success: function(msg) { 128 | if (msg.status != null && msg.status == "success") { 129 | var classname = $('.username-status').attr('class') 130 | classname += ' show-indicator '; 131 | $('.username-status').attr('class', classname) 132 | $('#register')[0].removeAttribute('disabled') 133 | existUser = false; 134 | }else { // 用户名存在 135 | var classname = $('.username-status').attr('class') 136 | classname = classname.replace(/\s*show-indicator\s*/, ''); 137 | $('.username-status').attr('class', classname) 138 | $('#register').attr('disabled', 'disabled') 139 | existUser = true 140 | alert('用户名已存在!') 141 | } 142 | }, 143 | error: function(response) { 144 | 145 | } 146 | }) 147 | }else { 148 | alert('您输入的用户名可能太短!') 149 | } 150 | } -------------------------------------------------------------------------------- /src/main/resources/DesignDiagrams/InteractionDiagrams.mgi: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 50 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 63 | 64 | 66 | 68 | 71 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/persiststorage/ExcuteQueryObject.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.persiststorage; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | import java.sql.PreparedStatement; 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.util.HashMap; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class ExcuteQueryObject { 14 | @SuppressWarnings("unchecked") 15 | protected Map[] select(PreparedStatement preparestatement, Object[] destObjParams)throws SQLException { 16 | if (null == preparestatement) { 17 | return null; 18 | } 19 | this.setPreparement(preparestatement, destObjParams); 20 | ResultSet results = null;; 21 | try { 22 | results = preparestatement.executeQuery(); 23 | List> allResult = new LinkedList>(); 24 | while (results.next()) { 25 | allResult.add(setToMap(results)); 26 | } 27 | return allResult.toArray((Map[])new Map[]{}); 28 | } catch (SQLException e) { 29 | e.printStackTrace(); 30 | return null; 31 | }finally { 32 | if (null != preparestatement) { 33 | preparestatement.close(); 34 | } 35 | if (null != results) { 36 | results.close(); 37 | } 38 | } 39 | } 40 | protected boolean update(PreparedStatement preparestatement, Object []oldObjParams, Object newObj) throws SQLException { 41 | if (null == preparestatement) { 42 | return false; 43 | } 44 | // 设置参数 45 | try { 46 | List allParams = new LinkedList(); 47 | this.addFieldsParams(allParams, newObj);// 添加参数 48 | allParams.addAll(allParams); 49 | this.setPreparement(preparestatement, allParams.toArray()); 50 | // 参数设置完毕 51 | preparestatement.executeUpdate(); 52 | return true; 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | return false; 56 | } finally { 57 | preparestatement.close(); 58 | } 59 | } 60 | protected int delete(PreparedStatement preparestatement, Object[] destObjParams) throws SQLException { 61 | if (null == preparestatement) { 62 | return -1; 63 | } 64 | try { 65 | this.setPreparement(preparestatement, destObjParams);// 设置参数 66 | return preparestatement.executeUpdate(); 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | return -1; 70 | } finally { 71 | preparestatement.close(); 72 | } 73 | } 74 | protected boolean insert(PreparedStatement preparestatement, Object oringinObj) throws SQLException { 75 | if (null == preparestatement) { 76 | return false; 77 | } 78 | try { 79 | //将对象参数获取到 80 | List params = new LinkedList(); 81 | this.addFieldsParams(params, oringinObj); 82 | this.setPreparement(preparestatement, params.toArray());// 设置参数 83 | //执行 84 | preparestatement.execute(); 85 | return true; 86 | } catch (Exception e) { 87 | e.printStackTrace(); 88 | return false; 89 | } finally { 90 | preparestatement.close(); 91 | } 92 | } 93 | protected Map setToMap(ResultSet set) { 94 | try { 95 | int col = set.getMetaData().getColumnCount(); 96 | Map dstObj = new HashMap(); 97 | for (int i = 0; i < col; i++) { 98 | dstObj.put(i, set.getObject(i + 1));// 获取值 99 | } 100 | return dstObj; 101 | } catch (SQLException e) { 102 | e.printStackTrace(); 103 | return null; 104 | } 105 | } 106 | protected void setPreparement(PreparedStatement statement, Object []params) { 107 | for (int i = 0;i < params.length; i++) { 108 | try { 109 | statement.setObject(i + 1, params[i]); 110 | } catch (SQLException e) { 111 | e.printStackTrace(); 112 | } 113 | } 114 | } 115 | protected void addFieldsParams(Listparams, Object obj) { 116 | java.lang.reflect.Field[]allFields = obj.getClass().getDeclaredFields(); 117 | // 获取所有属性 118 | for (int i = 0; i < allFields.length; i++) { 119 | String fieldName = allFields[i].getName(); 120 | String getterFunction = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); 121 | Method getterMethod = null; 122 | try { 123 | getterMethod = obj.getClass().getMethod(getterFunction); 124 | } catch (NoSuchMethodException e) { 125 | e.printStackTrace(); 126 | continue; 127 | } catch (SecurityException e) { 128 | e.printStackTrace(); 129 | continue; 130 | } 131 | Object value = null; 132 | try { 133 | value = getterMethod.invoke(obj, new Object[]{}); 134 | } catch (IllegalAccessException e) { 135 | e.printStackTrace(); 136 | continue; 137 | } catch (IllegalArgumentException e) { 138 | e.printStackTrace(); 139 | continue; 140 | } catch (InvocationTargetException e) { 141 | e.printStackTrace(); 142 | continue; 143 | } 144 | // 属性值 145 | params.add(value); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/dbhelper/UserHelper.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.dbhelper; 2 | 3 | import java.sql.Connection; 4 | import java.sql.PreparedStatement; 5 | import java.sql.SQLException; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import org.json.JSONObject; 11 | 12 | import com.chatroom.models.User; 13 | import com.chatroom.persiststorage.DataBase; 14 | import com.chatroom.persiststorage.ExcuteQueryObject; 15 | 16 | public class UserHelper extends ExcuteQueryObject { 17 | 18 | private DataBase db = new DataBase(); 19 | 20 | private final String SELECT_USER_BY_ID_WITH_PASSWORD = "SELECT * FROM CHATUSER WHERE USERNAME = ? AND PASSWORD = ?"; 21 | private final String SELECT_USER_BY_ID_WITH_PASSWORD_FROM_VIEW = "SELECT * FROM ViewUserToChat WHERE USERNAME = ? AND PASSWORD = ?"; 22 | public boolean userExists(User user) { 23 | //打开数据库连接 24 | if (!db.openConnection()) {// 打开失败 25 | return false; 26 | } 27 | Connection conn = db.getSqlConnection(); 28 | try { 29 | PreparedStatement queryStatement = conn.prepareStatement(SELECT_USER_BY_ID_WITH_PASSWORD);// 创建SQL语句 30 | String username = user.getUsername(); 31 | String password = user.getPassword(); 32 | Map[]result = select(queryStatement, new Object[]{username, password}); 33 | if (null != result && result.length > 0) { 34 | return true; 35 | } 36 | // 在本数据库中为验证到目标用户,在视图中查找 37 | queryStatement = conn.prepareStatement(SELECT_USER_BY_ID_WITH_PASSWORD_FROM_VIEW); 38 | result = select(queryStatement, new Object[]{username, password}); 39 | if (null == result || result.length <= 0) { 40 | return false; 41 | } 42 | return true; 43 | }catch (NullPointerException ex) { 44 | return false; 45 | } catch (Exception ex) { 46 | ex.printStackTrace(); 47 | return false; 48 | } finally { 49 | db.closeConnection(); 50 | } 51 | } 52 | 53 | private final String DELETE_USER = "DELETE FROM CHATUSER WHERE USERNAME = ?"; 54 | public boolean deleteUserExist(User user) { 55 | if (!db.openConnection()){ 56 | return false; 57 | } 58 | Connection conn = db.getSqlConnection(); 59 | try { 60 | PreparedStatement deleteStatement = conn.prepareStatement(DELETE_USER); 61 | if (-1 == delete(deleteStatement, new Object[]{user.getUsername()})) { 62 | return false; 63 | } 64 | return true; 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | return false; 68 | } finally { 69 | db.closeConnection(); 70 | } 71 | } 72 | 73 | private final String UPDATE_USER = "UPDATE CHATUSER SET USERNAME = ?, PASSWORD = ?, NICKNAME = ?, SEX = ? WHERE USERNAME = ?"; 74 | public boolean updateUser(User oldUser, User newUser) { 75 | if (!db.openConnection()) { 76 | return false; 77 | } 78 | Connection conn = db.getSqlConnection(); 79 | try { 80 | PreparedStatement updateStatement = conn.prepareStatement(UPDATE_USER); 81 | return this.update(updateStatement, new Object[]{oldUser.getUsername()}, newUser); 82 | }catch(Exception ex){ 83 | ex.printStackTrace(); 84 | return false; 85 | }finally { 86 | db.closeConnection(); 87 | } 88 | } 89 | 90 | 91 | private final String SELECT_USER_BY_USERNAME = "SELECT * FROM CHATUSER WHERE USERNAME = ?"; 92 | private final String SELECT_USER_BY_USERNAME_FROM_VIEW = "SELECT * FROM VIEWUSERTOCHAT WHERE USERNAME = ?"; 93 | public User[] queryUserWithName(String username) { 94 | if (!db.openConnection()) { 95 | return null; 96 | }Connection conn = db.getSqlConnection(); 97 | try { 98 | PreparedStatement selectStatement = conn.prepareStatement(SELECT_USER_BY_USERNAME); 99 | Map[] result = this.select(selectStatement, new Object[]{username}); 100 | if (null == result || result.length <= 0) { 101 | selectStatement = conn.prepareStatement(SELECT_USER_BY_USERNAME_FROM_VIEW);// 从视图查找 102 | result = this.select(selectStatement, new Object[]{username}); 103 | if (result == null || result.length <= 0) 104 | return null; 105 | } 106 | List users = new LinkedList(); 107 | for (int i = 0; i < result.length; i++) { 108 | User user = new User(); 109 | user.setUsername(result[i].get(0).toString()); 110 | user.setPassword(null); 111 | user.setNickname(result[i].get(2).toString()); 112 | user.setSex(result[i].get(3).toString()); 113 | users.add(user); 114 | } 115 | return users.toArray(new User[]{}); 116 | } catch (Exception e) { 117 | e.printStackTrace(); 118 | return null; 119 | } finally { 120 | db.closeConnection(); 121 | } 122 | } 123 | 124 | private final String SELECT_USER_BY_NICKNAME = "SELECT * FROM CHATUSER WHERE NICKNAME = ?"; 125 | public User[] queryUserWithNick(String nickname) { 126 | if (!db.openConnection()) { 127 | return null; 128 | } 129 | Connection conn = db.getSqlConnection(); 130 | try { 131 | PreparedStatement selectStatement = conn.prepareStatement(SELECT_USER_BY_NICKNAME); 132 | Map[] result = this.select(selectStatement, new Object[]{nickname}); 133 | if (null == result) { 134 | return null; 135 | } 136 | List users = new LinkedList(); 137 | for (int i = 0; i < result.length; i++) { 138 | JSONObject jo = new JSONObject(result[i]); 139 | User user = new User(); 140 | user.readFromJson(jo); 141 | users.add(user); 142 | } 143 | return users.toArray(new User[]{}); 144 | } catch (Exception e) { 145 | e.printStackTrace(); 146 | return null; 147 | } finally { 148 | db.closeConnection(); 149 | } 150 | } 151 | 152 | private final String INSERT_USER = "INSERT INTO CHATUSER VALUES(?, ?, ?, ?)"; 153 | public boolean addUser(User user) { 154 | if (!db.openConnection()) { 155 | return false; 156 | } 157 | Connection conn = db.getSqlConnection(); 158 | try { 159 | PreparedStatement insertStatement = conn.prepareStatement(INSERT_USER); 160 | return this.insert(insertStatement, user); 161 | } catch (SQLException e) { 162 | e.printStackTrace(); 163 | return false; 164 | }finally { 165 | db.closeConnection(); 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/com/chatroom/controller/ChatController.java: -------------------------------------------------------------------------------- 1 | package com.chatroom.controller; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.util.Set; 6 | import java.util.concurrent.CopyOnWriteArraySet; 7 | 8 | import javax.websocket.OnClose; 9 | import javax.websocket.OnError; 10 | import javax.websocket.OnMessage; 11 | import javax.websocket.OnOpen; 12 | import javax.websocket.Session; 13 | import javax.websocket.server.PathParam; 14 | import javax.websocket.server.ServerEndpoint; 15 | 16 | 17 | import org.json.JSONException; 18 | import org.json.JSONObject; 19 | 20 | import com.chatroom.business.UserBusiness; 21 | import com.chatroom.models.ChatMessage; 22 | import com.chatroom.models.User; 23 | 24 | @ServerEndpoint("/chatroom/chat/{username}") 25 | public class ChatController { 26 | public static final String MessageTypeSystemNotify = "SystemNotify"; 27 | public static final String MessageTypeChatMessage = "ChatMessage"; 28 | private static Set connectedUsers = new CopyOnWriteArraySet(); 29 | private static int onlineCount = 0; 30 | private Session session; 31 | private User user; 32 | private User chatingUser;// 正在私聊的用户 33 | public ChatController(){ 34 | 35 | } 36 | 37 | public static Set getConnectedUsers() { 38 | return connectedUsers; 39 | } 40 | 41 | public User getUser() { 42 | return user; 43 | } 44 | 45 | 46 | public User getChatingUser() { 47 | return chatingUser; 48 | } 49 | 50 | public void setChatingUser(User chatingUser) { 51 | this.chatingUser = chatingUser; 52 | } 53 | 54 | @OnOpen 55 | public void onOpen(Session session, @PathParam(value="username") String username) { 56 | this.session = session; 57 | session.setMaxBinaryMessageBufferSize(5 * 1024 *1024); 58 | User user = new User(); 59 | user.setUsername(username); 60 | this.user = new UserBusiness().queryUserWithUserName(username); 61 | user.setPassword(null); 62 | connectedUsers.add(this); 63 | userOnline(); 64 | sendNotifyMessage(); 65 | System.out.println(user.getUsername() + "上线!"); 66 | } 67 | 68 | @OnClose 69 | public void onClose(){ 70 | connectedUsers.remove(this); 71 | userOutline(); 72 | sendNotifyMessage(); 73 | System.out.println(user.getUsername() + "下线!"); 74 | // 下线后,应该发送通知 75 | user = null; 76 | } 77 | 78 | @OnMessage 79 | public void onMessage(String message, Session session){ 80 | try { 81 | JSONObject messageJson = new JSONObject(message); 82 | ChatMessage chatMessage = new ChatMessage(); 83 | chatMessage.readFromJson(messageJson); 84 | if (chatMessage.getFrom() == null) { 85 | return; 86 | } 87 | chatMessage.setMessageType(MessageTypeChatMessage); 88 | chatMessage.setFromNick(user.getNickname()); 89 | if (chatMessage.getTo() == null) { // 全局群聊消息 90 | for (ChatController chatController : connectedUsers) { 91 | if (chatController.user.getUsername().trim().equals(chatMessage.getFrom())) { 92 | continue; 93 | } 94 | // 发送 95 | if (chatMessage.getMessageContent() instanceof String) { 96 | try { 97 | String msgContent = chatMessage.toJson().toString(); 98 | chatController.sendMessageText(msgContent); 99 | } catch (IOException e) { 100 | // TODO Auto-generated catch block 101 | e.printStackTrace(); 102 | } 103 | }else { 104 | try { 105 | chatController.sendMessageStream(null); 106 | } catch (IOException e) { 107 | // TODO Auto-generated catch block 108 | e.printStackTrace(); 109 | } 110 | } 111 | } 112 | }else { // 私聊消息 113 | for (ChatController chatController : connectedUsers) { 114 | if (chatController.user.getUsername().trim().equals(chatMessage.getFrom().trim())) { 115 | continue;// 避免给自己发送,减少过度通信 116 | } 117 | if (!chatController.getUser().getUsername().trim().equals(chatMessage.getTo().trim())) { 118 | continue; 119 | } 120 | if (chatMessage.getMessageContent() instanceof String) { 121 | try { 122 | String msgContent = chatMessage.toJson().toString(); 123 | chatController.sendMessageText(msgContent); 124 | } catch (IOException e) { 125 | // TODO Auto-generated catch block 126 | e.printStackTrace(); 127 | } 128 | }else { 129 | try { 130 | chatController.sendMessageStream(null); 131 | } catch (IOException e) { 132 | // TODO Auto-generated catch block 133 | e.printStackTrace(); 134 | } 135 | } 136 | } 137 | } 138 | } catch (JSONException e) { 139 | // TODO Auto-generated catch block 140 | e.printStackTrace(); 141 | } 142 | } 143 | 144 | @OnMessage 145 | public void onMessage(ByteBuffer buffer, Session session) { 146 | if (chatingUser == null) { 147 | // 群聊消息 148 | for (ChatController chatController : connectedUsers) { 149 | if (chatController.getUser().getUsername().trim().equals(user.getUsername().trim())) { 150 | continue; 151 | } 152 | try { 153 | chatController.sendMessageStream(buffer); 154 | } catch (IOException e) { 155 | // TODO Auto-generated catch block 156 | e.printStackTrace(); 157 | } 158 | } 159 | }else { 160 | // 私聊消息 161 | for (ChatController chatController : connectedUsers) { 162 | if (chatController.getUser().getUsername().trim().equals(user.getUsername().trim())) { 163 | continue; 164 | } 165 | if (chatController.getUser().getUsername().trim().equals(chatingUser.getUsername().trim())) { // 目标用户 166 | try { 167 | chatController.sendMessageStream(buffer); 168 | } catch (IOException e) { 169 | // TODO Auto-generated catch block 170 | e.printStackTrace(); 171 | } 172 | break; 173 | } 174 | } 175 | } 176 | System.out.println(buffer); 177 | } 178 | 179 | @OnError 180 | public void onError(Throwable throwalble) { 181 | System.out.println(throwalble.getMessage()); 182 | } 183 | private synchronized void userOnline() { 184 | ChatController.onlineCount++; 185 | } 186 | private synchronized void userOutline() { 187 | ChatController.onlineCount--; 188 | } 189 | private void sendMessageText(String content) throws IOException { 190 | this.session.getBasicRemote().sendText(content); 191 | } 192 | private void sendMessageStream(ByteBuffer stream) throws IOException { 193 | this.session.getBasicRemote().sendBinary(stream); 194 | } 195 | private void sendNotifyMessage(){ 196 | ChatMessage message = new ChatMessage(); 197 | message.setMessageType(MessageTypeSystemNotify); 198 | message.setMessageContent(user.toString()); 199 | message.setFrom("system"); 200 | message.setTo(null); 201 | if (connectedUsers.size() > 0) { 202 | try { 203 | connectedUsers.iterator().next().session.getBasicRemote().sendText(message.toJson().toString()); 204 | } catch (IOException e) { 205 | e.printStackTrace(); 206 | } 207 | } 208 | 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/css/chat-main.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8"; 2 | .container { 3 | width: 100%; 4 | } 5 | 6 | .header-nav { 7 | width: 100%; 8 | margin: 0 auto; 9 | height: 32px; 10 | background: #33b3a1; 11 | text-align: center; 12 | } 13 | 14 | .header-nav a { 15 | text-align: center; 16 | line-height: 32px; 17 | font-size: 18px; 18 | color: #fff; 19 | font-family: "Consolas", "微软雅黑", "Arial"; 20 | } 21 | 22 | .content-top { 23 | width: 100%; 24 | height: 160px; 25 | background: #eee; 26 | border-bottom: 2px solid #999; 27 | line-height: 160px; 28 | } 29 | 30 | .content-top span.top-grid { 31 | margin-top: 20px; 32 | display: inline-block; 33 | width: 100%; 34 | } 35 | 36 | .content-top .chat-title { 37 | width: 120px; 38 | height: 120px; 39 | color: #fff; 40 | background: #33b3a1; 41 | border-radius: 60px; 42 | -webkit-border-radius: 60px; 43 | -moz-border-radius: 60px; 44 | text-align: center; 45 | line-height: 120px; 46 | font-size: 40px; 47 | display: inline-block; 48 | margin-left: calc(50% - 120px); 49 | margin-left: -webkit-calc(50% - 120px); 50 | margin-left: -moz-calc(50% - 120px); 51 | transition: background 0.2s ease-in-out; 52 | -webkit-transition: background 0.2s ease-in-out; 53 | -moz-transition: background 0.2s ease-in-out; 54 | } 55 | 56 | .content-top .chat-title:hover { 57 | background: #2cc6b0; 58 | } 59 | 60 | .content-top .chat-add { 61 | min-width: 60px; 62 | width: 60px; 63 | height: 40px; 64 | background: rgba(0, 0, 0, 0.15); 65 | display: inline-block; 66 | position: relative; 67 | z-index: 1000; 68 | left: 0; 69 | float: left; 70 | top: 40px; 71 | border-top-right-radius: 20px; 72 | border-bottom-right-radius: 20px; 73 | text-align: right; 74 | transition: background 0.2s ease-in-out; 75 | -webkit-transition: background 0.2s ease-in-out; 76 | -moz-transition: background 0.2s ease-in-out; 77 | } 78 | 79 | .content-top .chat-add:hover { 80 | background: rgba(0, 0, 0, 0.1); 81 | } 82 | 83 | .content-top .chat-close { 84 | min-width: 60px; 85 | height: 40px; 86 | width: 60px; 87 | background: rgba(0, 0, 0, 0.15); 88 | float: right; 89 | display: inline-block; 90 | z-index: 1000; 91 | position: relative; 92 | right: 0; 93 | float: right; 94 | top: 40px; 95 | border-top-left-radius: 20px; 96 | border-bottom-left-radius: 20px; 97 | text-align: left; 98 | transition: background 0.2s ease-in-out; 99 | -webkit-transition: background 0.2s ease-in-out; 100 | -moz-transition: background 0.2s ease-in-out; 101 | } 102 | .content-top .chat-close:hover { 103 | background: rgba(0, 0, 0, 0.1); 104 | } 105 | 106 | .content-top .chat-add i { 107 | line-height: 40px; 108 | display: inline; 109 | padding-right: 8px; 110 | color: #fff; 111 | font-size: 28px; 112 | } 113 | 114 | .content-top .chat-close i { 115 | line-height: 40px; 116 | display: inline; 117 | padding-left: 8px; 118 | color: #fff; 119 | font-size: 28px; 120 | position: relative; 121 | } 122 | 123 | .online-list { 124 | width: 90%; 125 | position: relative; 126 | z-index: 2000; 127 | left: 5%; 128 | height: 360px; 129 | border-radius: 8px; 130 | -webkit-border-radius: 8px; 131 | -moz-border-radius: 8px; 132 | top: 16px; 133 | transition: all 0.6s ease-in-out; 134 | -webkit-transition: all 0.6s ease-in-out; 135 | -moz-transition: all 0.6s ease-in-out; 136 | } 137 | 138 | .hide-list { 139 | opacity: 0; 140 | z-index: -2000; 141 | } 142 | 143 | .online-list .panel-nav { 144 | width: 100%; 145 | height: 48px; 146 | background: #33b3a1; 147 | text-align: center; 148 | line-height: 48px; 149 | border-top-left-radius: 8px; 150 | border-top-right-radius: 8px; 151 | } 152 | 153 | .online-list .panel-nav a { 154 | font-size: 18px; 155 | color: #fff; 156 | font-family: "Consolas", "微软雅黑", "Arial"; 157 | } 158 | 159 | .online-list .panel-nav a.opt-close { 160 | color: #fff; 161 | font-size: 18px; 162 | width: 48px; 163 | height: 48px; 164 | line-height: 48px; 165 | position: absolute; 166 | top: 0; 167 | right: 0; 168 | text-decoration: none; 169 | } 170 | 171 | .online-list .panel-nav a.opt-multichat { 172 | color: #fff; 173 | font-size: 18px; 174 | width: 48px; 175 | height: 48px; 176 | line-height: 48px; 177 | position: absolute; 178 | top: 0; 179 | left: 0; 180 | text-decoration: none; 181 | } 182 | .online-list a { 183 | text-decoration: none; 184 | } 185 | 186 | .online-list .panel-nav a.opt-close:hover { 187 | color: #f00; 188 | background: rgba(0, 0, 0, 0.1); 189 | } 190 | 191 | .online-list .panel-nav a.opt-multichat:hover { 192 | color: #f00; 193 | background: rgba(0, 0, 0, 0.1); 194 | } 195 | 196 | .online-list .contact-list { 197 | height: 312px; 198 | background: #e8edea; 199 | border-bottom-left-radius: 8px; 200 | border-bottom-right-radius: 8px; 201 | } 202 | 203 | .online-list .contact-list .users-list { 204 | width: 100%; 205 | height: 100%; 206 | overflow-y: scroll; 207 | } 208 | 209 | .online-list .contact-list .users-list .user-cell { 210 | width: 100%; 211 | height: 64px; 212 | line-height: 64px; 213 | } 214 | 215 | .online-list .contact-list .users-list .user-cell .user-face { 216 | width: 64px; 217 | height: 64px; 218 | display: inline-block; 219 | float: left; 220 | line-height: 64px; 221 | font-size: 46px; 222 | text-align: center; 223 | } 224 | .online-list .contact-list .users-list .user-cell span.user-info { 225 | margin: 0; 226 | height: 64px; 227 | width: calc(100% - 128px); 228 | width: -webkit-calc(100% - 128px); 229 | width: -moz-calc(100% - 128px); 230 | display: inline-block; 231 | float: left; 232 | } 233 | 234 | .online-list .contact-list .users-list .user-cell span.user-info .user-name, .user-nick { 235 | width: 100%; 236 | height: 50%; 237 | display: inline-block; 238 | line-height: 32px; 239 | font-family: "Consolas", "微软雅黑", "Arial"; 240 | } 241 | 242 | .user-name { 243 | font-size: 25px; 244 | color: #363636; 245 | } 246 | 247 | .user-nick { 248 | font-size: 14px; 249 | color: #999; 250 | } 251 | 252 | .online-list .contact-list .users-list .user-cell .opt-chat { 253 | width: 64px; 254 | height: 64px; 255 | display: inline-block; 256 | float: right; 257 | text-align: center; 258 | line-height: 64px; 259 | color: #9b9b9b; 260 | font-size: 26px; 261 | transition: all 0.15s ease-in-out; 262 | -webkit-transition: all 0.15s ease-in-out; 263 | -moz-transition: all 0.15s ease-in-out; 264 | } 265 | 266 | .online-list .contact-list .users-list .user-cell .opt-chat:hover { 267 | background: rgba(0, 0, 0, 0.2); 268 | color: #fff; 269 | } 270 | 271 | .online-list .contact-list .users-list .user-cell:nth-child(2n-1) { 272 | background: #dfede5; 273 | } 274 | 275 | .online-list .contact-list .users-list .user-cell:hover { 276 | background: #fdfdfd; 277 | } 278 | 279 | .content-message { 280 | width: 100%; 281 | height: 320px; 282 | background: #f8f3f0; 283 | overflow-y: scroll; 284 | } 285 | 286 | .content-message .messages-list { 287 | margin-top: 24px; 288 | width: 100%; 289 | } 290 | 291 | .content-message .messages-list .message-cell { 292 | width: 90%; 293 | min-height: 64px; 294 | margin-bottom: 16px; 295 | display: inline-block; 296 | animation: addanimation 0.8s 1 linear; 297 | -moz-animation: addanimation 0.8s 1 linear; 298 | -webkit-animation: addanimation 0.8s 1 linear; 299 | -ms-animation: addanimation 0.8s 1 linear; 300 | } 301 | 302 | /*涓ょ娑堟伅鏉ユ簮*/ 303 | 304 | .content-message .messages-list .message-cell a.user-face { 305 | width: 64px; 306 | height: 64px; 307 | display: inline-block; 308 | float: left; 309 | line-height: 64px; 310 | text-align: center; 311 | font-size: 46px; 312 | color: #333; 313 | } 314 | 315 | 316 | .content-message .messages-list .message-sent a.user-face { 317 | float: right; 318 | } 319 | 320 | .content-message .messages-list .message-sent{ 321 | background: #33b3a1; 322 | float: right; 323 | margin-right: 8px; 324 | border-top-left-radius: 10px; 325 | border-bottom-left-radius: 10px; 326 | } 327 | 328 | .content-message .messages-list .message-sent a.send-user { 329 | text-align: right; 330 | color: #eee; 331 | } 332 | 333 | .content-message .messages-list .message-recieved { 334 | background: #e2d2d2; 335 | margin-left: 8px; 336 | float: left; 337 | border-top-right-radius: 10px; 338 | border-bottom-right-radius: 10px; 339 | } 340 | 341 | .content-message .messages-list .message-recieved a.send-user { 342 | text-align: left; 343 | } 344 | 345 | /*娑堟伅浜х敓鏃剁殑澧炲姞鏃剁殑鍔ㄧ敾*/ 346 | 347 | @-ms-keyframes addanimation { 348 | from{-ms-transform:translateY(60px);opacity: 0;} 349 | to{-ms-transform:translateY(0);opacity: 1;} 350 | } 351 | @-webkit-keyframes addanimation { 352 | from{-webkit-transform:translateY(60px);opacity: 0;} 353 | to{-webkit-transform:translateY(0);opacity: 1;} 354 | } 355 | @-moz-keyframes addanimation { 356 | from{-moz-transform:translateY(60px);opacity: 0;} 357 | to{-moz-transform:translateY(0);opacity: 1;} 358 | } 359 | 360 | @keyframes addanimation { 361 | from{transform:translateY(60px);opacity: 0;} 362 | to{transform:translateY(0);opacity: 1;} 363 | } 364 | 365 | 366 | .content-message .messages-list .message-cell span.message-grid { 367 | height: 100%; 368 | width: calc(100% - 75px); 369 | width: -webkit-calc(100% - 75px); 370 | width: -moz-calc(100% - 75px); 371 | display: inline-block; 372 | } 373 | 374 | .content-message .messages-list .message-cell span.message-grid a.message-time { 375 | height: 15px; 376 | font-size: 12px; 377 | font-family: "Consolas", "微软雅黑", "Arial"; 378 | line-height: 15px; 379 | width: 100%; 380 | display: inline-block; 381 | color: #aaa; 382 | text-align: center; 383 | } 384 | 385 | 386 | .content-message .messages-list .message-sent span.message-grid a.message-time { 387 | color: #eee; 388 | } 389 | 390 | .content-message .messages-list .message-cell span.message-grid a.send-user { 391 | height: 15px; 392 | font-size: 12px; 393 | font-family: "Consolas", "微软雅黑", "Arial"; 394 | line-height: 15px; 395 | width: 100%; 396 | display: inline-block; 397 | color: #eee; 398 | } 399 | 400 | .content-message .messages-list .message-cell span.message-grid a.message-content { 401 | min-height: 44px; 402 | font-size: 15px; 403 | font-family: "Consolas", "微软雅黑", "Arial"; 404 | width: 100%; 405 | display: inline-block; 406 | color: #444; 407 | word-wrap: break-word; 408 | } 409 | 410 | .content-message .messages-list .message-cell span.message-grid a.message-content img { 411 | max-height: 120px; 412 | max-width: 300px; 413 | cursor: pointer; 414 | } 415 | 416 | .bottom-anchors { 417 | width: 100%; 418 | height: 5px; 419 | display: inline-block; 420 | } 421 | 422 | .content-message .messages-list .message-sent span.message-grid { 423 | float: right; 424 | } 425 | 426 | .binary-source { 427 | width: 100%; 428 | background: #ddd; 429 | height: 240px; 430 | transition: height 0.6s ease-in-out; 431 | -webkit-transition: height 0.6s ease-in-out; 432 | -moz-transition: height 0.6s ease-in-out; 433 | overflow: hidden; 434 | } 435 | 436 | .binary-source ul{ 437 | width: 100%; 438 | height: 100%; 439 | display: inline-block; 440 | } 441 | 442 | .binary-source ul li { 443 | width: 50%; 444 | height: 100%; 445 | display: inline-block; 446 | text-align: center; 447 | float: left; 448 | } 449 | .binary-source ul li:hover { 450 | background: #eee; 451 | } 452 | 453 | .binary-source ul li a{ 454 | line-height: 240px; 455 | font-size: 70px; 456 | color: #33b3a1; 457 | width: 100%; 458 | height: 100%; 459 | display: inline-block; 460 | } 461 | 462 | .hide-source { 463 | height: 0px; 464 | } 465 | 466 | .message-operate { 467 | width: 100%; 468 | height: 48px; 469 | background: #33b3a1; 470 | margin-bottom: 46px; 471 | } 472 | 473 | .message-operate .fm-message { 474 | width: 70%; 475 | height: 100%; 476 | float: left; 477 | } 478 | 479 | .message-operate .fm-message .message-input { 480 | height: 100%; 481 | width: 100%; 482 | background: rgba(0, 0, 0, 0.12); 483 | font-size: 18px; 484 | color: #eee; 485 | padding-left: 2%; 486 | font-family: "Consolas", "微软雅黑", "Arial"; 487 | } 488 | 489 | .message-operate ul { 490 | width: 28%; 491 | height: 100%; 492 | float: right; 493 | } 494 | 495 | .message-operate ul li { 496 | float: left; 497 | height: 100%; 498 | width: 50%; 499 | display: inline; 500 | color: #eee; 501 | text-align: center; 502 | background: rgba(0, 0, 0, 0.2); 503 | } 504 | 505 | .message-operate ul li:hover { 506 | background: rgba(0, 0, 0, 0.1); 507 | } 508 | 509 | .message-operate ul li a { 510 | line-height: 48px; 511 | font-size: 26px; 512 | } 513 | 514 | .preview-img { 515 | position: absolute; 516 | z-index: 50000; 517 | width: 100%; 518 | height: 100%; 519 | background: rgba(0, 0, 0, 0.6); 520 | text-align: center; 521 | left: 0; 522 | top: 0; 523 | } 524 | 525 | @media only screen 526 | and (min-device-width : 320px) 527 | and (max-device-width : 568px) 528 | and (orientation : portrait) {/* STYLES */ 529 | .user-name { 530 | font-size: 20px; 531 | color: #363636; 532 | } 533 | 534 | .user-nick { 535 | font-size: 12px; 536 | color: #999; 537 | } 538 | .content-message .messages-list .message-cell span.message-grid a.message-content img { 539 | max-width: 200px; 540 | } 541 | } -------------------------------------------------------------------------------- /src/main/resources/DesignDiagrams/ClassDiagram.mgc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 11 | 12 | 13 | 16 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 36 | 37 | 38 | 41 | 45 | 46 | 47 | 48 | 51 | 55 | 56 | 57 | 58 | 61 | 65 | 66 | 67 | 70 | 74 | 75 | 76 | 77 | 80 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 93 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 107 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 120 | 124 | 125 | 126 | 129 | 133 | 134 | 135 | 136 | 137 | 140 | 144 | 145 | 146 | 147 | 148 | 149 | 152 | 156 | 157 | 158 | 161 | 165 | 166 | 167 | 168 | 169 | 170 | 173 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 186 | 190 | 191 | 192 | 195 | 199 | 202 | 203 | 204 | 207 | 211 | 213 | 214 | 215 | 218 | 219 | 220 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/fonts/chatfonts.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/webapp/asserts/js/chat-websocket.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | 5 | var byte; 6 | var messageTo = null; 7 | 8 | var histories = []; 9 | 10 | var localStorage = window.localStorage; 11 | var sessionStrage = window.localStorage; 12 | 13 | 14 | Date.prototype.pattern = function (fmt) { 15 | var o = { 16 | "M+" : this.getMonth()+1, //月份 17 | "d+" : this.getDate(), //日 18 | "h+" : this.getHours()%12 == 0 ? 12 : this.getHours()%12, //小时 19 | "H+" : this.getHours(), //小时 20 | "m+" : this.getMinutes(), //分 21 | "s+" : this.getSeconds(), //秒 22 | "q+" : Math.floor((this.getMonth()+3)/3), //季度 23 | "S" : this.getMilliseconds() //毫秒 24 | }; 25 | var week = { 26 | "0" : "/u65e5", 27 | "1" : "/u4e00", 28 | "2" : "/u4e8c", 29 | "3" : "/u4e09", 30 | "4" : "/u56db", 31 | "5" : "/u4e94", 32 | "6" : "/u516d" 33 | }; 34 | if(/(y+)/.test(fmt)){ 35 | fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); 36 | } 37 | if(/(E+)/.test(fmt)){ 38 | fmt=fmt.replace(RegExp.$1, ((RegExp.$1.length>1) ? (RegExp.$1.length>2 ? "/u661f/u671f" : "/u5468") : "")+week[this.getDay()+""]); 39 | } 40 | for(var k in o){ 41 | if(new RegExp("("+ k +")").test(fmt)){ 42 | fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length))); 43 | } 44 | } 45 | return fmt; 46 | } 47 | 48 | function History(htmls, id) { 49 | this.id = id; 50 | this.htmls = htmls; 51 | return this; 52 | } 53 | 54 | 55 | $(document).ready(function(){ 56 | initConnection(); 57 | getOnlineUsers() 58 | loadChatHistory() 59 | $('#switch-list').click(function(){ 60 | var classname = $('.online-list').attr('class') 61 | if (classname.indexOf('hide-list') != -1) { 62 | classname = classname.replace(/\s*hide-list\s*/, '') 63 | }else { 64 | classname += ' hide-list ' 65 | } 66 | $('.online-list').attr('class', classname) 67 | }) 68 | 69 | $('#opt-close').click(function(){ 70 | var classname = $('.online-list').attr('class') 71 | classname = classname.replace(/\s*hide-list\s*/, '') 72 | classname += ' hide-list ' 73 | $('.online-list').attr('class', classname) 74 | }) 75 | 76 | $('#send-message').click(function(){ 77 | sendMessage() 78 | }) 79 | 80 | $('#logout').click(function(){ 81 | closeConnection() 82 | }) 83 | 84 | // 显示及隐藏二进制数据传输 85 | $('#other-message').click(function(){ 86 | var classname = $('.binary-source').attr('class') 87 | if (null != classname && classname.indexOf('hide-source') != -1) { 88 | classname = classname.replace(/\s*hide-source\s*/, '') 89 | }else { 90 | classname += ' hide-source ' 91 | } 92 | if (null != classname) { 93 | $('.binary-source').attr('class', classname) 94 | var inputElem = $('#message-content')[0] 95 | inputElem.scrollIntoView(false) 96 | } 97 | }) 98 | 99 | // 检查是否支持多媒体 100 | // initAudio(); 101 | $('#image').click(function(){ 102 | $('#image-input').click(); 103 | $('#image-input')[0].onchange = function(event) { 104 | var reader = new FileReader(); 105 | var preview = new FileReader() 106 | var input = event.target; 107 | var fileUrl = input.files[0]; 108 | reader.readAsArrayBuffer(fileUrl) 109 | preview.readAsDataURL(fileUrl) 110 | reader.onload = function(event){ 111 | websocket.binaryType="arraybuffer"; 112 | byte = event.target.result; 113 | websocket.send(event.target.result); 114 | } 115 | preview.onload = function(event) { 116 | var url = event.target.result; 117 | var time = new Date(); 118 | // var timeStr = time.getHours() + ':' + time.getMinutes(); 119 | var imgMsgTemplate = '
  • \ 120 | \ 121 | \ 122 | {{time}}\ 123 | \ 124 | \ 125 |
  • '; 126 | var msgText = imgMsgTemplate.replace(/\s*{{src}}\s*/, event.target.result).replace(/\s*{{time}}\s*/, time.pattern("HH:mm")); 127 | $('.messages-list').append(msgText) 128 | }; 129 | } 130 | }) 131 | 132 | 133 | $('#multichat').click(function(){ 134 | if (null == messageTo){ 135 | return; 136 | } 137 | $('#chat-title').html('群聊中...') 138 | backupCurHistory() 139 | var his = getHistory(null) 140 | if (null != his){ 141 | $('.messages-list').html(his.htmls) 142 | }else { 143 | $('.messages-list').html(''); 144 | } 145 | messageTo = null; 146 | }) 147 | 148 | }) 149 | 150 | function findHistory(history) { 151 | for (var i = 0;i < histories.length; i++) { 152 | var cur = histories[i] 153 | if (cur.id == history.id && cur.from == history.from) { 154 | return cur; 155 | } 156 | } 157 | } 158 | var websocket = null; 159 | function initConnection(){ 160 | if ("WebSocket" in window) { 161 | websocket = new WebSocket(url) 162 | }else{ 163 | alert ('您当前的浏览器不支持WebSocket!') 164 | } 165 | websocket.onerror = function(){ 166 | alert('error!'); 167 | } 168 | websocket.onopen = function(){ 169 | } 170 | websocket.onmessage = function(event){ 171 | 172 | if (event.data instanceof ArrayBuffer) { 173 | // 二进制数据 174 | byte = event.data; 175 | return; 176 | }else if (event.data instanceof Blob) { 177 | byte = event.data 178 | var reader = new FileReader() 179 | reader.readAsDataURL(byte) 180 | var time = new Date() 181 | // var timeStr = time.getHours() + ':' + time.getMinutes() 182 | reader.onload = function(event) { 183 | var imgMsgTemplate = '
  • \ 184 | \ 185 | \ 186 | {{time}}\ 187 | \ 188 | \ 189 |
  • '; 190 | var msgText = imgMsgTemplate.replace(/\s*{{src}}\s*/, event.target.result).replace(/\s*{{time}}\s*/, time.pattern("HH:mm")); 191 | $('.messages-list').append(msgText) 192 | } 193 | return; 194 | } 195 | var msg = JSON.parse(event.data) 196 | if (msg.to == null) { 197 | // 群聊消息 198 | if (messageTo == null) { 199 | // 当前为群聊模式 200 | 201 | }else { 202 | // 当前非群聊模式,切换至群聊模式,并备份当前聊天记录 203 | $('#multichat').click() 204 | // var chatHistory = $('.messages-list').html() 205 | // var id = messageTo; 206 | // var his = new History(chatHistory, id) 207 | // var curHistory = null; 208 | // if ((curHistory = findHistory(his)) != null) {// 存在,更新原记录 209 | // curHistory.htmls = his.htmls; 210 | // curHistory.id = his.id; 211 | // }else {// 不存在,直接push 212 | // histories.push(his); 213 | // } 214 | // // 将聊天记录写入界面 215 | // his.id = null; 216 | // his.htmls = null; 217 | // curHistory = findHistory(his) 218 | // if (null == curHistory) { 219 | // // 不存在记录 220 | // $('.messages-list').html('') 221 | // }else { 222 | // $('.messages-list').html(curHistory.htmls) 223 | // } 224 | // 225 | // messageTo = null;// 置为null表示群聊模式 226 | // $('#chat-title').html('群聊中...') 227 | } 228 | } 229 | // 当前为私聊模式 230 | if (null != msg.to) { 231 | // 私聊消息 232 | if (messageTo != msg.from) { // 当前私聊对象与消息到来的对象不同,备份聊天数据 233 | backupCurHistory() 234 | // var htmls = $('.messages-list').html() 235 | // var id = messageTo; 236 | // var his = new History(htmls, id); 237 | // var foundHis = null; 238 | // if ((foundHis = findHistory(his)) == null) { //不存在该历史纪录 239 | // histories.push(his) 240 | // }else { 241 | // // 存在该记录 242 | // histories.pop(foundHis) 243 | // histories.push(his) 244 | // } 245 | // 加载历史纪录 246 | // his.htmls = null; 247 | // his.id = msg.to; 248 | // foundHis = findHistory(his) 249 | // if (foundHis == null) // 不存在历史纪录 250 | // $('.messages-list').html('') 251 | // else 252 | // $('.messages-list').html(foundHis.htmls) 253 | var storedHis = getHistory(msg.from) 254 | if (null == storedHis) { 255 | $('.messages-list').html('') 256 | }else { 257 | $('.messages-list').html(storedHis.htmls) 258 | } 259 | } 260 | messageTo = msg.from; // 当前私信对象更改 261 | $.ajax({ 262 | url: 'information?username=' + messageTo, 263 | typte: 'GET', 264 | success: function(data) { 265 | $('#chat-title').html('与' + data.nickname + '聊天中...') 266 | $('#message-content').removeAttr('disabled'); 267 | }, 268 | error: function(){ 269 | 270 | } 271 | }) 272 | } 273 | // 需要判断msg类型 274 | if (null != msg.messageType && msg.messageType.trim() == 'ChatMessage') { 275 | addRecievedMessage(msg) 276 | }else { 277 | // 系统消息,用于通知更新用户列表 278 | if (null != msg.messageType && msg.messageType == 'SystemNotify') { 279 | getOnlineUsers()// 获取用户列表 280 | } 281 | } 282 | } 283 | websocket.onclose = function(){ 284 | logout() 285 | alert('您当前已经被注销!请重新登录'); 286 | window.location.href = window.location.href; 287 | } 288 | window.onbeforeunload = function(){ 289 | backupCurHistory() 290 | return '确定离开当前页面吗?\n注意:您的连接可能暂时被关闭,我们将自动为您备份聊天记录!' 291 | } 292 | } 293 | 294 | function closeConnection(){ 295 | websocket.close(); 296 | } 297 | 298 | 299 | function sendMessage() { 300 | var content = $('#message-content').val() 301 | if (content == null || content.trim().length <= 0) { 302 | $('#message-content')[0].focus() 303 | return false; 304 | } 305 | var date = new Date(); 306 | var msg = { 307 | time: date.pattern("HH:mm"), 308 | messageContent: content, 309 | messageType: 'ChatMessage', 310 | from: loginedUsername, 311 | }// 群聊消息 312 | if (null != messageTo) { 313 | msg.to = messageTo 314 | } 315 | addSentMessage(msg) 316 | $('#message-content').val('')// 滞空 317 | // 发送 318 | if (websocket == null) { 319 | initConnection() 320 | } 321 | websocket.send(JSON.stringify(msg)) 322 | return false; 323 | } 324 | 325 | function addSentMessage(msg) { 326 | var sentMsgTemplate = '
  • \ 327 | \ 328 | \ 329 | {{time}}\ 330 | {{user}}\ 331 | {{content}}\ 332 | \ 333 |
  • '; 334 | if (msg.time == null) { 335 | var now = new Date() 336 | msg.time = now.pattern("HH:mm") 337 | } 338 | var msgText = sentMsgTemplate.replace(/\s*{{time}}\s*/, msg.time).replace(/\s*{{content}}\a*/, msg.messageContent).replace(/\s*{{user}}\s*/, loginedUsernick); 339 | $('.messages-list').append(msgText); 340 | scrollToBottom() 341 | } 342 | 343 | function addRecievedMessage(msg) { 344 | var recievedMsgTemplate = '
  • \ 345 | \ 346 | \ 347 | {{time}}\ 348 | {{user}}\ 349 | {{content}}\ 350 | \ 351 |
  • '; 352 | if (msg.time == null) { 353 | var now = new Date() 354 | msg.time = now.pattern("HH:mm") 355 | } 356 | var msgText = recievedMsgTemplate.replace(/\s*{{time}}\s*/, msg.time).replace(/\s*{{content}}\a*/, msg.messageContent).replace(/\s*{{user}}\s*/, msg.fromNick); 357 | $('.messages-list').append(msgText) 358 | scrollToBottom() 359 | } 360 | 361 | function scrollToBottom() { 362 | var destView = $('.bottom-anchors')[0]; 363 | destView.scrollIntoView(false); 364 | } 365 | 366 | function logout(){ 367 | $.ajax({ 368 | url: 'logout', 369 | type: 'POST', 370 | data: JSON.stringify({}), 371 | dataType: "JSON", 372 | contentType: "application/json;charset=utf-8", 373 | success: function (){ 374 | }, 375 | error: function(){ 376 | 377 | } 378 | }) 379 | } 380 | 381 | function getOnlineUsers(){ 382 | $.ajax({ 383 | url: 'userlist', 384 | type: 'GET', 385 | success: function(data){ 386 | var onlineUserTemplate = '
  • \ 387 | \ 388 | \ 389 | \ 390 |
  • '; 391 | var htmls = []; 392 | var allUsers = data 393 | if (allUsers instanceof Array) { 394 | for (var i = 0; i < allUsers.length; i++) { 395 | var user = allUsers[i]; 396 | var _tmpHtml = onlineUserTemplate.replace(/\s*{{username}}\s*/, user.username).replace(/\s*{{nickname}}\s*/, user.nickname); 397 | htmls.push(_tmpHtml) 398 | } 399 | }else { 400 | 401 | } 402 | $('.users-list').html(''); 403 | $('.users-list').append(htmls.join('')) 404 | // 绑定事件 405 | }, 406 | error: function(){ 407 | 408 | } 409 | }) 410 | } 411 | 412 | function startSingleChat(){ 413 | var target = event.target; 414 | var username = target.previousElementSibling.firstElementChild.innerHTML; 415 | var nickname = target.previousElementSibling.lastElementChild.innerHTML; 416 | var destUser = { 417 | username: username, 418 | nickname: nickname, 419 | } 420 | var selfuser = { 421 | username: loginedUsername, 422 | nickname: loginedUsernick, 423 | } 424 | if (selfuser.username == destUser.username) { 425 | alert('不能同自己发送消息!'); 426 | return; 427 | } 428 | // 请求建立连接 429 | $('#message-content').attr('disabled', 'disabled'); 430 | $.ajax({ 431 | url: 'singlechat', 432 | type: 'POST', 433 | data: JSON.stringify({from: selfuser, to: destUser}), 434 | dataType: "JSON", 435 | contentType: "application/json;charset=utf-8", 436 | success: function(msg){ 437 | $('#chat-title').html('与' + destUser.nickname + '聊天中...') 438 | $('#message-content').removeAttr('disabled'); 439 | backupCurHistory() 440 | // 查找记录 441 | storedHis = getHistory(destUser.username); 442 | if (null == storedHis) { 443 | $('.messages-list').html('') 444 | }else { 445 | $('.messages-list').html(storedHis.htmls) 446 | } 447 | messageTo = destUser.username 448 | }, 449 | error: function() { 450 | 451 | } 452 | }) 453 | } 454 | var data,p; 455 | function initAudio(){ 456 | //调整兼容 457 | var AudioContext=AudioContext||webkitAudioContext; 458 | var context; 459 | context=new AudioContext; 460 | navigator.getUserMedia= 461 | navigator.getUserMedia|| 462 | navigator.webkitGetUserMedia|| 463 | navigator.mozGetUserMedia; 464 | //请求麦克风 465 | navigator.getUserMedia({audio:true},function(e){ 466 | 467 | //从麦克风的输入流创建源节点 468 | var stream=context.createMediaStreamSource(e); 469 | //用于录音的processor节点 470 | var recorder=context.createScriptProcessor(1024,1,0); 471 | recorder.onaudioprocess=function(e){ 472 | if(record.value=="停止")data.push(e.inputBuffer.getChannelData(0)); 473 | }; 474 | //用于播放的processor节点 475 | var player=context.createScriptProcessor(1024,0,1); 476 | player.onaudioprocess=function(e){ 477 | if(!data)return; 478 | var i,s=data[p++]; 479 | if(!s)return play.value=="停止"&&play.click(); 480 | var buffer=e.outputBuffer.getChannelData(0); 481 | for(i=0;i sW) { 550 | r = h / w 551 | w = sW - 30; 552 | h = w * r 553 | } 554 | if (h > sH) { 555 | r = w / h 556 | h = sH - 30; 557 | w = h * r 558 | } 559 | var top = (sH - h) / 2 560 | var left = (sW - w) / 2 561 | var previewView = '
    \ 562 | \ 563 |
    '; 564 | previewView = previewView.replace(/\s*{{src}}\s*/, img.src).replace(/\s*{{top}}\s*/, top + 'px').replace(/\s*{{left}}\s*/, left + 'px').replace(/\s*{{width}}\s*/, w + 'px').replace(/\s*{{height}}\s*/, h + 'px'); 565 | $('body').append(previewView) 566 | } 567 | 568 | function backupCurHistory(){ 569 | // 备份当前历史纪录 570 | // 从localStorage读取历史纪录 571 | histories = getHistories() 572 | var curHis = new History($('.messages-list').html(), messageTo) 573 | curHis.from = loginedUsername 574 | var storedHis = findHistory(curHis); 575 | if (null == storedHis) { 576 | histories.push(curHis); 577 | }else { 578 | histories.splice(histories.indexOf(storedHis), 1); 579 | histories.push(curHis) 580 | } 581 | saveHistories(histories) 582 | } 583 | 584 | function getHistory(historyid) { 585 | // 读取记录 586 | histories = getHistories() 587 | var desthis = new History(null, historyid); 588 | desthis.from = loginedUsername 589 | // 查找记录 590 | storedHis = findHistory(desthis); 591 | return storedHis; 592 | } 593 | 594 | function getHistories() { 595 | histories = window.localStorage.getItem('histories') 596 | if (null == histories) { 597 | histories = new Array() 598 | }else { 599 | histories = JSON.parse(histories) 600 | } 601 | return histories; 602 | } 603 | 604 | 605 | function saveHistories(his) { 606 | // 将历史纪录存储到storage 607 | window.localStorage.setItem('histories', JSON.stringify(his)); 608 | } 609 | 610 | function loadChatHistory() { 611 | histories = getHistories(); 612 | var destHis = new History(null, messageTo); 613 | destHis.from = loginedUsername 614 | var storedHis = findHistory(destHis); 615 | if (null == storedHis) { 616 | $('.messages-list').html('') 617 | }else { 618 | $('.messages-list').html(storedHis.htmls) 619 | } 620 | } -------------------------------------------------------------------------------- /src/main/webapp/asserts/selection.json: -------------------------------------------------------------------------------- 1 | { 2 | "IcoMoonType": "selection", 3 | "icons": [ 4 | { 5 | "icon": { 6 | "paths": [ 7 | "M149.239 302.028c-8.718 0-15.756 7.059-15.756 15.756 0 1.492 0.021 168.314 0.021 169.826 0.924 74.157 23.697 145.604 65.901 206.632 3.046 4.412 7.962 6.785 12.962 6.785 3.088 0 6.218-0.903 8.928-2.794 7.164-4.958 8.949-14.768 3.991-21.911-38.591-55.839-59.452-121.236-60.292-189.027 0-2.962-0.021-168.041-0.021-169.511 0.021-8.697-7.017-15.756-15.735-15.756z", 8 | "M890.055 317.784c0-8.697-7.038-15.756-15.756-15.756-8.697 0-15.756 7.059-15.756 15.756 0 1.996-0.021 167.263-0.042 170.919-3.088 185.939-158.629 337.237-346.732 337.237-70.313 0-138-20.693-195.771-59.851-7.227-4.874-16.995-3.004-21.89 4.202-4.874 7.206-2.899 17.016 4.307 21.89 58.675 39.768 119.051 61.805 197.83 64.704v127.748h-167.2c-8.718 0-15.756 7.059-15.756 15.756s7.038 15.756 15.756 15.756h365.555c8.697 0 15.756-7.059 15.756-15.756 0-8.718-7.038-15.756-15.756-15.756h-166.822v-127.559c204.804-8.214 359.106-170.015 362.383-367.845-0-2.206-0.105-169.742-0.105-171.444z", 9 | "M716.762 496.244v-173.293c0-8.697-7.038-15.756-15.756-15.756s-15.756 7.038-15.756 15.756v173.293c0 95.921-72.981 165.541-173.503 165.541-36.385 0-71.384-10.756-101.236-31.070-7.164-4.895-16.995-3.025-21.89 4.16s-3.046 16.995 4.16 21.89c35.083 23.907 76.237 36.532 118.967 36.532 118.799 0 205.014-82.875 205.014-197.052z", 10 | "M338.686 480.509v-283.583c0-98.799 74.409-173.293 173.083-173.293 68.821 0 132.307 39.789 161.717 101.362 3.76 7.836 13.151 11.176 21.008 7.416 7.878-3.739 11.176-13.151 7.437-21.008-34.6-72.456-109.261-119.282-190.141-119.282-114.723 0-204.594 89.955-204.594 204.804v283.583c0 8.676-0.126 16.449-0.294 24.243-0.294 15.693-0.588 31.932 0.315 55.124 0.315 8.487 7.311 15.147 15.735 15.147 0.21 0 0.42 0 0.609 0 8.697-0.336 15.483-7.668 15.126-16.344-0.84-22.31-0.567-37.373-0.273-53.339 0.105-7.983 0.273-15.966 0.273-24.831z", 11 | "M107.749 901.945c2.983 2.626 6.68 3.907 10.378 3.907 4.37 0 8.739-1.807 11.848-5.378l712.267-814.028c5.714-6.554 5.063-16.491-1.492-22.226-6.533-5.714-16.47-5.084-22.226 1.471l-712.267 814.028c-5.714 6.554-5.063 16.512 1.492 22.226z" 12 | ], 13 | "attrs": [ 14 | { 15 | "fill": "rgb(153, 152, 153)" 16 | }, 17 | { 18 | "fill": "rgb(153, 152, 153)" 19 | }, 20 | { 21 | "fill": "rgb(153, 152, 153)" 22 | }, 23 | { 24 | "fill": "rgb(153, 152, 153)" 25 | }, 26 | { 27 | "fill": "rgb(153, 152, 153)" 28 | } 29 | ], 30 | "isMulticolor": false, 31 | "grid": 0, 32 | "tags": [ 33 | "disableaudio" 34 | ] 35 | }, 36 | "attrs": [ 37 | { 38 | "fill": "rgb(153, 152, 153)" 39 | }, 40 | { 41 | "fill": "rgb(153, 152, 153)" 42 | }, 43 | { 44 | "fill": "rgb(153, 152, 153)" 45 | }, 46 | { 47 | "fill": "rgb(153, 152, 153)" 48 | }, 49 | { 50 | "fill": "rgb(153, 152, 153)" 51 | } 52 | ], 53 | "properties": { 54 | "order": 42, 55 | "id": 21, 56 | "prevSize": 32, 57 | "code": 59666, 58 | "name": "disableaudio" 59 | }, 60 | "setIdx": 0, 61 | "setId": 1, 62 | "iconIdx": 0 63 | }, 64 | { 65 | "icon": { 66 | "paths": [ 67 | "M81.867 866.463h860.266c40.797 0 73.989-33.192 73.989-73.989v-545.192c0-40.797-33.192-73.968-73.989-73.968h-860.266c-40.797 0-73.989 33.192-73.989 73.968v545.192c0 40.797 33.192 73.989 73.989 73.989zM942.133 834.973h-860.266c-23.424 0-42.478-19.054-42.478-42.478v-29.117l345.829-300.725 191.506 191.506 95.312-127.097 312.343 267.701c-1.197 22.331-19.6 40.209-42.247 40.209zM81.867 204.804h860.266c23.424 0 42.478 19.054 42.478 42.478v506.16l-317.595-272.218-93.715 124.954-186.591-186.591-347.321 302.028v-474.333c0-23.424 19.054-42.478 42.478-42.478z", 68 | "M811.318 456.876c52.12 0 94.535-42.415 94.535-94.535s-42.415-94.535-94.535-94.535-94.535 42.394-94.535 94.535c0.021 52.12 42.415 94.535 94.535 94.535zM811.318 299.339c34.747 0 63.023 28.276 63.023 63.023s-28.255 63.023-63.023 63.023-63.023-28.276-63.023-63.023 28.276-63.023 63.023-63.023z" 69 | ], 70 | "attrs": [ 71 | { 72 | "fill": "rgb(153, 152, 153)" 73 | }, 74 | { 75 | "fill": "rgb(153, 152, 153)" 76 | } 77 | ], 78 | "isMulticolor": false, 79 | "grid": 0, 80 | "tags": [ 81 | "image" 82 | ] 83 | }, 84 | "attrs": [ 85 | { 86 | "fill": "rgb(153, 152, 153)" 87 | }, 88 | { 89 | "fill": "rgb(153, 152, 153)" 90 | } 91 | ], 92 | "properties": { 93 | "order": 41, 94 | "id": 20, 95 | "prevSize": 32, 96 | "code": 59667, 97 | "name": "image" 98 | }, 99 | "setIdx": 0, 100 | "setId": 1, 101 | "iconIdx": 1 102 | }, 103 | { 104 | "icon": { 105 | "paths": [ 106 | "M149.428 302.028c-8.718 0-15.756 7.059-15.756 15.756 0 1.492 0.126 168.314 0.126 169.826 2.437 198.691 157.789 361.228 362.593 369.421v127.559h-167.2c-8.718 0-15.756 7.059-15.756 15.756s7.038 15.756 15.756 15.756h365.555c8.718 0 15.756-7.059 15.756-15.756s-7.038-15.756-15.756-15.756h-166.822v-127.559c204.804-8.214 359.106-170.015 362.383-367.845 0.021-2.185 0-169.742 0-171.423 0-8.697-7.080-15.756-15.777-15.756s-15.756 7.059-15.756 15.756c0 1.996-0.021 167.284-0.042 170.919-3.088 185.939-158.629 337.237-346.732 337.237-188.901 0-344.442-151.949-346.732-338.623 0-2.962-0.021-168.041-0.021-169.532-0.063-8.676-7.101-15.735-15.819-15.735z", 107 | "M716.951 485.131v-284.823c0-114.807-88.905-208.186-204.804-208.186-115.921 0-204.804 93.379-204.804 208.186v284.823c0 114.786 88.884 208.165 204.804 208.165 115.9 0 204.804-93.379 204.804-208.165zM338.854 485.131v-284.823c0-97.413 74.745-176.675 173.293-176.675 98.526 0 173.293 79.262 173.293 176.675v284.823c0 97.413-74.766 176.675-173.293 176.675-98.547-0.021-173.293-79.283-173.293-176.675z" 108 | ], 109 | "attrs": [ 110 | { 111 | "fill": "rgb(153, 152, 153)" 112 | }, 113 | { 114 | "fill": "rgb(153, 152, 153)" 115 | } 116 | ], 117 | "isMulticolor": false, 118 | "grid": 0, 119 | "tags": [ 120 | "enableaudio" 121 | ] 122 | }, 123 | "attrs": [ 124 | { 125 | "fill": "rgb(153, 152, 153)" 126 | }, 127 | { 128 | "fill": "rgb(153, 152, 153)" 129 | } 130 | ], 131 | "properties": { 132 | "order": 40, 133 | "id": 19, 134 | "prevSize": 32, 135 | "code": 59668, 136 | "name": "enableaudio" 137 | }, 138 | "setIdx": 0, 139 | "setId": 1, 140 | "iconIdx": 2 141 | }, 142 | { 143 | "icon": { 144 | "paths": [ 145 | "M998.966 760.436c3.165 3.165 7.326 4.768 11.488 4.768s8.323-1.582 11.488-4.768c6.351-6.351 6.351-16.646 0-22.975l-490.325-490.347c-6.091-6.091-16.885-6.091-22.997 0l-490.304 490.347c-6.351 6.329-6.351 16.625 0 22.975s16.646 6.351 22.997 0l478.816-478.838 478.838 478.838z" 146 | ], 147 | "attrs": [ 148 | { 149 | "fill": "rgb(153, 152, 153)" 150 | } 151 | ], 152 | "isMulticolor": false, 153 | "grid": 0, 154 | "tags": [ 155 | "up" 156 | ] 157 | }, 158 | "attrs": [ 159 | { 160 | "fill": "rgb(153, 152, 153)" 161 | } 162 | ], 163 | "properties": { 164 | "order": 39, 165 | "id": 17, 166 | "prevSize": 32, 167 | "code": 59665, 168 | "name": "up" 169 | }, 170 | "setIdx": 0, 171 | "setId": 1, 172 | "iconIdx": 3 173 | }, 174 | { 175 | "icon": { 176 | "paths": [ 177 | "M512.021 11.323c-280.432 0-508.555 228.144-508.555 508.555s228.144 508.555 508.555 508.555c280.411 0 508.555-228.144 508.555-508.555s-228.144-508.555-508.555-508.555zM262.197 926.083c17.983-7.395 40.335-7.92 73.947-7.92h47.94c5.231 0 10.084-2.584 13.025-6.891l55.061-80.733c2.206-3.235 2.857-6.996 2.458-10.609 18.13 8.97 37.331 14.222 57.162 14.222 20.314 0 39.957-5.567 58.485-14.936-0.609 3.886 0.168 8.004 2.605 11.47l56.385 80.733c2.941 4.223 7.752 6.743 12.92 6.743h57.456c30.776 0 48.675 0.378 63.359 7.164-72.96 45.335-158.944 71.594-250.979 71.594-91.531 0-177.095-25.966-249.824-70.838zM672.31 390.302c17.899 61.826 33.255 170.73-1.765 199.72-3.613 3.004-5.693 7.437-5.693 12.142 0 88.379-72.056 200.498-153.041 200.498s-153.020-112.118-153.020-200.498c0-2.185-0.441-4.349-1.323-6.344-46.259-105.396-7.143-202.892-6.743-203.859 0.777-1.912 1.197-3.97 1.197-6.029 0-52.372 9.411-92.434 127.958-92.434 2.941 0 5.819-0.819 8.319-2.374 33.906-21.029 66.132-31.68 95.795-31.68 33.423 0 61.406 13.844 78.758 38.969 16.596 24.033 20.335 54.662 10 81.93-1.239 3.172-1.387 6.68-0.441 9.958zM791.739 905.958c-26.092-19.411-54.305-19.306-92.203-19.306h-49.158l-51.679-73.989c-2.017-2.899-4.916-4.643-8.046-5.672 59.746-41.7 102.98-124.513 105.564-197.914 50.398-52.876 16.449-190.813 7.731-222.619 11.743-35.65 6.155-74.892-15.315-105.963-23.066-33.402-61.216-52.561-104.66-52.561-34.432 0-70.922 11.449-108.547 34.075-133.966 1.239-154.386 58.401-154.995 120.92-6.954 18.55-38.843 115.248 6.87 222.556 1.281 74.472 45.188 159.554 106.152 201.821-2.815 1.113-5.462 2.794-7.311 5.504l-50.355 73.842h-39.621c-41.049 0-74.304 0.189-103.358 19.684-119.744-86.762-197.83-227.598-197.83-386.458 0-263.038 214.006-477.043 477.043-477.043s477.043 214.006 477.043 477.043c-0.021 158.608-77.897 299.297-197.325 386.080z" 178 | ], 179 | "attrs": [ 180 | { 181 | "fill": "rgb(153, 152, 153)" 182 | } 183 | ], 184 | "isMulticolor": false, 185 | "grid": 0, 186 | "tags": [ 187 | "face" 188 | ] 189 | }, 190 | "attrs": [ 191 | { 192 | "fill": "rgb(153, 152, 153)" 193 | } 194 | ], 195 | "properties": { 196 | "order": 38, 197 | "id": 16, 198 | "prevSize": 32, 199 | "code": 59664, 200 | "name": "face" 201 | }, 202 | "setIdx": 0, 203 | "setId": 1, 204 | "iconIdx": 4 205 | }, 206 | { 207 | "icon": { 208 | "paths": [ 209 | "M186.178 781.986c17.773 0 31.044 23.128 31.044 43.824v145.405l197.55-171.947c2.71-2.347 5.355-5.078 8.022-7.83 3.627-3.755 9.132-9.431 11.628-9.431h387.867c37.914 0 62.066-22.061 62.066-57.586v-488.168c0-36.037-24.707-59.335-62.066-59.335h-744.712c-37.701 0-62.066 23.299-62.066 59.335v488.168c0 35.524 23.768 57.586 62.066 57.586h108.6zM46.555 724.4v-488.168c0-23.384 16.877-28.292 31.023-28.292h744.733c20.205 0 31.044 9.516 31.044 28.292v488.168c0 11.415-3.734 26.563-31.044 26.563h-387.888c-15.661 0-26.222 10.924-33.924 18.904-2.027 2.091-4.033 4.182-6.081 5.974l-146.173 127.227v-77.258c0-36.143-24.92-74.847-62.066-74.847h-108.6c-31.023 0-31.023-19.992-31.023-26.563z", 210 | "M946.422 52.785h-744.733c-37.701 0-62.045 23.299-62.045 59.335v33.775c0 8.577 6.934 15.511 15.511 15.511s15.511-6.934 15.511-15.511v-33.796c0-23.384 16.877-28.292 31.023-28.292h744.733c20.205 0 31.044 9.516 31.044 28.292v488.168c0 20.312-12.823 42.075-31.044 42.075h-28.441c-8.577 0-15.533 6.934-15.533 15.511 0 8.556 6.934 15.511 15.533 15.511h28.441c37.978 0 62.066-37.935 62.066-73.097v-488.168c0-36.037-24.728-59.314-62.066-59.314z" 211 | ], 212 | "attrs": [ 213 | { 214 | "fill": "rgb(153, 152, 153)" 215 | }, 216 | { 217 | "fill": "rgb(153, 152, 153)" 218 | } 219 | ], 220 | "isMulticolor": false, 221 | "grid": 0, 222 | "tags": [ 223 | "multichat" 224 | ] 225 | }, 226 | "attrs": [ 227 | { 228 | "fill": "rgb(153, 152, 153)" 229 | }, 230 | { 231 | "fill": "rgb(153, 152, 153)" 232 | } 233 | ], 234 | "properties": { 235 | "order": 37, 236 | "id": 15, 237 | "prevSize": 32, 238 | "code": 59659, 239 | "name": "multichat" 240 | }, 241 | "setIdx": 0, 242 | "setId": 1, 243 | "iconIdx": 5 244 | }, 245 | { 246 | "icon": { 247 | "paths": [ 248 | "M680 177.174v-80.010c0-57.97-61.021-95.991-120.015-95.991h-143.975c-59.015 0-120.015 38.021-120.015 95.991v80.010c0 19.138 19.288 32.004 48.006 32.004h287.993c28.718 0 48.006-12.866 48.006-32.004zM647.996 174.294c-2.688 1.259-8.534 2.859-16.002 2.859h-287.993c-7.446 0-13.292-1.6-16.002-2.859v-77.13c0-37.445 45.68-64.008 88.011-64.008h143.975c42.331 0 88.011 26.563 88.011 64.008v77.13z", 249 | "M552.005 721.178v-448.013c0-8.833-7.148-16.002-16.002-16.002-8.833 0-16.002 7.148-16.002 16.002v448.013c0 8.833 7.148 16.002 16.002 16.002 8.833 0 16.002-7.169 16.002-16.002z", 250 | "M455.993 721.178v-448.013c0-8.833-7.148-16.002-16.002-16.002s-16.002 7.148-16.002 16.002v448.013c0 8.833 7.148 16.002 16.002 16.002s16.002-7.169 16.002-16.002z", 251 | "M344 257.184c-8.854 0-16.002 7.148-16.002 16.002v503.188l93.366 204.911 1.366 2.432c6.273 9.089 15.149 20.845 21.976 25.198 1.408 0.896 3.307 1.6 4.993 2.39 10.156 6.892 22.68 11.543 38.298 11.543 33.135 0 57.351-19.97 72.329-59.101l87.67-198.553v-492.030c0-8.833-7.148-16.002-16.002-16.002-8.833 0-16.002 7.148-16.002 16.002v485.287l-77.279 175.361c-1.835 1.835-3.414 3.968-4.182 6.657-1.366 4.63-2.795 8.748-4.289 12.567l-2.731 6.209c-7.425 11.607-17.090 23.896-20.077 25.859-8.086 3.414-30.254 2.39-41.008-1.131-10.028-7.34-18.050-20.781-25.176-41.648-1.109-3.222-3.307-5.633-5.867-7.51l-75.38-165.439v-496.211c-0-8.833-7.148-15.981-16.002-15.981z" 252 | ], 253 | "attrs": [ 254 | { 255 | "fill": "rgb(153, 152, 153)" 256 | }, 257 | { 258 | "fill": "rgb(153, 152, 153)" 259 | }, 260 | { 261 | "fill": "rgb(153, 152, 153)" 262 | }, 263 | { 264 | "fill": "rgb(153, 152, 153)" 265 | } 266 | ], 267 | "isMulticolor": false, 268 | "grid": 0, 269 | "tags": [ 270 | "edit" 271 | ] 272 | }, 273 | "attrs": [ 274 | { 275 | "fill": "rgb(153, 152, 153)" 276 | }, 277 | { 278 | "fill": "rgb(153, 152, 153)" 279 | }, 280 | { 281 | "fill": "rgb(153, 152, 153)" 282 | }, 283 | { 284 | "fill": "rgb(153, 152, 153)" 285 | } 286 | ], 287 | "properties": { 288 | "order": 36, 289 | "id": 14, 290 | "prevSize": 32, 291 | "code": 59660, 292 | "name": "edit" 293 | }, 294 | "setIdx": 0, 295 | "setId": 1, 296 | "iconIdx": 6 297 | }, 298 | { 299 | "icon": { 300 | "paths": [ 301 | "M529.752 522.833c52.049-54.416 84.639-130.862 84.639-215.638 0-164.681-122.509-298.684-273.080-298.684s-273.035 134.003-273.035 298.684c0 84.23 32.181 160.266 83.615 214.614-75.172 9.126-151.891 71.189-151.891 152.323v180.954c0 84.707 68.89 160.38 153.598 160.38h375.471c84.707 0 153.598-75.672 153.598-160.38v-180.954c0-77.47-77.243-139.214-152.915-151.299zM102.414 307.195c0-145.86 107.17-264.546 238.942-264.546s238.942 118.663 238.942 264.546-107.17 264.546-238.942 264.546-238.942-118.686-238.942-264.546zM648.552 855.086c0 65.863-53.596 126.265-119.483 126.265h-375.471c-65.863 0-119.46-60.401-119.46-126.265v-180.954c0-67.297 73.396-119.46 136.529-119.46 4.666 0 8.876-1.889 11.948-4.916 44.789 35.162 99.501 56.123 158.718 56.123 57.488 0 110.812-19.641 154.872-52.959 2.23 1.047 4.643 1.775 7.26 1.775 65.704 0 145.086 53.278 145.086 119.46v180.931z", 302 | "M989.885 213.339h-290.149c-9.445 0-17.069 7.624-17.069 17.069s7.624 17.069 17.069 17.069h290.149c9.445 0 17.069-7.624 17.069-17.069s-7.647-17.069-17.069-17.069z", 303 | "M989.885 418.144c0-9.445-7.624-17.069-17.069-17.069h-290.149c-9.422 0-17.069 7.624-17.069 17.069 0 9.422 7.624 17.069 17.069 17.069h290.149c9.422 0 17.069-7.647 17.069-17.069z", 304 | "M989.885 588.81h-238.942c-9.445 0-17.069 7.624-17.069 17.069s7.624 17.069 17.069 17.069h238.942c9.445 0 17.069-7.624 17.069-17.069s-7.647-17.069-17.069-17.069z", 305 | "M972.816 776.546h-221.873c-9.445 0-17.069 7.624-17.069 17.069s7.624 17.069 17.069 17.069h221.873c9.445 0 17.069-7.624 17.069-17.069s-7.647-17.069-17.069-17.069z" 306 | ], 307 | "attrs": [ 308 | { 309 | "fill": "rgb(153, 152, 153)" 310 | }, 311 | { 312 | "fill": "rgb(153, 152, 153)" 313 | }, 314 | { 315 | "fill": "rgb(153, 152, 153)" 316 | }, 317 | { 318 | "fill": "rgb(153, 152, 153)" 319 | }, 320 | { 321 | "fill": "rgb(153, 152, 153)" 322 | } 323 | ], 324 | "isMulticolor": false, 325 | "grid": 0, 326 | "tags": [ 327 | "nickname" 328 | ] 329 | }, 330 | "attrs": [ 331 | { 332 | "fill": "rgb(153, 152, 153)" 333 | }, 334 | { 335 | "fill": "rgb(153, 152, 153)" 336 | }, 337 | { 338 | "fill": "rgb(153, 152, 153)" 339 | }, 340 | { 341 | "fill": "rgb(153, 152, 153)" 342 | }, 343 | { 344 | "fill": "rgb(153, 152, 153)" 345 | } 346 | ], 347 | "properties": { 348 | "order": 35, 349 | "id": 13, 350 | "prevSize": 32, 351 | "code": 59661, 352 | "name": "nickname" 353 | }, 354 | "setIdx": 0, 355 | "setId": 1, 356 | "iconIdx": 7 357 | }, 358 | { 359 | "icon": { 360 | "paths": [ 361 | "M939.509 62.621h-871.021c-42.181 0-76.49 34.308-76.49 76.468v551.045c0 42.181 34.308 76.468 76.49 76.468h151.038c24.536 0 44.486 19.97 44.486 44.486v166.314l233.437-200.089c8.044-6.892 18.306-10.689 28.932-10.689h413.129c42.181 0 76.49-34.308 76.49-76.468v-551.066c0-42.16-34.308-76.468-76.49-76.468zM983.995 690.134c0 24.515-19.949 44.486-44.486 44.486h-413.129c-18.242 0-35.951 6.529-49.777 18.413l-180.609 154.793v-96.737c0-42.181-34.308-76.468-76.468-76.468h-151.038c-24.536 0-44.486-19.97-44.486-44.486v-551.045c0-24.515 19.949-44.486 44.486-44.486h871.021c24.536 0 44.486 19.97 44.486 44.486v551.045z" 362 | ], 363 | "attrs": [ 364 | { 365 | "fill": "rgb(153, 152, 153)" 366 | } 367 | ], 368 | "isMulticolor": false, 369 | "grid": 0, 370 | "tags": [ 371 | "singlechat" 372 | ] 373 | }, 374 | "attrs": [ 375 | { 376 | "fill": "rgb(153, 152, 153)" 377 | } 378 | ], 379 | "properties": { 380 | "order": 34, 381 | "id": 12, 382 | "prevSize": 32, 383 | "code": 59662, 384 | "name": "singlechat" 385 | }, 386 | "setIdx": 0, 387 | "setId": 1, 388 | "iconIdx": 8 389 | }, 390 | { 391 | "icon": { 392 | "paths": [ 393 | "M1021.497 203.485l-24.124-24.147-651.192 651.192-319.553-319.553-24.124 24.124 343.7 343.677 675.293-675.293z" 394 | ], 395 | "attrs": [ 396 | { 397 | "fill": "rgb(153, 152, 153)" 398 | } 399 | ], 400 | "isMulticolor": false, 401 | "grid": 0, 402 | "tags": [ 403 | "yes" 404 | ] 405 | }, 406 | "attrs": [ 407 | { 408 | "fill": "rgb(153, 152, 153)" 409 | } 410 | ], 411 | "properties": { 412 | "order": 33, 413 | "id": 11, 414 | "prevSize": 32, 415 | "code": 59663, 416 | "name": "yes" 417 | }, 418 | "setIdx": 0, 419 | "setId": 1, 420 | "iconIdx": 9 421 | }, 422 | { 423 | "icon": { 424 | "paths": [ 425 | "M859.497 995.514h-695.032c-25.034 0-45.327-20.293-45.327-45.327v-392.88c0-25.034 20.293-45.289 45.327-45.289h75.557v-211.574c0-150.165 121.756-271.921 271.959-271.921s271.959 121.756 271.959 271.921v211.574h75.557c25.034 0 45.327 20.255 45.327 45.289v392.842c0 25.034-20.293 45.364-45.327 45.364zM466.655 771.385v88.15c0 25.034 20.293 45.327 45.327 45.327s45.327-20.293 45.327-45.327v-88.15c26.968-15.703 45.327-44.606 45.327-78.060 0-50.106-40.585-90.653-90.653-90.653s-90.653 40.547-90.653 90.653c0 33.454 18.358 62.357 45.327 78.060zM693.325 300.445c0-100.136-81.208-181.306-181.306-181.306-100.136 0-181.306 81.171-181.306 181.306v211.574h362.65v-211.574z" 426 | ], 427 | "attrs": [ 428 | { 429 | "fill": "rgb(153, 152, 153)" 430 | } 431 | ], 432 | "isMulticolor": false, 433 | "grid": 0, 434 | "tags": [ 435 | "password" 436 | ] 437 | }, 438 | "attrs": [ 439 | { 440 | "fill": "rgb(153, 152, 153)" 441 | } 442 | ], 443 | "properties": { 444 | "order": 32, 445 | "id": 10, 446 | "prevSize": 32, 447 | "code": 59657, 448 | "name": "password" 449 | }, 450 | "setIdx": 0, 451 | "setId": 1, 452 | "iconIdx": 10 453 | }, 454 | { 455 | "icon": { 456 | "paths": [ 457 | "M1011.142 948.785c0 0.038 0 0.076 0 0.114 0 25.83-20.937 46.768-46.768 46.768h-904.748c-25.868 0-46.806-20.937-46.806-46.768 0-0.038 0-0.076 0-0.114h-0c0 0 0-170.534 124.601-232.512 78.857-39.22 48.475-7.396 145.272-47.526 96.798-40.054 119.708-54.050 119.708-54.050l0.91-92.398c0 0-36.261-27.727-47.526-114.663-22.682 6.562-30.154-26.627-31.52-47.754-1.176-20.482-13.086-84.205 14.603-78.477-5.652-42.596-9.71-80.981-7.738-101.273 6.941-71.385 75.709-145.993 181.61-151.455 124.601 5.462 173.948 79.995 180.889 151.379 2.010 20.368-2.428 58.754-8.117 101.273 27.689-5.69 15.665 57.957 14.3 78.364-1.214 21.127-8.876 54.202-31.52 47.64-11.303 86.936-47.564 114.473-47.564 114.473l0.834 91.943c0 0 22.91 13.086 119.708 53.178 96.798 40.13 66.416 10.127 145.31 49.347 124.601 62.016 124.601 232.512 124.563 232.512v0z" 458 | ], 459 | "attrs": [ 460 | { 461 | "fill": "rgb(153, 152, 153)" 462 | } 463 | ], 464 | "isMulticolor": false, 465 | "grid": 0, 466 | "tags": [ 467 | "user" 468 | ] 469 | }, 470 | "attrs": [ 471 | { 472 | "fill": "rgb(153, 152, 153)" 473 | } 474 | ], 475 | "properties": { 476 | "order": 31, 477 | "id": 9, 478 | "prevSize": 32, 479 | "code": 59658, 480 | "name": "user" 481 | }, 482 | "setIdx": 0, 483 | "setId": 1, 484 | "iconIdx": 11 485 | }, 486 | { 487 | "icon": { 488 | "paths": [ 489 | "M487.997 1012.009c-2.134 0-4.31-0.213-6.443-0.683-13.954-2.859-24.28-14.594-25.433-28.761l-29.828-372.74-388.742-29.913c-14.167-1.109-25.945-11.436-28.846-25.347-2.944-13.911 3.67-28.121 16.258-34.778l943.99-503.999c12.375-6.7 27.651-4.395 37.637 5.547s12.29 25.219 5.675 37.637l-495.977 936.010c-5.633 10.625-16.599 17.026-28.292 17.026zM151.955 524.524l306.556 23.555c15.703 1.237 28.206 13.655 29.444 29.358l23.214 290.596 395.527-746.461-754.74 402.952z" 490 | ], 491 | "attrs": [], 492 | "isMulticolor": false, 493 | "grid": 0, 494 | "tags": [ 495 | "follow" 496 | ] 497 | }, 498 | "attrs": [], 499 | "properties": { 500 | "order": 13, 501 | "id": 8, 502 | "prevSize": 32, 503 | "code": 59656, 504 | "name": "follow" 505 | }, 506 | "setIdx": 0, 507 | "setId": 1, 508 | "iconIdx": 12 509 | }, 510 | { 511 | "icon": { 512 | "paths": [ 513 | "M512.021 0c-282.318 0-512.021 229.703-512.021 512.021s229.703 512.021 512.021 512.021 512.021-229.703 512.021-512.021-229.746-512.021-512.021-512.021zM512.021 959.992c-247.071 0-448.013-200.942-448.013-447.971s200.942-448.013 448.013-448.013 448.013 200.942 448.013 448.013-200.985 447.971-448.013 447.971z", 514 | "M694.615 329.385c-12.503-12.503-32.772-12.503-45.275 0l-137.361 137.361-137.361-137.361c-12.503-12.503-32.772-12.503-45.275 0s-12.503 32.772 0 45.275l137.361 137.361-137.361 137.361c-12.503 12.503-12.503 32.772 0 45.275 6.23 6.23 14.423 9.388 22.616 9.388s16.386-3.115 22.616-9.388l137.361-137.361 137.361 137.361c6.23 6.23 14.423 9.388 22.616 9.388s16.386-3.115 22.616-9.388c12.503-12.503 12.503-32.772 0-45.275l-137.361-137.361 137.361-137.361c12.588-12.546 12.588-32.772 0.085-45.275z" 515 | ], 516 | "attrs": [], 517 | "isMulticolor": false, 518 | "grid": 0, 519 | "tags": [ 520 | "cancel" 521 | ] 522 | }, 523 | "attrs": [], 524 | "properties": { 525 | "order": 12, 526 | "id": 7, 527 | "prevSize": 32, 528 | "code": 59655, 529 | "name": "cancel" 530 | }, 531 | "setIdx": 0, 532 | "setId": 1, 533 | "iconIdx": 13 534 | }, 535 | { 536 | "icon": { 537 | "paths": [ 538 | "M508.864-0.085c-231.197 0-413.065 184.13-413.065 410.462 0 102.583 100.663 249.93 165.909 323.411v0.085l3.371 11.607-3.072 0c63.283 73.993 183.148 244.852 185.581 248.138 15.618 19.373 37.253 30.425 60.722 30.468 23.427 0 45.403-11.095 61.448-30.809 95.969-122.511 118.714-158.014 133.819-181.484 11.265-17.41 16.855-26.158 51.59-66.142 17.709-20.355 173.035-202.735 173.035-335.231 0-226.375-188.141-410.505-419.338-410.505zM705.368 704.216c-37.765 43.44-44.635 54.065-57.138 73.439-13.655 21.165-36.485 56.498-130.32 176.321-3.243 4.011-6.956 6.23-10.412 6.23s-7.084-2.176-9.089-4.566c-5.078-7.126-124.432-175.041-189.549-251.21-59.57-69.683-154.089-211.226-154.089-294.053 0-191.128 158.825-346.582 354.050-346.582s354.007 155.497 354.007 346.582c0.043 77.492-81.589 206.618-157.46 293.839z", 539 | "M508.864 188.781c-107.448 0-194.883 85.557-194.883 190.701s87.435 190.658 194.883 190.658c107.405 0 194.755-85.515 194.755-190.658s-87.35-190.701-194.755-190.701zM508.864 506.261c-71.476 0-129.638-56.882-129.638-126.779 0-69.939 58.162-126.821 129.638-126.821 71.39 0 129.51 56.882 129.51 126.821 0 69.897-58.077 126.779-129.51 126.779z" 540 | ], 541 | "attrs": [], 542 | "isMulticolor": false, 543 | "grid": 0, 544 | "tags": [ 545 | "location" 546 | ] 547 | }, 548 | "attrs": [], 549 | "properties": { 550 | "order": 11, 551 | "id": 6, 552 | "prevSize": 32, 553 | "code": 59649, 554 | "name": "location" 555 | }, 556 | "setIdx": 0, 557 | "setId": 1, 558 | "iconIdx": 14 559 | }, 560 | { 561 | "icon": { 562 | "paths": [ 563 | "M512.021 0c-282.318 0-512.021 229.703-512.021 512.021s229.703 512.021 512.021 512.021 512.021-229.703 512.021-512.021-229.746-512.021-512.021-512.021zM512.021 959.992c-247.071 0-448.013-200.942-448.013-447.971s200.942-448.013 448.013-448.013 448.013 200.942 448.013 448.013-200.985 447.971-448.013 447.971z", 564 | "M598.603 297.381c-12.46-12.503-32.729-12.503-45.232 0s-12.503 32.772 0 45.275l137.361 137.361h-434.7c-17.709 0-32.004 14.338-32.004 32.004s14.295 32.004 32.004 32.004h434.742l-137.361 137.361c-12.503 12.503-12.503 32.772 0 45.275 6.23 6.23 14.423 9.388 22.616 9.388s16.343-3.115 22.616-9.388l192.024-192.024c12.503-12.503 12.503-32.772 0-45.275l-192.067-191.981z" 565 | ], 566 | "attrs": [], 567 | "isMulticolor": false, 568 | "grid": 0, 569 | "tags": [ 570 | "next" 571 | ] 572 | }, 573 | "attrs": [], 574 | "properties": { 575 | "order": 6, 576 | "id": 5, 577 | "prevSize": 32, 578 | "code": 59650, 579 | "name": "next" 580 | }, 581 | "setIdx": 0, 582 | "setId": 1, 583 | "iconIdx": 15 584 | }, 585 | { 586 | "icon": { 587 | "paths": [ 588 | "M512.021 0c-282.318 0-512.021 229.703-512.021 512.021s229.703 512.021 512.021 512.021 512.021-229.703 512.021-512.021-229.746-512.021-512.021-512.021zM512.021 959.992c-247.071 0-448.013-200.942-448.013-447.971s200.942-448.013 448.013-448.013 448.013 200.942 448.013 448.013-200.985 447.971-448.013 447.971z", 589 | "M768.011 480.017h-434.742l137.361-137.361c12.503-12.503 12.503-32.772 0-45.275s-32.772-12.503-45.275 0l-191.981 192.024c-12.503 12.503-12.503 32.772 0 45.275l191.981 192.024c6.273 6.23 14.423 9.388 22.616 9.388s16.386-3.115 22.616-9.388c12.503-12.503 12.503-32.772 0-45.275l-137.318-137.404h434.742c17.709 0 32.004-14.338 32.004-32.004s-14.338-32.004-32.004-32.004z" 590 | ], 591 | "attrs": [], 592 | "isMulticolor": false, 593 | "grid": 0, 594 | "tags": [ 595 | "previous" 596 | ] 597 | }, 598 | "attrs": [], 599 | "properties": { 600 | "order": 7, 601 | "id": 4, 602 | "prevSize": 32, 603 | "code": 59651, 604 | "name": "previous" 605 | }, 606 | "setIdx": 0, 607 | "setId": 1, 608 | "iconIdx": 16 609 | }, 610 | { 611 | "icon": { 612 | "paths": [ 613 | "M1014.612 969.38l-271.010-238.963c74.377-77.663 120.378-182.679 120.378-298.405 0.043-238.195-193.774-432.011-431.969-432.011s-432.011 193.816-432.011 432.011c0 238.238 193.816 432.011 432.011 432.011 99.468 0 190.872-34.095 263.969-90.763l273.4 241.396c6.23 6.23 14.423 9.388 22.616 9.388s16.386-3.115 22.616-9.388c12.503-12.546 12.503-32.772-0-45.275zM64.008 432.011c0-202.905 165.055-368.003 368.003-368.003s368.003 165.098 368.003 368.003c0 202.948-165.098 368.003-368.003 368.003s-368.003-165.098-368.003-368.003z" 614 | ], 615 | "attrs": [], 616 | "isMulticolor": false, 617 | "grid": 0, 618 | "tags": [ 619 | "search" 620 | ] 621 | }, 622 | "attrs": [], 623 | "properties": { 624 | "order": 8, 625 | "id": 3, 626 | "prevSize": 32, 627 | "code": 59652, 628 | "name": "search" 629 | }, 630 | "setIdx": 0, 631 | "setId": 1, 632 | "iconIdx": 17 633 | }, 634 | { 635 | "icon": { 636 | "paths": [ 637 | "M319.997 832.019c17.709 0 32.004-14.295 32.004-32.004v-352.001c0-17.666-14.295-32.004-32.004-32.004s-32.004 14.295-32.004 32.004v352.001c0 17.666 14.295 32.004 32.004 32.004z", 638 | "M512.021 832.019c17.709 0 32.004-14.295 32.004-32.004v-352.001c0-17.666-14.295-32.004-32.004-32.004s-32.004 14.295-32.004 32.004v352.001c0 17.666 14.295 32.004 32.004 32.004z", 639 | "M704.003 832.019c17.709 0 32.004-14.295 32.004-32.004v-352.001c0-17.666-14.295-32.004-32.004-32.004s-32.004 14.295-32.004 32.004v352.001c0 17.666 14.295 32.004 32.004 32.004z", 640 | "M896.027 288.036h64.008v-32.004h32.004v-32.004c0-70.579-57.394-128.016-128.016-128.016h-132.539c-14.338-55.090-64.008-96.012-123.493-96.012h-191.981c-59.485 0-109.155 40.922-123.493 96.012h-132.497c-70.579 0-128.016 57.437-128.016 128.016v32.004h32.004v32.004h64.008v607.991c0 70.537 57.394 128.016 128.016 128.016h511.979c70.622 0 128.016-57.437 128.016-128.016v-607.991zM416.009 64.008h191.981c23.555 0 43.995 12.972 55.090 32.004h-302.16c11.095-19.032 31.535-32.004 55.090-32.004zM832.019 896.027c0 35.332-28.718 64.008-64.008 64.008h-511.979c-35.29 0-68.446-28.676-68.446-64.008l4.438-607.991h639.995v607.991zM864.023 160.020c35.29 0 64.008 28.676 64.008 64.008h-832.019c0-35.332 28.718-64.008 64.008-64.008h704.003z" 641 | ], 642 | "attrs": [], 643 | "isMulticolor": false, 644 | "grid": 0, 645 | "tags": [ 646 | "trash" 647 | ] 648 | }, 649 | "attrs": [], 650 | "properties": { 651 | "order": 9, 652 | "id": 2, 653 | "prevSize": 32, 654 | "code": 59653, 655 | "name": "trash" 656 | }, 657 | "setIdx": 0, 658 | "setId": 1, 659 | "iconIdx": 18 660 | }, 661 | { 662 | "icon": { 663 | "paths": [ 664 | "M512.021 0c-282.318 0-512.021 229.703-512.021 512.021s229.703 512.021 512.021 512.021 512.021-229.703 512.021-512.021-229.746-512.021-512.021-512.021zM512.021 959.992c-247.071 0-448.013-200.942-448.013-447.971s200.942-448.013 448.013-448.013 448.013 200.942 448.013 448.013-200.985 447.971-448.013 447.971z", 665 | "M704.003 480.017h-384.005c-17.709 0-32.004 14.338-32.004 32.004s14.295 32.004 32.004 32.004h384.005c17.709 0 32.004-14.338 32.004-32.004s-14.295-32.004-32.004-32.004z" 666 | ], 667 | "attrs": [], 668 | "isMulticolor": false, 669 | "grid": 0, 670 | "tags": [ 671 | "delete" 672 | ] 673 | }, 674 | "attrs": [], 675 | "properties": { 676 | "order": 10, 677 | "id": 1, 678 | "prevSize": 32, 679 | "code": 59654, 680 | "name": "delete" 681 | }, 682 | "setIdx": 0, 683 | "setId": 1, 684 | "iconIdx": 19 685 | }, 686 | { 687 | "icon": { 688 | "paths": [ 689 | "M513.024 0c-282.88 0-513.024 229.696-513.024 512 0 282.272 230.144 512 513.024 512s513.024-229.728 513.024-512c-0.032-282.304-230.176-512-513.024-512zM513.024 959.968c-247.52 0-448.896-200.896-448.896-447.968 0-247.040 201.344-448 448.864-448s448.928 200.96 448.928 448c0 247.072-201.408 447.968-448.896 447.968z", 690 | "M705.408 478.464h-160.352v-159.488c0-17.6-14.336-31.872-32.032-31.872s-32.064 14.24-32.064 31.872v159.488h-160.32c-17.728 0-32.064 14.304-32.064 31.904s14.336 31.936 32.064 31.936h160.32v159.456c0 17.6 14.336 31.904 32.064 31.904s32.032-14.304 32.032-31.904v-159.456h160.352c17.696 0 32.064-14.304 32.064-31.936s-14.368-31.904-32.064-31.904z" 691 | ], 692 | "attrs": [], 693 | "isMulticolor": false, 694 | "grid": 0, 695 | "tags": [ 696 | "plus" 697 | ] 698 | }, 699 | "attrs": [], 700 | "properties": { 701 | "order": 4, 702 | "id": 0, 703 | "prevSize": 32, 704 | "code": 59648, 705 | "name": "plus" 706 | }, 707 | "setIdx": 0, 708 | "setId": 1, 709 | "iconIdx": 20 710 | } 711 | ], 712 | "height": 1024, 713 | "metadata": { 714 | "name": "chatfonts" 715 | }, 716 | "preferences": { 717 | "showGlyphs": true, 718 | "showQuickUse": false, 719 | "showQuickUse2": false, 720 | "showSVGs": false, 721 | "fontPref": { 722 | "prefix": "icon-", 723 | "metadata": { 724 | "fontFamily": "chatfonts", 725 | "majorVersion": 1, 726 | "minorVersion": 0 727 | }, 728 | "metrics": { 729 | "emSize": 1024, 730 | "baseline": 6.25, 731 | "whitespace": 50 732 | }, 733 | "embed": false, 734 | "ie7": true, 735 | "noie8": false, 736 | "showSelector": false, 737 | "showMetrics": false, 738 | "showMetadata": false, 739 | "showVersion": false 740 | }, 741 | "imagePref": { 742 | "prefix": "icon-", 743 | "png": true, 744 | "useClassSelector": true, 745 | "color": 0, 746 | "bgColor": 16777215, 747 | "classSelector": ".icon" 748 | }, 749 | "historySize": 100, 750 | "showCodes": true, 751 | "gridSize": 16 752 | } 753 | } --------------------------------------------------------------------------------