├── chatroom.iml ├── pom.xml └── src └── main ├── java └── com │ └── howie │ ├── ApplicationMain.java │ ├── config │ └── NettyConfig.java │ ├── constant │ ├── MessageCodeConstant.java │ └── WebSocketConstant.java │ ├── handler │ ├── WebSocketChannelHandler.java │ └── WebSocketHandler.java │ ├── model │ ├── User.java │ └── WebSocketMessage.java │ ├── service │ ├── MessageService.java │ └── WebSocketInfoService.java │ └── util │ ├── DateFormatUtils.java │ ├── DateUtils.java │ └── WebSocketUtil.java └── resources ├── WEB-INF └── web.xml └── webapp ├── bootstrap ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap-theme.min.css.map │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── bootstrap.min.css.map │ ├── chat.css │ └── index.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── chat.js │ └── npm.js ├── html ├── chat.html └── index.html └── img ├── 1.png ├── 10.png ├── 11.png ├── 12.png ├── 13.png ├── 14.png ├── 15.png ├── 16.png ├── 17.png ├── 18.png ├── 19.png ├── 2.png ├── 20.png ├── 21.png ├── 22.png ├── 23.png ├── 24.png ├── 25.png ├── 26.png ├── 27.png ├── 28.png ├── 29.png ├── 3.png ├── 30.png ├── 31.png ├── 32.png ├── 33.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png ├── 9.png ├── chatroom.png └── redPoint.png /chatroom.iml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | jar 7 | com.howie 8 | chatroom 9 | 1.0-SNAPSHOT 10 | 11 | chatroom 12 | 13 | 14 | org.apache.maven.plugins 15 | maven-war-plugin 16 | 3.0.0 17 | 18 | false 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 25 | 26 | 1.8 27 | 28 | 1.8 29 | 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | io.netty 39 | netty-all 40 | 4.1.24.Final 41 | 42 | 43 | 44 | org.projectlombok 45 | lombok 46 | 1.16.10 47 | 48 | 49 | com.alibaba 50 | fastjson 51 | 1.2.8 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/com/howie/ApplicationMain.java: -------------------------------------------------------------------------------- 1 | package com.howie; 2 | 3 | import com.howie.constant.WebSocketConstant; 4 | import com.howie.handler.WebSocketChannelHandler; 5 | import com.howie.service.WebSocketInfoService; 6 | import io.netty.bootstrap.ServerBootstrap; 7 | import io.netty.channel.Channel; 8 | import io.netty.channel.EventLoopGroup; 9 | import io.netty.channel.nio.NioEventLoopGroup; 10 | import io.netty.channel.socket.nio.NioServerSocketChannel; 11 | 12 | import java.util.concurrent.Executors; 13 | import java.util.concurrent.ScheduledExecutorService; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | /** 17 | * Created with IntelliJ IDEA 18 | * 19 | * @Author yuanhaoyue swithaoy@gmail.com 20 | * @Description 应用启动类 21 | * @Date 2018-05-07 22 | * @Time 20:55 23 | */ 24 | public class ApplicationMain { 25 | public static void main(String[] args) { 26 | //两个线程组 27 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 28 | EventLoopGroup workGroup = new NioEventLoopGroup(); 29 | try { 30 | ServerBootstrap b = new ServerBootstrap(); 31 | b.group(bossGroup, workGroup); 32 | b.channel(NioServerSocketChannel.class); 33 | b.childHandler(new WebSocketChannelHandler()); 34 | System.out.println("服务端开启等待客户端连接...."); 35 | Channel ch = b.bind(WebSocketConstant.WEB_SOCKET_PORT).sync().channel(); 36 | 37 | //创建一个定长线程池,支持定时及周期性任务执行 38 | ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2); 39 | WebSocketInfoService webSocketInfoService = new WebSocketInfoService(); 40 | //定时任务:扫描所有的Channel,关闭失效的Channel 41 | executorService.scheduleAtFixedRate(webSocketInfoService::scanNotActiveChannel, 42 | 3, 60, TimeUnit.SECONDS); 43 | 44 | //定时任务:向所有客户端发送Ping消息 45 | executorService.scheduleAtFixedRate(webSocketInfoService::sendPing, 46 | 3, 50, TimeUnit.SECONDS); 47 | 48 | ch.closeFuture().sync(); 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | } finally { 52 | //退出程序 53 | bossGroup.shutdownGracefully(); 54 | workGroup.shutdownGracefully(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/howie/config/NettyConfig.java: -------------------------------------------------------------------------------- 1 | package com.howie.config; 2 | 3 | import io.netty.channel.group.ChannelGroup; 4 | import io.netty.channel.group.DefaultChannelGroup; 5 | import io.netty.util.concurrent.GlobalEventExecutor; 6 | 7 | /** 8 | * Created with IntelliJ IDEA 9 | * 10 | * @Author yuanhaoyue swithaoy@gmail.com 11 | * @Description netty 全局配置 12 | * @Date 2018-05-07 13 | * @Time 15:11 14 | */ 15 | public class NettyConfig { 16 | /** 17 | * 存储每个客户端接入进来时的 channel 对象 18 | * 主要用于使用 writeAndFlush 方法广播信息 19 | */ 20 | public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/howie/constant/MessageCodeConstant.java: -------------------------------------------------------------------------------- 1 | package com.howie.constant; 2 | 3 | /** 4 | * Created with IntelliJ IDEA 5 | * 6 | * @Author yuanhaoyue swithaoy@gmail.com 7 | * @Description Code 常量 8 | * @Date 2018-05-12 9 | * @Time 16:53 10 | */ 11 | public class MessageCodeConstant { 12 | /* 13 | CODE 14 | 客户端信息传送 code 15 | */ 16 | /** 17 | * 登陆 18 | */ 19 | public static final int LOGIN_CODE = 1000; 20 | /** 21 | * 群聊 22 | */ 23 | public static final int GROUP_CHAT_CODE = 1002; 24 | /** 25 | * 私聊 26 | */ 27 | public static final int PRIVATE_CHAT_CODE = 1003; 28 | /** 29 | * pong 信息 30 | */ 31 | public static final int PONG_CHAT_CODE = 1004; 32 | 33 | 34 | /* 35 | MESSAGE_CODE 36 | 服务端信息传送 code 37 | */ 38 | /** 39 | * 群聊信息 40 | */ 41 | public static final int GROUP_CHAT_MESSAGE_CODE = 2000; 42 | /** 43 | * 系统信息 44 | */ 45 | public static final int SYSTEM_MESSAGE_CODE = 2001; 46 | /** 47 | * 私聊信息 48 | */ 49 | public static final int PRIVATE_CHAT_MESSAGE_CODE = 2002; 50 | /** 51 | * ping 信息 52 | */ 53 | public static final int PING_MESSAGE_CODE = 2003; 54 | 55 | 56 | /* 57 | SYSTEM_MESSGAE_CODE 58 | */ 59 | /** 60 | * 普通系统信息:用户上线,下线广播通知等 61 | */ 62 | public static final int NORMAL_SYSTEM_MESSGAE_CODE = 3000; 63 | /** 64 | * 更新当前用户数量的系统信息 65 | */ 66 | public static final int UPDATE_USERCOUNT_SYSTEM_MESSGAE_CODE = 3001; 67 | /** 68 | * 更新当前用户列表的系统信息 69 | */ 70 | public static final int UPDATE_USERLIST_SYSTEM_MESSGAE_CODE = 3002; 71 | /** 72 | * 获取个人信息的系统信息 73 | */ 74 | public static final int PERSONAL_SYSTEM_MESSGAE_CODE = 3003; 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/howie/constant/WebSocketConstant.java: -------------------------------------------------------------------------------- 1 | package com.howie.constant; 2 | 3 | /** 4 | * Created with IntelliJ IDEA 5 | * 6 | * @Author yuanhaoyue swithaoy@gmail.com 7 | * @Description websocket 相关常量 8 | * @Date 2018-05-08 9 | * @Time 20:54 10 | */ 11 | public class WebSocketConstant { 12 | public static final String WEB_SOCKET_URL = "ws://localhost:8090/websocket"; 13 | 14 | public static final int WEB_SOCKET_PORT = 8090; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/howie/handler/WebSocketChannelHandler.java: -------------------------------------------------------------------------------- 1 | package com.howie.handler; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.socket.SocketChannel; 5 | import io.netty.handler.codec.http.HttpObjectAggregator; 6 | import io.netty.handler.codec.http.HttpServerCodec; 7 | import io.netty.handler.stream.ChunkedWriteHandler; 8 | 9 | /** 10 | * Created with IntelliJ IDEA 11 | * 12 | * @Author yuanhaoyue swithaoy@gmail.com 13 | * @Description 初始化连接时的各个组件 14 | * @Date 2018-05-07 15 | * @Time 20:51 16 | */ 17 | public class WebSocketChannelHandler extends ChannelInitializer { 18 | @Override 19 | protected void initChannel(SocketChannel socketChannel) throws Exception { 20 | //请求解码器 21 | socketChannel.pipeline().addLast("http-codec", new HttpServerCodec()); 22 | //将多个消息转换成单一的消息对象 23 | socketChannel.pipeline().addLast("aggregator", new HttpObjectAggregator(65536)); 24 | //支持异步发送大的码流,一般用于发送文件流 25 | socketChannel.pipeline().addLast("http-chunked", new ChunkedWriteHandler()); 26 | //处理 websocket 和处理消息的发送 27 | socketChannel.pipeline().addLast("handler", new WebSocketHandler()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/howie/handler/WebSocketHandler.java: -------------------------------------------------------------------------------- 1 | package com.howie.handler; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.howie.config.NettyConfig; 5 | import com.howie.constant.WebSocketConstant; 6 | import com.howie.model.User; 7 | import com.howie.service.MessageService; 8 | import com.howie.service.WebSocketInfoService; 9 | import io.netty.buffer.ByteBuf; 10 | import io.netty.buffer.Unpooled; 11 | import io.netty.channel.*; 12 | import io.netty.handler.codec.http.DefaultFullHttpResponse; 13 | import io.netty.handler.codec.http.FullHttpRequest; 14 | import io.netty.handler.codec.http.HttpResponseStatus; 15 | import io.netty.handler.codec.http.HttpVersion; 16 | import io.netty.handler.codec.http.websocketx.*; 17 | import io.netty.util.CharsetUtil; 18 | 19 | import java.util.Objects; 20 | import java.util.UUID; 21 | 22 | import static com.howie.constant.MessageCodeConstant.*; 23 | 24 | 25 | /** 26 | * Created with IntelliJ IDEA 27 | * 28 | * @Author yuanhaoyue swithaoy@gmail.com 29 | * @Description 接收请求,接收 WebSocket 信息的控制类 30 | * @Date 2018-05-07 31 | * @Time 15:41 32 | */ 33 | public class WebSocketHandler extends SimpleChannelInboundHandler { 34 | private WebSocketServerHandshaker handshaker; 35 | private WebSocketInfoService webSocketInfoService = new WebSocketInfoService(); 36 | private MessageService messageService = new MessageService(); 37 | 38 | /** 39 | * 客户端与服务端创建连接的时候调用 40 | */ 41 | @Override 42 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 43 | //创建新的 WebSocket 连接,保存当前 channel 44 | webSocketInfoService.addChannel(ctx.channel()); 45 | System.out.println("————客户端与服务端连接开启————"); 46 | } 47 | 48 | /** 49 | * 客户端与服务端断开连接的时候调用 50 | */ 51 | @Override 52 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 53 | System.out.println("————客户端与服务端连接断开————"); 54 | } 55 | 56 | /** 57 | * 服务端接收客户端发送过来的数据结束之后调用 58 | */ 59 | @Override 60 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 61 | ctx.flush(); 62 | } 63 | 64 | /** 65 | * 工程出现异常的时候调用 66 | */ 67 | @Override 68 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 69 | cause.printStackTrace(); 70 | ctx.close(); 71 | } 72 | 73 | /** 74 | * 服务端处理客户端websocket请求的核心方法 75 | */ 76 | @Override 77 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception { 78 | if (o instanceof FullHttpRequest) { 79 | //处理客户端向服务端发起 http 请求的业务 80 | handHttpRequest(channelHandlerContext, (FullHttpRequest) o); 81 | } else if (o instanceof WebSocketFrame) { 82 | //处理客户端与服务端之间的 websocket 业务 83 | handWebsocketFrame(channelHandlerContext, (WebSocketFrame) o); 84 | } 85 | } 86 | 87 | /** 88 | * 处理客户端向服务端发起 http 握手请求的业务 89 | * WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。 90 | * 91 | * WebSocket 连接过程: 92 | * 首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等; 93 | * 然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据; 94 | * 最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信。 95 | */ 96 | private void handHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) { 97 | // 如果请求失败或者该请求不是客户端向服务端发起的 http 请求,则响应错误信息 98 | if (!request.decoderResult().isSuccess() 99 | || !("websocket".equals(request.headers().get("Upgrade")))) { 100 | // code :400 101 | sendHttpResponse(ctx, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); 102 | return; 103 | } 104 | // WebSocket 握手工厂类 105 | WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory( 106 | WebSocketConstant.WEB_SOCKET_URL, null, false); 107 | //新建一个握手 108 | handshaker = factory.newHandshaker(request); 109 | if (handshaker == null) { 110 | //如果为空,返回响应:不受支持的 websocket 版本 111 | WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); 112 | } else { 113 | //否则,执行握手 114 | handshaker.handshake(ctx.channel(), request); 115 | } 116 | } 117 | 118 | /** 119 | * 处理客户端与服务端之间的 websocket 业务 120 | */ 121 | private void handWebsocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { 122 | //判断是否是关闭 websocket 的指令 123 | if (frame instanceof CloseWebSocketFrame) { 124 | //关闭握手 125 | handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); 126 | webSocketInfoService.deleteChannel(ctx.channel()); 127 | return; 128 | } 129 | //判断是否是ping消息 130 | if (frame instanceof PingWebSocketFrame) { 131 | ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); 132 | return; 133 | } 134 | // 判断是否Pong消息 135 | if (frame instanceof PongWebSocketFrame) { 136 | ctx.writeAndFlush(new PongWebSocketFrame(frame.content().retain())); 137 | return; 138 | } 139 | //判断是否是二进制消息,如果是二进制消息,抛出异常 140 | if (!(frame instanceof TextWebSocketFrame)) { 141 | System.out.println("目前我们不支持二进制消息"); 142 | ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); 143 | throw new RuntimeException("【" + this.getClass().getName() + "】不支持消息"); 144 | } 145 | // 返回应答消息 146 | // 获取并解析客户端向服务端发送的 json 消息 147 | String message = ((TextWebSocketFrame) frame).text(); 148 | JSONObject json = JSONObject.parseObject(message); 149 | int code = json.getInteger("code"); 150 | String nick = json.getString("nick"); 151 | String chatMessage = json.getString("chatMessage"); 152 | //从 webSocketInfoMap 拿出属于当前 channel 的 User 信息 153 | User user = WebSocketInfoService.webSocketInfoMap.get(ctx.channel()); 154 | TextWebSocketFrame tws; 155 | switch (code) { 156 | //用户登陆 157 | case LOGIN_CODE: 158 | //生成 UUID 作为用户 id 159 | String id = UUID.randomUUID().toString(); 160 | if (!webSocketInfoService.addUser(ctx.channel(), nick, id)) { 161 | return; 162 | } 163 | webSocketInfoService.updateUserListAndCount(); 164 | //向每个连接上来的客户端群发新用户登陆信息 165 | tws = new TextWebSocketFrame(messageService.messageJSONStringFactory(SYSTEM_MESSAGE_CODE, 166 | "欢迎" + nick + "来到聊天室~", NORMAL_SYSTEM_MESSGAE_CODE, null)); 167 | NettyConfig.channelGroup.writeAndFlush(tws); 168 | //再向当前登陆的客户端发送该用户的用户信息,用于前端使用 169 | tws = new TextWebSocketFrame(messageService.messageJSONStringFactory(SYSTEM_MESSAGE_CODE, null, 170 | PERSONAL_SYSTEM_MESSGAE_CODE, user)); 171 | ctx.channel().writeAndFlush(tws); 172 | break; 173 | //群聊 174 | case GROUP_CHAT_CODE: 175 | //向每个连接上来的客户端群发群聊消息 176 | tws = new TextWebSocketFrame(messageService.messageJSONStringFactory(GROUP_CHAT_MESSAGE_CODE, chatMessage, 177 | user, null)); 178 | NettyConfig.channelGroup.writeAndFlush(tws); 179 | break; 180 | //私聊 181 | case PRIVATE_CHAT_CODE: 182 | Channel myChannel = ctx.channel(); 183 | //接收人id 184 | String receiverId = json.getString("id"); 185 | //发送人id 186 | String senderId = user.getId(); 187 | /* 188 | 向目标用户发送私聊信息,发送人 id 为 senderId 189 | */ 190 | tws = new TextWebSocketFrame(messageService.messageJSONStringFactory(PRIVATE_CHAT_MESSAGE_CODE, chatMessage, 191 | user, senderId)); 192 | webSocketInfoService.sendPrivateChatMessage(receiverId, tws); 193 | /* 194 | 如果当前信息是自己发给自己,那么信息只需要发给自己就好 195 | 但是如果是发给他人,则信息既要发给对方,也要发给自己(两边都要显示) 196 | */ 197 | if (!Objects.equals(receiverId, senderId)) { 198 | //发给自己,发送人 id 为 receiverId 199 | tws = new TextWebSocketFrame(messageService.messageJSONStringFactory(PRIVATE_CHAT_MESSAGE_CODE, chatMessage, 200 | user, receiverId)); 201 | myChannel.writeAndFlush(tws); 202 | } 203 | break; 204 | //pong 205 | case PONG_CHAT_CODE: 206 | Channel channel = ctx.channel(); 207 | webSocketInfoService.resetUserTime(channel); 208 | default: 209 | } 210 | } 211 | 212 | /** 213 | * 服务端向客户端响应消息 214 | */ 215 | private void sendHttpResponse(ChannelHandlerContext ctx, DefaultFullHttpResponse response) { 216 | if (response.status().code() != 200) { 217 | //创建源缓冲区 218 | ByteBuf byteBuf = Unpooled.copiedBuffer(response.status().toString(), CharsetUtil.UTF_8); 219 | //将源缓冲区的数据传送到此缓冲区 220 | response.content().writeBytes(byteBuf); 221 | //释放源缓冲区 222 | byteBuf.release(); 223 | } 224 | //写入请求,服务端向客户端发送数据 225 | ChannelFuture channelFuture = ctx.channel().writeAndFlush(response); 226 | if (response.status().code() != 200) { 227 | /* 228 | 如果请求失败,关闭 ChannelFuture 229 | 230 | ChannelFutureListener.CLOSE 源码:future.channel().close(); 231 | */ 232 | channelFuture.addListener(ChannelFutureListener.CLOSE); 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/main/java/com/howie/model/User.java: -------------------------------------------------------------------------------- 1 | package com.howie.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Created with IntelliJ IDEA 9 | * 10 | * @Author yuanhaoyue swithaoy@gmail.com 11 | * @Description 用户实体类 12 | * @Date 2018-05-11 13 | * @Time 21:01 14 | */ 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Data 18 | public class User { 19 | private String id; 20 | private String nick; 21 | private String address; 22 | private String avatarAddress; 23 | /** 24 | * 确认在线时间 25 | */ 26 | private long time = 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/howie/model/WebSocketMessage.java: -------------------------------------------------------------------------------- 1 | package com.howie.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * Created with IntelliJ IDEA 12 | * 13 | * @Author yuanhaoyue swithaoy@gmail.com 14 | * @Description 15 | * @Date 2018-05-12 16 | * @Time 18:50 17 | */ 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | @Data 21 | public class WebSocketMessage { 22 | private Integer code; 23 | /** 24 | * 发送人信息 25 | */ 26 | private User user; 27 | /** 28 | * 接收人 id 29 | */ 30 | private String receiverId; 31 | private String time; 32 | private String message; 33 | /** 34 | * 可以存放在线人数,在线用户列表,code等 35 | */ 36 | private Map body = new HashMap<>(); 37 | 38 | public WebSocketMessage(Integer code, String message, String time) { 39 | this.code = code; 40 | this.time = time; 41 | this.message = message; 42 | } 43 | 44 | public WebSocketMessage(Integer code, String message, User user, 45 | String receiverId, String time) { 46 | this.code = code; 47 | this.user = user; 48 | this.receiverId = receiverId; 49 | this.time = time; 50 | this.message = message; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/howie/service/MessageService.java: -------------------------------------------------------------------------------- 1 | package com.howie.service; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.howie.model.User; 5 | import com.howie.model.WebSocketMessage; 6 | import com.howie.util.DateUtils; 7 | 8 | import java.util.Date; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | import static com.howie.constant.MessageCodeConstant.*; 13 | 14 | /** 15 | * Created with IntelliJ IDEA 16 | * 17 | * @Author yuanhaoyue swithaoy@gmail.com 18 | * @Description 19 | * @Date 2018-05-12 20 | * @Time 20:57 21 | */ 22 | public class MessageService { 23 | public String messageJSONStringFactory(int messageCode, String chatMessage, int systemMessageCode, Object o) { 24 | WebSocketMessage webSocketMessage = new WebSocketMessage(messageCode, chatMessage, 25 | DateUtils.date2String(new Date())); 26 | Map map = new HashMap<>(); 27 | map.put("systemMessageCode", systemMessageCode); 28 | map.put("object", o); 29 | webSocketMessage.setBody(map); 30 | return JSONObject.toJSONString(webSocketMessage); 31 | } 32 | 33 | public String messageJSONStringFactory(int messageCode, String chatMessage, User user, String receiverId) { 34 | WebSocketMessage webSocketMessage = new WebSocketMessage(messageCode, chatMessage, user 35 | , receiverId, DateUtils.date2String(new Date())); 36 | return JSONObject.toJSONString(webSocketMessage); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/howie/service/WebSocketInfoService.java: -------------------------------------------------------------------------------- 1 | package com.howie.service; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.howie.config.NettyConfig; 5 | import com.howie.model.User; 6 | import com.howie.model.WebSocketMessage; 7 | import com.howie.util.WebSocketUtil; 8 | import io.netty.channel.Channel; 9 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 10 | 11 | import java.util.*; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | import java.util.concurrent.ConcurrentMap; 14 | import java.util.concurrent.atomic.AtomicInteger; 15 | 16 | import static com.howie.constant.MessageCodeConstant.*; 17 | 18 | /** 19 | * Created with IntelliJ IDEA 20 | * 21 | * @Author yuanhaoyue swithaoy@gmail.com 22 | * @Description 23 | * @Date 2018-05-11 24 | * @Time 20:56 25 | */ 26 | public class WebSocketInfoService { 27 | /** 28 | * 存储 Channel 与用户信息 29 | */ 30 | public static ConcurrentMap webSocketInfoMap = new ConcurrentHashMap<>(); 31 | /** 32 | * 用户在线数量 33 | */ 34 | private static AtomicInteger userCount = new AtomicInteger(0); 35 | 36 | /** 37 | * 新的客户端与服务端进行连接,先保存新的 channel, 38 | * 当连接建立后,客户端会发送用户登陆请求(LOGIN_CODE),这时再将用户信息保存进去 39 | */ 40 | public void addChannel(Channel channel) { 41 | User user = new User(); 42 | user.setAddress(WebSocketUtil.getChannelAddress(channel)); 43 | webSocketInfoMap.put(channel, user); 44 | NettyConfig.channelGroup.add(channel); 45 | } 46 | 47 | /** 48 | * 有用户退出,需要删除该用户的信息,并移除该用户的 channel 49 | */ 50 | public void deleteChannel(Channel channel) { 51 | String nick = webSocketInfoMap.get(channel).getNick(); 52 | webSocketInfoMap.remove(channel); 53 | userCount.decrementAndGet(); 54 | NettyConfig.channelGroup.remove(channel); 55 | //广播用户离开的信息 56 | TextWebSocketFrame tws = new TextWebSocketFrame(new MessageService().messageJSONStringFactory(SYSTEM_MESSAGE_CODE, 57 | nick + "离开了聊天室~", NORMAL_SYSTEM_MESSGAE_CODE, null)); 58 | new WebSocketInfoService().updateUserListAndCount(); 59 | NettyConfig.channelGroup.writeAndFlush(tws); 60 | } 61 | 62 | /** 63 | * 向服务端发送信息,携带新的在线人数/携带新的用户列表 64 | */ 65 | public void updateUserListAndCount() { 66 | //更新在线人数 67 | TextWebSocketFrame tws = new TextWebSocketFrame(new MessageService().messageJSONStringFactory(SYSTEM_MESSAGE_CODE, 68 | null, UPDATE_USERCOUNT_SYSTEM_MESSGAE_CODE, userCount)); 69 | NettyConfig.channelGroup.writeAndFlush(tws); 70 | 71 | //更新在线用户列表 72 | List userList = new ArrayList<>(); 73 | Set set = webSocketInfoMap.keySet(); 74 | for (Channel channel : set) { 75 | User user = webSocketInfoMap.get(channel); 76 | userList.add(user); 77 | } 78 | tws = new TextWebSocketFrame(new MessageService().messageJSONStringFactory(SYSTEM_MESSAGE_CODE, 79 | null, UPDATE_USERLIST_SYSTEM_MESSGAE_CODE, userList)); 80 | NettyConfig.channelGroup.writeAndFlush(tws); 81 | } 82 | 83 | /** 84 | * 将 nick,id,avatarAddress 等用户信息保存到对应的 channel 的 value 中 85 | * 86 | * @param channel 属于某用户的 channel 87 | * @param nick 昵称 88 | * @param id 用户 id 89 | * @return 如果当前用户不存在,则返回 false 90 | */ 91 | public boolean addUser(Channel channel, String nick, String id) { 92 | User user = webSocketInfoMap.get(channel); 93 | if (user == null) { 94 | return false; 95 | } 96 | user.setId(id); 97 | user.setNick(nick); 98 | user.setAvatarAddress(getRandomAvatar()); 99 | user.setTime(System.currentTimeMillis()); 100 | //用户在线数量 + 1 101 | userCount.incrementAndGet(); 102 | return true; 103 | } 104 | 105 | /** 106 | * 返回一个随机的头像地址 107 | */ 108 | private String getRandomAvatar() { 109 | int num = new Random().nextInt(33) + 1; 110 | return "../img/" + num + ".png"; 111 | } 112 | 113 | /** 114 | * 发送私聊信息 115 | * 116 | * @param id 收信人id 117 | * @param tws 118 | */ 119 | public void sendPrivateChatMessage(String id, TextWebSocketFrame tws) { 120 | Set set = webSocketInfoMap.keySet(); 121 | Channel receiverChannel = null; 122 | for (Channel channel : set) { 123 | User user = webSocketInfoMap.get(channel); 124 | if (user.getId().equals(id)) { 125 | receiverChannel = channel; 126 | break; 127 | } 128 | } 129 | if (receiverChannel != null) { 130 | receiverChannel.writeAndFlush(tws); 131 | } 132 | } 133 | 134 | /** 135 | * 广播 ping 信息 136 | */ 137 | public void sendPing() { 138 | Set keySet = webSocketInfoMap.keySet(); 139 | for (Channel channel : keySet) { 140 | User user = webSocketInfoMap.get(channel); 141 | if (user == null) { 142 | continue; 143 | } 144 | WebSocketMessage webSocketMessage = new WebSocketMessage(); 145 | webSocketMessage.setCode(PING_MESSAGE_CODE); 146 | String message = JSONObject.toJSONString(webSocketMessage); 147 | TextWebSocketFrame tws = new TextWebSocketFrame(message); 148 | NettyConfig.channelGroup.writeAndFlush(tws); 149 | } 150 | } 151 | 152 | /** 153 | * 从缓存中移除Channel,并且关闭Channel 154 | */ 155 | public void scanNotActiveChannel() { 156 | Set keySet = webSocketInfoMap.keySet(); 157 | for (Channel channel : keySet) { 158 | User user = webSocketInfoMap.get(channel); 159 | if (user == null) { 160 | continue; 161 | } 162 | if (!channel.isOpen() || !channel.isActive() && 163 | (System.currentTimeMillis() - user.getTime()) > 10000) { 164 | deleteChannel(channel); 165 | } 166 | } 167 | } 168 | 169 | /** 170 | * 重设验证在线时间 171 | */ 172 | public void resetUserTime(Channel channel) { 173 | User user = webSocketInfoMap.get(channel); 174 | if (user != null) { 175 | user.setTime(System.currentTimeMillis()); 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/com/howie/util/DateFormatUtils.java: -------------------------------------------------------------------------------- 1 | package com.howie.util; 2 | 3 | import java.text.ParsePosition; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | 7 | /** 8 | * Created with IntelliJ IDEA 9 | * 10 | * @Author yuanhaoyue swithaoy@gmail.com 11 | * @Description 日期格式转换工具类 12 | * @Date 2018-05-09 13 | * @Time 21:35 14 | */ 15 | public class DateFormatUtils { 16 | /** yyyy:年 */ 17 | public static final String DATE_YEAR = "yyyy"; 18 | 19 | /** MM:月 */ 20 | public static final String DATE_MONTH = "MM"; 21 | 22 | /** DD:日 */ 23 | public static final String DATE_DAY = "dd"; 24 | 25 | /** HH:时 */ 26 | public static final String DATE_HOUR = "HH"; 27 | 28 | /** mm:分 */ 29 | public static final String DATE_MINUTE = "mm"; 30 | 31 | /** ss:秒 */ 32 | public static final String DATE_SECONDES = "ss"; 33 | 34 | /** yyyy-MM-dd */ 35 | public static final String DATE_FORMAT1 = "yyyy-MM-dd"; 36 | 37 | /** yyyy-MM-dd hh:mm:ss */ 38 | public static final String DATE_FORMAT2 = "yyyy-MM-dd HH:mm:ss"; 39 | 40 | /** yyyy-MM-dd hh:mm:ss|SSS */ 41 | public static final String TIME_FORMAT_SSS = "yyyy-MM-dd HH:mm:ss|SSS"; 42 | 43 | /** yyyyMMdd */ 44 | public static final String DATE_NOFUll_FORMAT = "yyyyMMdd"; 45 | 46 | /** yyyyMMddhhmmss */ 47 | public static final String TIME_NOFUll_FORMAT = "yyyyMMddHHmmss"; 48 | 49 | /** 50 | * 51 | * 格式转换
52 | * yyyy-MM-dd hh:mm:ss 和 yyyyMMddhhmmss 相互转换
53 | * yyyy-mm-dd 和yyyymmss 相互转换 54 | * @author chenssy 55 | * @date Dec 26, 2013 56 | * @param value 57 | * 日期 58 | * @return String 59 | */ 60 | public static String formatString(String value) { 61 | String sReturn = ""; 62 | if (value == null || "".equals(value)) { 63 | return sReturn; 64 | } 65 | if (value.length() == 14) { //长度为14格式转换成yyyy-mm-dd hh:mm:ss 66 | sReturn = value.substring(0, 4) + "-" + value.substring(4, 6) + "-" + value.substring(6, 8) + " " 67 | + value.substring(8, 10) + ":" + value.substring(10, 12) + ":" + value.substring(12, 14); 68 | return sReturn; 69 | } 70 | if (value.length() == 19) { //长度为19格式转换成yyyymmddhhmmss 71 | sReturn = value.substring(0, 4) + value.substring(5, 7) + value.substring(8, 10) + value.substring(11, 13) 72 | + value.substring(14, 16) + value.substring(17, 19); 73 | return sReturn; 74 | } 75 | if(value.length() == 10){ //长度为10格式转换成yyyymmhh 76 | sReturn = value.substring(0, 4) + value.substring(5,7) + value.substring(8,10); 77 | } 78 | if(value.length() == 8){ //长度为8格式转化成yyyy-mm-dd 79 | sReturn = value.substring(0, 4) + "-" + value.substring(4, 6) + "-" + value.substring(6, 8); 80 | } 81 | return sReturn; 82 | } 83 | 84 | public static String formatDate(String date, String format) { 85 | if (date == null || "".equals(date)){ 86 | return ""; 87 | } 88 | Date dt = null; 89 | SimpleDateFormat inFmt = null; 90 | SimpleDateFormat outFmt = null; 91 | ParsePosition pos = new ParsePosition(0); 92 | date = date.replace("-", "").replace(":", ""); 93 | if ((date == null) || ("".equals(date.trim()))) { 94 | return ""; 95 | } 96 | try { 97 | if (Long.parseLong(date) == 0L) { 98 | return ""; 99 | } 100 | } catch (Exception nume) { 101 | return date; 102 | } 103 | try { 104 | switch (date.trim().length()) { 105 | case 14: 106 | inFmt = new SimpleDateFormat("yyyyMMddHHmmss"); 107 | break; 108 | case 12: 109 | inFmt = new SimpleDateFormat("yyyyMMddHHmm"); 110 | break; 111 | case 10: 112 | inFmt = new SimpleDateFormat("yyyyMMddHH"); 113 | break; 114 | case 8: 115 | inFmt = new SimpleDateFormat("yyyyMMdd"); 116 | break; 117 | case 6: 118 | inFmt = new SimpleDateFormat("yyyyMM"); 119 | break; 120 | case 7: 121 | case 9: 122 | case 11: 123 | case 13: 124 | default: 125 | return date; 126 | } 127 | if ((dt = inFmt.parse(date, pos)) == null) { 128 | return date; 129 | } 130 | if ((format == null) || ("".equals(format.trim()))) { 131 | outFmt = new SimpleDateFormat("yyyy年MM月dd日"); 132 | } else { 133 | outFmt = new SimpleDateFormat(format); 134 | } 135 | return outFmt.format(dt); 136 | } catch (Exception ex) { 137 | } 138 | return date; 139 | } 140 | 141 | /** 142 | * 格式化日期 143 | * 144 | * @author chenming 145 | * @date 2016年5月31日 146 | * 147 | * @param date 148 | * @param format 149 | * @return 150 | */ 151 | public static String formatDate(Date date,String format){ 152 | return formatDate(DateUtils.date2String(date), format); 153 | } 154 | 155 | /** 156 | * @desc:格式化是时间,采用默认格式(yyyy-MM-dd HH:mm:ss) 157 | * @autor:chenssy 158 | * @date:2014年8月6日 159 | * 160 | * @param value 161 | * @return 162 | */ 163 | public static String formatDate(String value){ 164 | return getFormat(DATE_FORMAT2).format(DateUtils.string2Date(value, DATE_FORMAT2)); 165 | } 166 | 167 | /** 168 | * 格式化日期 169 | * 170 | * @author : chenssy 171 | * @date : 2016年5月31日 下午5:40:58 172 | * 173 | * @param value 174 | * @return 175 | */ 176 | public static String formatDate(Date value){ 177 | return formatDate(DateUtils.date2String(value)); 178 | } 179 | 180 | /** 181 | * 获取日期显示格式,为空默认为yyyy-mm-dd HH:mm:ss 182 | * @author chenssy 183 | * @date Dec 30, 2013 184 | * @param format 185 | * @return 186 | * @return SimpleDateFormat 187 | */ 188 | protected static SimpleDateFormat getFormat(String format){ 189 | if(format == null || "".equals(format)){ 190 | format = DATE_FORMAT2; 191 | } 192 | return new SimpleDateFormat(format); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/com/howie/util/DateUtils.java: -------------------------------------------------------------------------------- 1 | package com.howie.util; 2 | 3 | import java.text.DateFormat; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Calendar; 7 | import java.util.Date; 8 | 9 | /** 10 | * Created with IntelliJ IDEA 11 | * 12 | * @Author yuanhaoyue swithaoy@gmail.com 13 | * @Description 14 | * @Date 2018-05-09 15 | * @Time 21:34 16 | */ 17 | public class DateUtils { 18 | private static final String[] weeks = {"星期日","星期一","星期二","星期三","星期四","星期五","星期六"}; 19 | 20 | /** 21 | * 获得某日期的月份的总天数 22 | * @param dateString "2018-01-30" 23 | */ 24 | public static int getDaysOfMonth(String dateString){ 25 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 26 | Date date = null; 27 | try { 28 | date = sdf.parse(dateString); 29 | } catch (ParseException e) { 30 | e.printStackTrace(); 31 | } 32 | Calendar calendar = Calendar.getInstance(); 33 | calendar.setTime(date); 34 | return calendar.getActualMaximum(Calendar.DAY_OF_MONTH); 35 | } 36 | 37 | 38 | /** 39 | * 比较两个日期的大小。
40 | * 若date1 > date2 则返回 1
41 | * 若date1 = date2 则返回 0
42 | * 若date1 < date2 则返回-1 43 | * @autor:chenssy 44 | * @date:2014年9月9日 45 | * 46 | * @param date1 47 | * @param date2 48 | * @param format 待转换的格式 49 | * @return 比较结果 50 | */ 51 | public static int compare(String date1, String date2,String format) { 52 | DateFormat df = DateFormatUtils.getFormat(format); 53 | try { 54 | Date dt1 = df.parse(date1); 55 | Date dt2 = df.parse(date2); 56 | if (dt1.getTime() > dt2.getTime()) { 57 | return 1; 58 | } else if (dt1.getTime() < dt2.getTime()) { 59 | return -1; 60 | } else { 61 | return 0; 62 | } 63 | } catch (Exception exception) { 64 | exception.printStackTrace(); 65 | } 66 | return 0; 67 | } 68 | 69 | /** 70 | * 对 "yyyy-MM-dd" 格式的日期数组进行排序(插入排序) 71 | * @param a 72 | * @return 73 | */ 74 | public static String[] dateSort(String[] a) { 75 | String temp; 76 | int j; 77 | for (int i = 1; i < a.length; i++) { 78 | temp = a[i]; 79 | for (j = i; j > 0 && DateUtils.compare(temp, a[j - 1], "yyyy-MM-dd") == -1; j--) { 80 | a[j] = a[j - 1]; 81 | } 82 | a[j] = temp; 83 | } 84 | return a; 85 | } 86 | 87 | /** 88 | * 根据指定格式获取当前时间 89 | * @author chenssy 90 | * @date Dec 27, 2013 91 | * @param format 92 | * @return String 93 | */ 94 | public static String getCurrentTime(String format){ 95 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 96 | Date date = new Date(); 97 | return sdf.format(date); 98 | } 99 | 100 | /** 101 | * 获取当前时间,格式为:yyyy-MM-dd HH:mm:ss 102 | * @author chenssy 103 | * @date Dec 27, 2013 104 | * @return String 105 | */ 106 | public static String getCurrentTime(){ 107 | return getCurrentTime(DateFormatUtils.DATE_FORMAT2); 108 | } 109 | 110 | /** 111 | * 获取指定格式的当前时间:为空时格式为yyyy-mm-dd HH:mm:ss 112 | * @author chenssy 113 | * @date Dec 30, 2013 114 | * @param format 115 | * @return Date 116 | */ 117 | public static Date getCurrentDate(String format){ 118 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 119 | String dateS = getCurrentTime(format); 120 | Date date = null; 121 | try { 122 | date = sdf.parse(dateS); 123 | } catch (ParseException e) { 124 | e.printStackTrace(); 125 | } 126 | return date; 127 | } 128 | 129 | /** 130 | * 获取当前时间,格式为yyyy-MM-dd HH:mm:ss 131 | * @author chenssy 132 | * @date Dec 30, 2013 133 | * @return Date 134 | */ 135 | public static Date getCurrentDate(){ 136 | return getCurrentDate(DateFormatUtils.DATE_FORMAT2); 137 | } 138 | 139 | /** 140 | * 给指定日期加入年份,为空时默认当前时间 141 | * @author chenssy 142 | * @date Dec 30, 2013 143 | * @param year 年份 正数相加、负数相减 144 | * @param date 为空时,默认为当前时间 145 | * @param format 默认格式为:yyyy-MM-dd HH:mm:ss 146 | * @return String 147 | */ 148 | public static String addYearToDate(int year,Date date,String format){ 149 | Calendar calender = getCalendar(date,format); 150 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 151 | 152 | calender.add(Calendar.YEAR, year); 153 | 154 | return sdf.format(calender.getTime()); 155 | } 156 | 157 | /** 158 | * 给指定日期加入年份,为空时默认当前时间 159 | * @author chenssy 160 | * @date Dec 30, 2013 161 | * @param year 年份 正数相加、负数相减 162 | * @param date 为空时,默认为当前时间 163 | * @param format 默认格式为:yyyy-MM-dd HH:mm:ss 164 | * @return String 165 | */ 166 | public static String addYearToDate(int year,String date,String format){ 167 | Date newDate = new Date(); 168 | if(null != date && !"".equals(date)){ 169 | newDate = string2Date(date, format); 170 | } 171 | 172 | return addYearToDate(year, newDate, format); 173 | } 174 | 175 | /** 176 | * 给指定日期增加月份 为空时默认当前时间 177 | * @author chenssy 178 | * @date Dec 30, 2013 179 | * @param month 增加月份 正数相加、负数相减 180 | * @param date 指定时间 181 | * @param format 指定格式 为空默认 yyyy-mm-dd HH:mm:ss 182 | * @return String 183 | */ 184 | public static String addMothToDate(int month,Date date,String format) { 185 | Calendar calender = getCalendar(date,format); 186 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 187 | 188 | calender.add(Calendar.MONTH, month); 189 | 190 | return sdf.format(calender.getTime()); 191 | } 192 | 193 | /** 194 | * 给指定日期增加月份 为空时默认当前时间 195 | * @author chenssy 196 | * @date Dec 30, 2013 197 | * @param month 增加月份 正数相加、负数相减 198 | * @param date 指定时间 199 | * @param format 指定格式 为空默认 yyyy-mm-dd HH:mm:ss 200 | * @return String 201 | */ 202 | public static String addMothToDate(int month,String date,String format) { 203 | Date newDate = new Date(); 204 | if(null != date && !"".equals(date)){ 205 | newDate = string2Date(date, format); 206 | } 207 | 208 | return addMothToDate(month, newDate, format); 209 | } 210 | 211 | /** 212 | * 给指定日期增加天数,为空时默认当前时间 213 | * @author chenssy 214 | * @date Dec 31, 2013 215 | * @param day 增加天数 正数相加、负数相减 216 | * @param date 指定日期 217 | * @param format 日期格式 为空默认 yyyy-mm-dd HH:mm:ss 218 | * @return String 219 | */ 220 | public static String addDayToDate(int day,Date date,String format) { 221 | Calendar calendar = getCalendar(date, format); 222 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 223 | 224 | calendar.add(Calendar.DATE, day); 225 | 226 | return sdf.format(calendar.getTime()); 227 | } 228 | 229 | /** 230 | * 给指定日期增加天数,为空时默认当前时间 231 | * @author chenssy 232 | * @date Dec 31, 2013 233 | * @param day 增加天数 正数相加、负数相减 234 | * @param date 指定日期 235 | * @param format 日期格式 为空默认 yyyy-mm-dd HH:mm:ss 236 | * @return String 237 | */ 238 | public static String addDayToDate(int day,String date,String format) { 239 | Date newDate = new Date(); 240 | if(null != date && !"".equals(date)){ 241 | newDate = string2Date(date, format); 242 | } 243 | 244 | return addDayToDate(day, newDate, format); 245 | } 246 | 247 | /** 248 | * 给指定日期增加小时,为空时默认当前时间 249 | * @author chenssy 250 | * @date Dec 31, 2013 251 | * @param hour 增加小时 正数相加、负数相减 252 | * @param date 指定日期 253 | * @param format 日期格式 为空默认 yyyy-mm-dd HH:mm:ss 254 | * @return String 255 | */ 256 | public static String addHourToDate(int hour,Date date,String format) { 257 | Calendar calendar = getCalendar(date, format); 258 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 259 | 260 | calendar.add(Calendar.HOUR, hour); 261 | 262 | return sdf.format(calendar.getTime()); 263 | } 264 | 265 | /** 266 | * 给指定日期增加小时,为空时默认当前时间 267 | * @author chenssy 268 | * @date Dec 31, 2013 269 | * @param hour 增加小时 正数相加、负数相减 270 | * @param date 指定日期 271 | * @param format 日期格式 为空默认 yyyy-mm-dd HH:mm:ss 272 | * @return String 273 | */ 274 | public static String addHourToDate(int hour,String date,String format) { 275 | Date newDate = new Date(); 276 | if(null != date && !"".equals(date)){ 277 | newDate = string2Date(date, format); 278 | } 279 | 280 | return addHourToDate(hour, newDate, format); 281 | } 282 | 283 | /** 284 | * 给指定的日期增加分钟,为空时默认当前时间 285 | * @author chenssy 286 | * @date Dec 31, 2013 287 | * @param minute 增加分钟 正数相加、负数相减 288 | * @param date 指定日期 289 | * @param format 日期格式 为空默认 yyyy-mm-dd HH:mm:ss 290 | * @return String 291 | */ 292 | public static String addMinuteToDate(int minute,Date date,String format) { 293 | Calendar calendar = getCalendar(date, format); 294 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 295 | 296 | calendar.add(Calendar.MINUTE, minute); 297 | 298 | return sdf.format(calendar.getTime()); 299 | } 300 | 301 | /** 302 | * 给指定的日期增加分钟,为空时默认当前时间 303 | * @author chenssy 304 | * @date Dec 31, 2013 305 | * @param minute 增加分钟 正数相加、负数相减 306 | * @param date 指定日期 307 | * @param format 日期格式 为空默认 yyyy-mm-dd HH:mm:ss 308 | * @return String 309 | */ 310 | public static String addMinuteToDate(int minute,String date,String format){ 311 | Date newDate = new Date(); 312 | if(null != date && !"".equals(date)){ 313 | newDate = string2Date(date, format); 314 | } 315 | 316 | return addMinuteToDate(minute, newDate, format); 317 | } 318 | 319 | /** 320 | * 给指定日期增加秒,为空时默认当前时间 321 | * @author chenssy 322 | * @date Dec 31, 2013 323 | * @param second 增加秒 正数相加、负数相减 324 | * @param date 指定日期 325 | * @param format 日期格式 为空默认 yyyy-mm-dd HH:mm:ss 326 | * @return String 327 | */ 328 | public static String addSecondToDate(int second,Date date,String format){ 329 | Calendar calendar = getCalendar(date, format); 330 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 331 | 332 | calendar.add(Calendar.SECOND, second); 333 | 334 | return sdf.format(calendar.getTime()); 335 | } 336 | 337 | /** 338 | * 给指定日期增加秒,为空时默认当前时间 339 | * @author chenssy 340 | * @date Dec 31, 2013 341 | * @param second 增加秒 正数相加、负数相减 342 | * @param date 指定日期 343 | * @param format 日期格式 为空默认 yyyy-mm-dd HH:mm:ss 344 | * @return String 345 | * @throws Exception 346 | */ 347 | public static String addSecondToDate(int second,String date,String format){ 348 | Date newDate = new Date(); 349 | if(null != date && !"".equals(date)){ 350 | newDate = string2Date(date, format); 351 | } 352 | 353 | return addSecondToDate(second, newDate, format); 354 | } 355 | 356 | /** 357 | * 获取指定格式指定时间的日历 358 | * @author chenssy 359 | * @date Dec 30, 2013 360 | * @param date 时间 361 | * @param format 格式 362 | * @return Calendar 363 | */ 364 | public static Calendar getCalendar(Date date,String format){ 365 | if(date == null){ 366 | date = getCurrentDate(format); 367 | } 368 | 369 | Calendar calender = Calendar.getInstance(); 370 | calender.setTime(date); 371 | 372 | return calender; 373 | } 374 | 375 | /** 376 | * 字符串转换为日期,日期格式为 377 | * 378 | * @author : chenssy 379 | * @date : 2016年5月31日 下午5:20:22 380 | * 381 | * @param value 382 | * @return 383 | */ 384 | public static Date string2Date(String value){ 385 | if(value == null || "".equals(value)){ 386 | return null; 387 | } 388 | 389 | SimpleDateFormat sdf = DateFormatUtils.getFormat(DateFormatUtils.DATE_FORMAT2); 390 | Date date = null; 391 | 392 | try { 393 | value = DateFormatUtils.formatDate(value, DateFormatUtils.DATE_FORMAT2); 394 | date = sdf.parse(value); 395 | } catch (Exception e) { 396 | e.printStackTrace(); 397 | } 398 | return date; 399 | } 400 | 401 | /** 402 | * 将字符串(格式符合规范)转换成Date 403 | * @author chenssy 404 | * @date Dec 31, 2013 405 | * @param value 需要转换的字符串 406 | * @param format 日期格式 407 | * @return Date 408 | */ 409 | public static Date string2Date(String value,String format){ 410 | if(value == null || "".equals(value)){ 411 | return null; 412 | } 413 | 414 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 415 | Date date = null; 416 | 417 | try { 418 | value = DateFormatUtils.formatDate(value, format); 419 | date = sdf.parse(value); 420 | } catch (Exception e) { 421 | e.printStackTrace(); 422 | } 423 | return date; 424 | } 425 | 426 | /** 427 | * 将日期格式转换成String 428 | * @author chenssy 429 | * @date Dec 31, 2013 430 | * 431 | * @param value 需要转换的日期 432 | * @param format 日期格式 433 | * @return String 434 | */ 435 | public static String date2String(Date value,String format){ 436 | if(value == null){ 437 | return null; 438 | } 439 | 440 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 441 | return sdf.format(value); 442 | } 443 | 444 | /** 445 | * 日期转换为字符串 446 | * 447 | * @author : chenssy 448 | * @date : 2016年5月31日 下午5:21:38 449 | * 450 | * @param value 451 | * @return 452 | */ 453 | public static String date2String(Date value){ 454 | if(value == null){ 455 | return null; 456 | } 457 | 458 | SimpleDateFormat sdf = DateFormatUtils.getFormat(DateFormatUtils.DATE_FORMAT2); 459 | return sdf.format(value); 460 | } 461 | 462 | public static String date1String(Date value){ 463 | if(value == null){ 464 | return null; 465 | } 466 | 467 | SimpleDateFormat sdf = DateFormatUtils.getFormat(DateFormatUtils.DATE_FORMAT1); 468 | return sdf.format(value); 469 | } 470 | 471 | /** 472 | * 获取指定日期的年份 473 | * @author chenssy 474 | * @date Dec 31, 2013 475 | * @param value 日期 476 | * @return int 477 | */ 478 | public static int getCurrentYear(Date value){ 479 | String date = date2String(value, DateFormatUtils.DATE_YEAR); 480 | return Integer.valueOf(date); 481 | } 482 | 483 | /** 484 | * 获取指定日期的年份 485 | * @author chenssy 486 | * @date Dec 31, 2013 487 | * @param value 日期 488 | * @return int 489 | */ 490 | public static int getCurrentYear(String value) { 491 | Date date = string2Date(value, DateFormatUtils.DATE_YEAR); 492 | Calendar calendar = getCalendar(date, DateFormatUtils.DATE_YEAR); 493 | return calendar.get(Calendar.YEAR); 494 | } 495 | 496 | /** 497 | * 获取指定日期的月份 498 | * @author chenssy 499 | * @date Dec 31, 2013 500 | * @param value 日期 501 | * @return int 502 | */ 503 | public static int getCurrentMonth(Date value){ 504 | String date = date2String(value, DateFormatUtils.DATE_MONTH); 505 | return Integer.valueOf(date); 506 | } 507 | 508 | /** 509 | * 获取指定日期的月份 yyyy-MM-dd 格式 510 | * @author chenssy 511 | * @date Dec 31, 2013 512 | * @param value 日期 513 | * @return int 514 | */ 515 | public static int getCurrentMonth(String value) { 516 | Date date = string2Date(value, DateFormatUtils.DATE_FORMAT1); 517 | Calendar calendar = getCalendar(date, DateFormatUtils.DATE_FORMAT1); 518 | return calendar.get(Calendar.MONTH) + 1; 519 | } 520 | 521 | /** 522 | * 获取指定日期的天份 523 | * @author chenssy 524 | * @date Dec 31, 2013 525 | * @param value 日期 526 | * @return int 527 | */ 528 | public static int getCurrentDay(Date value){ 529 | String date = date2String(value, DateFormatUtils.DATE_DAY); 530 | return Integer.valueOf(date); 531 | } 532 | 533 | /** 534 | * 获取指定日期的天份 535 | * @author chenssy 536 | * @date Dec 31, 2013 537 | * @param value 日期 538 | * @return int 539 | */ 540 | public static int getCurrentDay(String value){ 541 | Date date = string2Date(value, DateFormatUtils.DATE_DAY); 542 | Calendar calendar = getCalendar(date, DateFormatUtils.DATE_DAY); 543 | 544 | return calendar.get(Calendar.DATE); 545 | } 546 | 547 | /** 548 | * 获取当前日期为星期几 549 | * @author chenssy 550 | * @date Dec 31, 2013 551 | * @param value 日期 552 | * @return String 553 | */ 554 | public static String getCurrentWeek(Date value) { 555 | Calendar calendar = getCalendar(value, DateFormatUtils.DATE_FORMAT1); 556 | int weekIndex = calendar.get(Calendar.DAY_OF_WEEK) - 1 < 0 ? 0 : calendar.get(Calendar.DAY_OF_WEEK) - 1; 557 | 558 | return weeks[weekIndex]; 559 | } 560 | 561 | /** 562 | * 获取当前日期为星期几 563 | * @author chenssy 564 | * @date Dec 31, 2013 565 | * @param value 日期 566 | * @return String 567 | */ 568 | public static String getCurrentWeek(String value) { 569 | Date date = string2Date(value, DateFormatUtils.DATE_FORMAT1); 570 | return getCurrentWeek(date); 571 | } 572 | 573 | /** 574 | * 获取指定日期的小时 575 | * @author chenssy 576 | * @date Dec 31, 2013 577 | * @param value 日期 578 | * @return int 579 | */ 580 | public static int getCurrentHour(Date value){ 581 | String date = date2String(value, DateFormatUtils.DATE_HOUR); 582 | return Integer.valueOf(date); 583 | } 584 | 585 | /** 586 | * 获取指定日期的小时 587 | * @author chenssy 588 | * @date Dec 31, 2013 589 | * @param value 日期 590 | * @return 591 | * @return int 592 | */ 593 | public static int getCurrentHour(String value) { 594 | Date date = string2Date(value, DateFormatUtils.DATE_HOUR); 595 | Calendar calendar = getCalendar(date, DateFormatUtils.DATE_HOUR); 596 | 597 | return calendar.get(Calendar.DATE); 598 | } 599 | 600 | /** 601 | * 获取指定日期的分钟 602 | * @author chenssy 603 | * @date Dec 31, 2013 604 | * @param value 日期 605 | * @return int 606 | */ 607 | public static int getCurrentMinute(Date value){ 608 | String date = date2String(value, DateFormatUtils.DATE_MINUTE); 609 | return Integer.valueOf(date); 610 | } 611 | 612 | /** 613 | * 获取指定日期的分钟 614 | * @author chenssy 615 | * @date Dec 31, 2013 616 | * @param value 日期 617 | * @return int 618 | */ 619 | public static int getCurrentMinute(String value){ 620 | Date date = string2Date(value, DateFormatUtils.DATE_MINUTE); 621 | Calendar calendar = getCalendar(date, DateFormatUtils.DATE_MINUTE); 622 | 623 | return calendar.get(Calendar.MINUTE); 624 | } 625 | 626 | /** 627 | * 比较两个日期相隔多少天(月、年)
628 | * 例:
629 | *  compareDate("2009-09-12", null, 0);//比较天
630 | *  compareDate("2009-09-12", null, 1);//比较月
631 | *  compareDate("2009-09-12", null, 2);//比较年
632 | * 633 | * @author chenssy 634 | * @date Dec 31, 2013 635 | * @param startDay 需要比较的时间 不能为空(null),需要正确的日期格式 ,如:2009-09-12 636 | * @param endDay 被比较的时间 为空(null)则为当前时间 637 | * @param stype 返回值类型 0为多少天,1为多少个月,2为多少年 638 | * @return int 639 | */ 640 | public static int compareDate(String startDay,String endDay,int stype) { 641 | int n = 0; 642 | startDay = DateFormatUtils.formatDate(startDay, "yyyy-MM-dd"); 643 | endDay = DateFormatUtils.formatDate(endDay, "yyyy-MM-dd"); 644 | 645 | String formatStyle = "yyyy-MM-dd"; 646 | if(1 == stype){ 647 | formatStyle = "yyyy-MM"; 648 | }else if(2 == stype){ 649 | formatStyle = "yyyy"; 650 | } 651 | 652 | endDay = endDay==null ? getCurrentTime("yyyy-MM-dd") : endDay; 653 | 654 | DateFormat df = new SimpleDateFormat(formatStyle); 655 | Calendar c1 = Calendar.getInstance(); 656 | Calendar c2 = Calendar.getInstance(); 657 | try { 658 | c1.setTime(df.parse(startDay)); 659 | c2.setTime(df.parse(endDay)); 660 | } catch (Exception e) { 661 | e.printStackTrace(); 662 | } 663 | while (!c1.after(c2)) { // 循环对比,直到相等,n 就是所要的结果 664 | n++; 665 | if(stype==1){ 666 | c1.add(Calendar.MONTH, 1); // 比较月份,月份+1 667 | } 668 | else{ 669 | c1.add(Calendar.DATE, 1); // 比较天数,日期+1 670 | } 671 | } 672 | n = n-1; 673 | if(stype==2){ 674 | n = (int)n/365; 675 | } 676 | return n; 677 | } 678 | 679 | /** 680 | * 比较两个时间相差多少小时(分钟、秒) 681 | * @author chenssy 682 | * @date Jan 2, 2014 683 | * @param startTime 需要比较的时间 不能为空,且必须符合正确格式:2012-12-12 12:12: 684 | * @param endTime 需要被比较的时间 若为空则默认当前时间 685 | * @param type 1:小时 2:分钟 3:秒 686 | * @return int 687 | */ 688 | public static int compareTime(String startTime , String endTime , int type) { 689 | //endTime是否为空,为空默认当前时间 690 | if(endTime == null || "".equals(endTime)){ 691 | endTime = getCurrentTime(); 692 | } 693 | 694 | SimpleDateFormat sdf = DateFormatUtils.getFormat(""); 695 | int value = 0; 696 | try { 697 | Date begin = sdf.parse(startTime); 698 | Date end = sdf.parse(endTime); 699 | long between = (end.getTime() - begin.getTime()) / 1000; //除以1000转换成豪秒 700 | if(type == 1){ //小时 701 | value = (int) (between % (24 * 36000) / 3600); 702 | } 703 | else if(type == 2){ 704 | value = (int) (between % 3600 / 60); 705 | } 706 | else if(type == 3){ 707 | value = (int) (between % 60 / 60); 708 | } 709 | } catch (ParseException e) { 710 | e.printStackTrace(); 711 | } 712 | return value; 713 | } 714 | 715 | /** 716 | * 获取指定月份的第一天 717 | * 718 | * @author : chenssy 719 | * @date : 2016年5月31日 下午5:31:10 720 | * 721 | * @param date 722 | * @return 723 | */ 724 | public static String getMonthFirstDate(String date){ 725 | date = DateFormatUtils.formatDate(date); 726 | return DateFormatUtils.formatDate(date, "yyyy-MM") + "-01"; 727 | } 728 | 729 | /** 730 | * 获取指定月份的最后一天 731 | * 732 | * @author : chenssy 733 | * @date : 2016年5月31日 下午5:32:09 734 | * 735 | * @param date 736 | * @return 737 | */ 738 | public static String getMonthLastDate(String date) { 739 | Date strDate = DateUtils.string2Date(getMonthFirstDate(date)); 740 | Calendar calendar = Calendar.getInstance(); 741 | calendar.setTime(strDate); 742 | calendar.add(Calendar.MONTH, 1); 743 | calendar.add(Calendar.DAY_OF_YEAR, -1); 744 | return DateFormatUtils.formatDate(calendar.getTime()); 745 | } 746 | 747 | /** 748 | * 获取所在星期的第一天 749 | * 750 | * @author : chenssy 751 | * @date : 2016年6月1日 下午12:38:53 752 | * 753 | * @param date 754 | * @return 755 | */ 756 | @SuppressWarnings("static-access") 757 | public static Date getWeekFirstDate(Date date) { 758 | Calendar now = Calendar.getInstance(); 759 | now.setTime(date); 760 | int today = now.get(Calendar.DAY_OF_WEEK); 761 | int first_day_of_week = now.get(Calendar.DATE) + 2 - today; // 星期一 762 | now.set(Calendar.DATE, first_day_of_week); 763 | return now.getTime(); 764 | } 765 | 766 | /** 767 | * 获取所在星期的最后一天 768 | * 769 | * @author : chenssy 770 | * @date : 2016年6月1日 下午12:40:31 771 | * 772 | * @param date 773 | * @return 774 | */ 775 | @SuppressWarnings("static-access") 776 | public static Date geWeektLastDate(Date date) { 777 | Calendar now = Calendar.getInstance(); 778 | now.setTime(date); 779 | int today = now.get(Calendar.DAY_OF_WEEK); 780 | int first_day_of_week = now.get(Calendar.DATE) + 2 - today; // 星期一 781 | int last_day_of_week = first_day_of_week + 6; // 星期日 782 | now.set(Calendar.DATE, last_day_of_week); 783 | return now.getTime(); 784 | } 785 | } 786 | -------------------------------------------------------------------------------- /src/main/java/com/howie/util/WebSocketUtil.java: -------------------------------------------------------------------------------- 1 | package com.howie.util; 2 | 3 | import io.netty.channel.Channel; 4 | 5 | import java.net.SocketAddress; 6 | 7 | /** 8 | * Created with IntelliJ IDEA 9 | * 10 | * @Author yuanhaoyue swithaoy@gmail.com 11 | * @Description 12 | * @Date 2018-05-12 13 | * @Time 17:15 14 | */ 15 | public class WebSocketUtil { 16 | /** 17 | * 获得Channel远程主机IP地址 18 | */ 19 | public static String getChannelAddress(final Channel channel) { 20 | if (null == channel) { 21 | return ""; 22 | } 23 | SocketAddress address = channel.remoteAddress(); 24 | String addr = (address != null ? address.toString() : ""); 25 | int index = addr.lastIndexOf("/"); 26 | if (index >= 0) { 27 | return addr.substring(index + 1); 28 | } 29 | return addr; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Archetype Created Web Application 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | .btn-default, 7 | .btn-primary, 8 | .btn-success, 9 | .btn-info, 10 | .btn-warning, 11 | .btn-danger { 12 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 13 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 14 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | } 16 | .btn-default:active, 17 | .btn-primary:active, 18 | .btn-success:active, 19 | .btn-info:active, 20 | .btn-warning:active, 21 | .btn-danger:active, 22 | .btn-default.active, 23 | .btn-primary.active, 24 | .btn-success.active, 25 | .btn-info.active, 26 | .btn-warning.active, 27 | .btn-danger.active { 28 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 29 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | } 31 | .btn-default.disabled, 32 | .btn-primary.disabled, 33 | .btn-success.disabled, 34 | .btn-info.disabled, 35 | .btn-warning.disabled, 36 | .btn-danger.disabled, 37 | .btn-default[disabled], 38 | .btn-primary[disabled], 39 | .btn-success[disabled], 40 | .btn-info[disabled], 41 | .btn-warning[disabled], 42 | .btn-danger[disabled], 43 | fieldset[disabled] .btn-default, 44 | fieldset[disabled] .btn-primary, 45 | fieldset[disabled] .btn-success, 46 | fieldset[disabled] .btn-info, 47 | fieldset[disabled] .btn-warning, 48 | fieldset[disabled] .btn-danger { 49 | -webkit-box-shadow: none; 50 | box-shadow: none; 51 | } 52 | .btn-default .badge, 53 | .btn-primary .badge, 54 | .btn-success .badge, 55 | .btn-info .badge, 56 | .btn-warning .badge, 57 | .btn-danger .badge { 58 | text-shadow: none; 59 | } 60 | .btn:active, 61 | .btn.active { 62 | background-image: none; 63 | } 64 | .btn-default { 65 | text-shadow: 0 1px 0 #fff; 66 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 67 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 68 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 69 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 70 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 71 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 72 | background-repeat: repeat-x; 73 | border-color: #dbdbdb; 74 | border-color: #ccc; 75 | } 76 | .btn-default:hover, 77 | .btn-default:focus { 78 | background-color: #e0e0e0; 79 | background-position: 0 -15px; 80 | } 81 | .btn-default:active, 82 | .btn-default.active { 83 | background-color: #e0e0e0; 84 | border-color: #dbdbdb; 85 | } 86 | .btn-default.disabled, 87 | .btn-default[disabled], 88 | fieldset[disabled] .btn-default, 89 | .btn-default.disabled:hover, 90 | .btn-default[disabled]:hover, 91 | fieldset[disabled] .btn-default:hover, 92 | .btn-default.disabled:focus, 93 | .btn-default[disabled]:focus, 94 | fieldset[disabled] .btn-default:focus, 95 | .btn-default.disabled.focus, 96 | .btn-default[disabled].focus, 97 | fieldset[disabled] .btn-default.focus, 98 | .btn-default.disabled:active, 99 | .btn-default[disabled]:active, 100 | fieldset[disabled] .btn-default:active, 101 | .btn-default.disabled.active, 102 | .btn-default[disabled].active, 103 | fieldset[disabled] .btn-default.active { 104 | background-color: #e0e0e0; 105 | background-image: none; 106 | } 107 | .btn-primary { 108 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); 109 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); 110 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); 111 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); 112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); 113 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 114 | background-repeat: repeat-x; 115 | border-color: #245580; 116 | } 117 | .btn-primary:hover, 118 | .btn-primary:focus { 119 | background-color: #265a88; 120 | background-position: 0 -15px; 121 | } 122 | .btn-primary:active, 123 | .btn-primary.active { 124 | background-color: #265a88; 125 | border-color: #245580; 126 | } 127 | .btn-primary.disabled, 128 | .btn-primary[disabled], 129 | fieldset[disabled] .btn-primary, 130 | .btn-primary.disabled:hover, 131 | .btn-primary[disabled]:hover, 132 | fieldset[disabled] .btn-primary:hover, 133 | .btn-primary.disabled:focus, 134 | .btn-primary[disabled]:focus, 135 | fieldset[disabled] .btn-primary:focus, 136 | .btn-primary.disabled.focus, 137 | .btn-primary[disabled].focus, 138 | fieldset[disabled] .btn-primary.focus, 139 | .btn-primary.disabled:active, 140 | .btn-primary[disabled]:active, 141 | fieldset[disabled] .btn-primary:active, 142 | .btn-primary.disabled.active, 143 | .btn-primary[disabled].active, 144 | fieldset[disabled] .btn-primary.active { 145 | background-color: #265a88; 146 | background-image: none; 147 | } 148 | .btn-success { 149 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 150 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 151 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 152 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 153 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 154 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 155 | background-repeat: repeat-x; 156 | border-color: #3e8f3e; 157 | } 158 | .btn-success:hover, 159 | .btn-success:focus { 160 | background-color: #419641; 161 | background-position: 0 -15px; 162 | } 163 | .btn-success:active, 164 | .btn-success.active { 165 | background-color: #419641; 166 | border-color: #3e8f3e; 167 | } 168 | .btn-success.disabled, 169 | .btn-success[disabled], 170 | fieldset[disabled] .btn-success, 171 | .btn-success.disabled:hover, 172 | .btn-success[disabled]:hover, 173 | fieldset[disabled] .btn-success:hover, 174 | .btn-success.disabled:focus, 175 | .btn-success[disabled]:focus, 176 | fieldset[disabled] .btn-success:focus, 177 | .btn-success.disabled.focus, 178 | .btn-success[disabled].focus, 179 | fieldset[disabled] .btn-success.focus, 180 | .btn-success.disabled:active, 181 | .btn-success[disabled]:active, 182 | fieldset[disabled] .btn-success:active, 183 | .btn-success.disabled.active, 184 | .btn-success[disabled].active, 185 | fieldset[disabled] .btn-success.active { 186 | background-color: #419641; 187 | background-image: none; 188 | } 189 | .btn-info { 190 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 191 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 192 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 193 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 194 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 195 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 196 | background-repeat: repeat-x; 197 | border-color: #28a4c9; 198 | } 199 | .btn-info:hover, 200 | .btn-info:focus { 201 | background-color: #2aabd2; 202 | background-position: 0 -15px; 203 | } 204 | .btn-info:active, 205 | .btn-info.active { 206 | background-color: #2aabd2; 207 | border-color: #28a4c9; 208 | } 209 | .btn-info.disabled, 210 | .btn-info[disabled], 211 | fieldset[disabled] .btn-info, 212 | .btn-info.disabled:hover, 213 | .btn-info[disabled]:hover, 214 | fieldset[disabled] .btn-info:hover, 215 | .btn-info.disabled:focus, 216 | .btn-info[disabled]:focus, 217 | fieldset[disabled] .btn-info:focus, 218 | .btn-info.disabled.focus, 219 | .btn-info[disabled].focus, 220 | fieldset[disabled] .btn-info.focus, 221 | .btn-info.disabled:active, 222 | .btn-info[disabled]:active, 223 | fieldset[disabled] .btn-info:active, 224 | .btn-info.disabled.active, 225 | .btn-info[disabled].active, 226 | fieldset[disabled] .btn-info.active { 227 | background-color: #2aabd2; 228 | background-image: none; 229 | } 230 | .btn-warning { 231 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 232 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 233 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 234 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 235 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 236 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 237 | background-repeat: repeat-x; 238 | border-color: #e38d13; 239 | } 240 | .btn-warning:hover, 241 | .btn-warning:focus { 242 | background-color: #eb9316; 243 | background-position: 0 -15px; 244 | } 245 | .btn-warning:active, 246 | .btn-warning.active { 247 | background-color: #eb9316; 248 | border-color: #e38d13; 249 | } 250 | .btn-warning.disabled, 251 | .btn-warning[disabled], 252 | fieldset[disabled] .btn-warning, 253 | .btn-warning.disabled:hover, 254 | .btn-warning[disabled]:hover, 255 | fieldset[disabled] .btn-warning:hover, 256 | .btn-warning.disabled:focus, 257 | .btn-warning[disabled]:focus, 258 | fieldset[disabled] .btn-warning:focus, 259 | .btn-warning.disabled.focus, 260 | .btn-warning[disabled].focus, 261 | fieldset[disabled] .btn-warning.focus, 262 | .btn-warning.disabled:active, 263 | .btn-warning[disabled]:active, 264 | fieldset[disabled] .btn-warning:active, 265 | .btn-warning.disabled.active, 266 | .btn-warning[disabled].active, 267 | fieldset[disabled] .btn-warning.active { 268 | background-color: #eb9316; 269 | background-image: none; 270 | } 271 | .btn-danger { 272 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 273 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 274 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 275 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 276 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 277 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 278 | background-repeat: repeat-x; 279 | border-color: #b92c28; 280 | } 281 | .btn-danger:hover, 282 | .btn-danger:focus { 283 | background-color: #c12e2a; 284 | background-position: 0 -15px; 285 | } 286 | .btn-danger:active, 287 | .btn-danger.active { 288 | background-color: #c12e2a; 289 | border-color: #b92c28; 290 | } 291 | .btn-danger.disabled, 292 | .btn-danger[disabled], 293 | fieldset[disabled] .btn-danger, 294 | .btn-danger.disabled:hover, 295 | .btn-danger[disabled]:hover, 296 | fieldset[disabled] .btn-danger:hover, 297 | .btn-danger.disabled:focus, 298 | .btn-danger[disabled]:focus, 299 | fieldset[disabled] .btn-danger:focus, 300 | .btn-danger.disabled.focus, 301 | .btn-danger[disabled].focus, 302 | fieldset[disabled] .btn-danger.focus, 303 | .btn-danger.disabled:active, 304 | .btn-danger[disabled]:active, 305 | fieldset[disabled] .btn-danger:active, 306 | .btn-danger.disabled.active, 307 | .btn-danger[disabled].active, 308 | fieldset[disabled] .btn-danger.active { 309 | background-color: #c12e2a; 310 | background-image: none; 311 | } 312 | .thumbnail, 313 | .img-thumbnail { 314 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 315 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 316 | } 317 | .dropdown-menu > li > a:hover, 318 | .dropdown-menu > li > a:focus { 319 | background-color: #e8e8e8; 320 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 321 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 322 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 323 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 324 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 325 | background-repeat: repeat-x; 326 | } 327 | .dropdown-menu > .active > a, 328 | .dropdown-menu > .active > a:hover, 329 | .dropdown-menu > .active > a:focus { 330 | background-color: #2e6da4; 331 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 332 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 333 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 334 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 336 | background-repeat: repeat-x; 337 | } 338 | .navbar-default { 339 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 340 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 342 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 344 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 345 | background-repeat: repeat-x; 346 | border-radius: 4px; 347 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 348 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 349 | } 350 | .navbar-default .navbar-nav > .open > a, 351 | .navbar-default .navbar-nav > .active > a { 352 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 353 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 354 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); 355 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); 356 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); 357 | background-repeat: repeat-x; 358 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 359 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 360 | } 361 | .navbar-brand, 362 | .navbar-nav > li > a { 363 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 364 | } 365 | .navbar-inverse { 366 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 367 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 368 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 369 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 370 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 371 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 372 | background-repeat: repeat-x; 373 | border-radius: 4px; 374 | } 375 | .navbar-inverse .navbar-nav > .open > a, 376 | .navbar-inverse .navbar-nav > .active > a { 377 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); 378 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); 379 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); 380 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); 381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); 382 | background-repeat: repeat-x; 383 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 384 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 385 | } 386 | .navbar-inverse .navbar-brand, 387 | .navbar-inverse .navbar-nav > li > a { 388 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 389 | } 390 | .navbar-static-top, 391 | .navbar-fixed-top, 392 | .navbar-fixed-bottom { 393 | border-radius: 0; 394 | } 395 | @media (max-width: 767px) { 396 | .navbar .navbar-nav .open .dropdown-menu > .active > a, 397 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, 398 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { 399 | color: #fff; 400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 405 | background-repeat: repeat-x; 406 | } 407 | } 408 | .alert { 409 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 410 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 411 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 412 | } 413 | .alert-success { 414 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 415 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 416 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 417 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 418 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 419 | background-repeat: repeat-x; 420 | border-color: #b2dba1; 421 | } 422 | .alert-info { 423 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 424 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 425 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 426 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 428 | background-repeat: repeat-x; 429 | border-color: #9acfea; 430 | } 431 | .alert-warning { 432 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 433 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 435 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 437 | background-repeat: repeat-x; 438 | border-color: #f5e79e; 439 | } 440 | .alert-danger { 441 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 442 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 443 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 444 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 445 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 446 | background-repeat: repeat-x; 447 | border-color: #dca7a7; 448 | } 449 | .progress { 450 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 451 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 453 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 455 | background-repeat: repeat-x; 456 | } 457 | .progress-bar { 458 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); 459 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); 460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); 461 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); 462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); 463 | background-repeat: repeat-x; 464 | } 465 | .progress-bar-success { 466 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 467 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 469 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 471 | background-repeat: repeat-x; 472 | } 473 | .progress-bar-info { 474 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 475 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 476 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 477 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 478 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 479 | background-repeat: repeat-x; 480 | } 481 | .progress-bar-warning { 482 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 483 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 484 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 485 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 486 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 487 | background-repeat: repeat-x; 488 | } 489 | .progress-bar-danger { 490 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 491 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 492 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 493 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 494 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 495 | background-repeat: repeat-x; 496 | } 497 | .progress-bar-striped { 498 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 499 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 500 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 501 | } 502 | .list-group { 503 | border-radius: 4px; 504 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 505 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 506 | } 507 | .list-group-item.active, 508 | .list-group-item.active:hover, 509 | .list-group-item.active:focus { 510 | text-shadow: 0 -1px 0 #286090; 511 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); 512 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); 513 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); 514 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); 515 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); 516 | background-repeat: repeat-x; 517 | border-color: #2b669a; 518 | } 519 | .list-group-item.active .badge, 520 | .list-group-item.active:hover .badge, 521 | .list-group-item.active:focus .badge { 522 | text-shadow: none; 523 | } 524 | .panel { 525 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 526 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 527 | } 528 | .panel-default > .panel-heading { 529 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 530 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 531 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 532 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 533 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 534 | background-repeat: repeat-x; 535 | } 536 | .panel-primary > .panel-heading { 537 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 538 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 539 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 540 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 541 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 542 | background-repeat: repeat-x; 543 | } 544 | .panel-success > .panel-heading { 545 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 546 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 547 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 548 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 549 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 550 | background-repeat: repeat-x; 551 | } 552 | .panel-info > .panel-heading { 553 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 554 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 555 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 556 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 557 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 558 | background-repeat: repeat-x; 559 | } 560 | .panel-warning > .panel-heading { 561 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 562 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 563 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 564 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 565 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 566 | background-repeat: repeat-x; 567 | } 568 | .panel-danger > .panel-heading { 569 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 570 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 571 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 572 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 573 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 574 | background-repeat: repeat-x; 575 | } 576 | .well { 577 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 578 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 579 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 580 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 581 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 582 | background-repeat: repeat-x; 583 | border-color: #dcdcdc; 584 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 585 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 586 | } 587 | /*# sourceMappingURL=bootstrap-theme.css.map */ 588 | -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} 6 | /*# sourceMappingURL=bootstrap-theme.min.css.map */ -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/css/bootstrap-theme.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/css/chat.css: -------------------------------------------------------------------------------- 1 | .background { 2 | background-image: url('http://p8np6teyy.bkt.clouddn.com/chatroombackground'); 3 | } 4 | 5 | .mianFrame { 6 | max-width: 1000px; 7 | min-width: 800px; 8 | height: 100%; 9 | margin: 0 auto; 10 | overflow: hidden; 11 | background: #2e3238; 12 | } 13 | 14 | .panel { 15 | position: relative; 16 | width: 280px; 17 | height: 100%; 18 | float: left; 19 | background: #2e3641; 20 | } 21 | 22 | .header { 23 | padding: 18px; 24 | border-bottom: 1px solid #cccccc; 25 | height: 85px; 26 | } 27 | 28 | .avatar { 29 | float: left; 30 | margin-right: 20px; 31 | position: relative; 32 | } 33 | 34 | .box { 35 | position: relative; 36 | background-color: #eee; 37 | height: 100%; 38 | overflow: hidden; 39 | } 40 | 41 | .textareaHead { 42 | position: absolute; 43 | width: 100%; 44 | padding-top: 5px; 45 | padding-bottom: 5px; 46 | text-align: center; 47 | font-size: 15px; 48 | border-style: groove; 49 | } 50 | 51 | .textarea { 52 | position: absolute; 53 | right: 0; 54 | left: 0; 55 | top: 5.7%; 56 | width: 100%; 57 | height: 67%; 58 | } 59 | 60 | .sendButton { 61 | left: 600px; 62 | position: absolute; 63 | background-color: #fff; 64 | color: #222; 65 | display: inline-block; 66 | border: 1px solid #c1c1c1; 67 | border-radius: 4px; 68 | -moz-border-radius: 4px; 69 | -webkit-border-radius: 4px; 70 | padding: 4px 30px; 71 | font-size: 14px; 72 | } 73 | 74 | .chatMessageBox { 75 | margin-top: 15px; 76 | margin-left: 15px; 77 | min-height: 57px; 78 | margin-bottom: 18px; 79 | } 80 | 81 | .chatMessageBox_me { 82 | margin-top: 15px; 83 | text-align: right; 84 | margin-right: 65px; 85 | margin-left: 15px; 86 | min-height: 57px; 87 | margin-bottom: 18px; 88 | } 89 | 90 | .chatAvatar { 91 | position: absolute; 92 | height: 40px; 93 | width: 40px; 94 | border-radius: 5px; 95 | } 96 | 97 | .chatAvatar_me { 98 | right: 15px; 99 | position: absolute; 100 | height: 40px; 101 | width: 40px; 102 | border-radius: 5px; 103 | } 104 | 105 | .chatTime { 106 | margin-left: 50px; 107 | margin-bottom: 3px; 108 | font-size: xx-small; 109 | } 110 | 111 | .chatMessgae { 112 | display: inline-block; 113 | max-width: 500px; 114 | font-size: small; 115 | padding-left: 10px; 116 | padding-right: 10px; 117 | padding-bottom: 12px; 118 | padding-top: 12px; 119 | margin-left: 50px; 120 | background-color: white; 121 | border-radius: 4px; 122 | word-wrap: break-word; 123 | overflow: hidden; 124 | } 125 | 126 | .chatMessgae_me { 127 | text-align: left; 128 | display: inline-block; 129 | max-width: 500px; 130 | font-size: small; 131 | padding-left: 10px; 132 | padding-right: 10px; 133 | padding-bottom: 12px; 134 | padding-top: 12px; 135 | background-color: #B2E281; 136 | border-radius: 4px; 137 | word-wrap: break-word; 138 | overflow: hidden; 139 | } 140 | 141 | .systemMessage { 142 | text-align: center; 143 | margin-bottom: 10px; 144 | margin-top: 10px; 145 | margin-left: 200px; 146 | margin-right: 200px; 147 | color: #f9f2f4; 148 | background-color: #d4d4d4; 149 | } 150 | 151 | .box_ft { 152 | padding-top: 15px; 153 | padding-left: 20px; 154 | padding-right: 20px; 155 | padding-bottom: 15px; 156 | outline: none; 157 | resize: none; 158 | position: absolute; 159 | right: 0; 160 | bottom: 7.5%; 161 | left: 0; 162 | height: 20%; 163 | width: 100%; 164 | background-color: #eee; 165 | border-style: solid none none none; 166 | } 167 | 168 | .chat_item { 169 | background-color: #2e3641; 170 | overflow: hidden; 171 | padding: 12px 18px 11px; 172 | border-bottom: 1px solid #292c33; 173 | width: 100%; 174 | cursor: pointer; 175 | position: relative; 176 | } 177 | 178 | .send { 179 | position: absolute; 180 | right: 0; 181 | bottom: 2.5%; 182 | left: 0; 183 | width: 100%; 184 | height: 5%; 185 | } 186 | 187 | .scroll { 188 | overflow-y: scroll; 189 | } 190 | 191 | ::-webkit-scrollbar-track-piece { 192 | background-color: #f8f8f8; 193 | } 194 | 195 | ::-webkit-scrollbar { 196 | width: 9px; 197 | height: 9px; 198 | } 199 | 200 | ::-webkit-scrollbar-thumb { 201 | background-color: #dddddd; 202 | background-clip: padding-box; 203 | min-height: 28px; 204 | } 205 | 206 | ::-webkit-scrollbar-thumb:hover { 207 | background-color: #bbb; 208 | } 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/css/index.css: -------------------------------------------------------------------------------- 1 | /*.indexBox {*/ 2 | /*width: 500px;*/ 3 | /*}*/ -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/js/chat.js: -------------------------------------------------------------------------------- 1 | let socket; 2 | const groupChatName = "群聊室"; 3 | let currentChatUserNick = groupChatName; 4 | let currentChatUserId; 5 | 6 | const webSocketUrl = "ws://localhost:8090/websocket"; 7 | const myNick = GetQueryString("nick"); 8 | let me; 9 | 10 | const GROUP_CHAT_MESSAGE_CODE = 2000; 11 | const SYSTEM_MESSAGE_CODE = 2001; 12 | const PRIVATE_CHAT_MESSAGE_CODE = 2002; 13 | const PING_MESSAGE_CODE = 2003; 14 | 15 | const NORMAL_SYSTEM_MESSGAE_CODE = 3000; 16 | const UPDATE_USERCOUNT_SYSTEM_MESSGAE_CODE = 3001; 17 | const UPDATE_USERLIST_SYSTEM_MESSGAE_CODE = 3002; 18 | const PERSONAL_SYSTEM_MESSGAE_CODE = 3003; 19 | 20 | function systemMessage(data) { 21 | switch (data.body.systemMessageCode) { 22 | case PING_MESSAGE_CODE : 23 | sendPong(); 24 | case NORMAL_SYSTEM_MESSGAE_CODE : 25 | $("#responseContent").append("
" + data.message + " (" + data.time + ")" + "
"); 26 | break; 27 | case PERSONAL_SYSTEM_MESSGAE_CODE: 28 | me = data.body.object; 29 | $("#myAvatar").attr("src", me.avatarAddress); 30 | break; 31 | case UPDATE_USERCOUNT_SYSTEM_MESSGAE_CODE : 32 | $('#userCount').text("在线人数:" + data.body.object); 33 | break; 34 | case UPDATE_USERLIST_SYSTEM_MESSGAE_CODE : 35 | let users = data.body.object; 36 | let userList = $("#userList"); 37 | let repeatBox = $("#repeatBox"); 38 | let appendString; 39 | userList.text(""); 40 | userList.append( 41 | '
' + 42 | '' + 43 | '' + 44 | '
群聊室
' + 45 | '
'); 46 | users.forEach(function (user) { 47 | userList.append( 48 | '
' + 49 | '' + 50 | '' + 51 | '
' + user.nick + '
' + 52 | '
'); 53 | appendString = 54 | [''].join(""); 64 | repeatBox.append(appendString); 65 | }); 66 | break; 67 | } 68 | } 69 | 70 | function websocket() { 71 | if (!window.WebSocket) { 72 | window.WebSocket = window.MozWebSocket; 73 | } 74 | if (window.WebSocket) { 75 | socket = new WebSocket(webSocketUrl); 76 | socket.onmessage = function (event) { 77 | let data = JSON.parse(event.data); 78 | switch (data.code) { 79 | case GROUP_CHAT_MESSAGE_CODE: 80 | if (data.user.id !== me.id) { 81 | $("#responseContent").append( 82 | "
" + 83 | " " + 84 | "
" + data.user.nick + "   " + data.time + "
" + 85 | "
" + data.message + "
" + 86 | "
"); 87 | } else { 88 | $("#responseContent").append( 89 | "
" + 90 | " " + 91 | "
" + data.time + "   " + data.user.nick + "
" + 92 | "
" + data.message + "
" + 93 | "
"); 94 | } 95 | updateRedPoint(null); 96 | boxScroll(document.getElementById("responseContent")); 97 | break; 98 | case SYSTEM_MESSAGE_CODE: 99 | systemMessage(data); 100 | boxScroll(document.getElementById("responseContent")); 101 | break; 102 | case PRIVATE_CHAT_MESSAGE_CODE: 103 | if (data.user.id !== me.id) { 104 | $("#responseContent-" + data.receiverId).append( 105 | "
" + 106 | " " + 107 | "
" + data.user.nick + "   " + data.time + "
" + 108 | "
" + data.message + "
" + 109 | "
"); 110 | } else { 111 | $("#responseContent-" + data.receiverId).append( 112 | "
" + 113 | " " + 114 | "
" + data.time + "   " + data.user.nick + "
" + 115 | "
" + data.message + "
" + 116 | "
"); 117 | } 118 | updateRedPoint(data.user.id); 119 | boxScroll(document.getElementById("responseContent-" + data.receiverId)); 120 | } 121 | }; 122 | socket.onopen = function () { 123 | loginSend(); 124 | }; 125 | socket.onclose = function () { 126 | quitSend(); 127 | }; 128 | return true; 129 | } else { 130 | alert("您的浏览器不支持WebSocket"); 131 | return false; 132 | } 133 | } 134 | 135 | function loginSend() { 136 | let object = {}; 137 | object.code = 1000; 138 | object.nick = myNick; 139 | send(JSON.stringify(object)); 140 | } 141 | 142 | function quitSend() { 143 | let object = {}; 144 | object.code = 1001; 145 | object.nick = myNick; 146 | send(JSON.stringify(object)); 147 | } 148 | 149 | 150 | function sendPong() { 151 | let object = {}; 152 | object.code = 1004; 153 | send(JSON.stringify(object)); 154 | } 155 | 156 | function sendMessageToUser(message, id) { 157 | if (message === "" || message == null) { 158 | alert("信息不能为空~"); 159 | return; 160 | } 161 | let object = {}; 162 | object.code = 1003; 163 | object.nick = myNick; 164 | object.id = id; 165 | object.chatMessage = message; 166 | $('#sendTextarea-' + id).val(""); 167 | send(JSON.stringify(object)); 168 | } 169 | 170 | function sendMessage(message) { 171 | if (message === "" || message == null) { 172 | alert("信息不能为空~"); 173 | return; 174 | } 175 | let object = {}; 176 | object.code = 1002; 177 | object.nick = myNick; 178 | object.chatMessage = message; 179 | $('#sendTextarea').val(""); 180 | send(JSON.stringify(object)); 181 | } 182 | 183 | function send(message) { 184 | if (!window.WebSocket) { 185 | return; 186 | } 187 | if (socket.readyState === WebSocket.OPEN) { 188 | socket.send(message); 189 | } else { 190 | alert("WebSocket连接没有建立成功!!"); 191 | } 192 | } 193 | 194 | function chooseUser(username, id) { 195 | let box = $("#box"); 196 | if (username != null) { 197 | $("#redPoint-" + id).css("display", "none"); 198 | if (currentChatUserNick === groupChatName) { 199 | $("#box-" + id).css("display", "block"); 200 | box.css("display", "none"); 201 | currentChatUserNick = username; 202 | currentChatUserId = id; 203 | } else if (currentChatUserNick !== groupChatName && currentChatUserId !== id) { 204 | $("#box-" + id).css("display", "block"); 205 | $("#box-" + currentChatUserId).css("display", "none"); 206 | currentChatUserNick = username; 207 | currentChatUserId = id; 208 | } 209 | } else if (username === null && currentChatUserNick !== groupChatName) { 210 | $("#redPoint").css("display", "none"); 211 | $("#box-" + id).css("display", "none"); 212 | box.css("display", "block"); 213 | currentChatUserNick = groupChatName; 214 | } 215 | } 216 | 217 | /** 218 | * 新消息红点提醒 219 | * @param id 220 | */ 221 | function updateRedPoint(id) { 222 | if (id == null && currentChatUserNick !== groupChatName) { 223 | $("#redPoint").css("display", "block"); 224 | } else if (currentChatUserId !== id && id !== me.id) { 225 | $("#redPoint-" + id).css("display", "block"); 226 | } 227 | } 228 | 229 | /** 230 | * Get 请求获取参数 231 | * @return {null} 232 | * @param name 参数名 233 | */ 234 | function GetQueryString(name) { 235 | const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); 236 | const r = window.location.search.substr(1).match(reg); 237 | if (r !== null) { 238 | return unescape(decodeURI(decodeURI(r[2]))); 239 | } 240 | return null; 241 | } 242 | 243 | /** 244 | * 滚动条置底 245 | * @param o document.getElementById("id") 246 | */ 247 | function boxScroll(o) { 248 | o.scrollTop = o.scrollHeight; 249 | } -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /src/main/resources/webapp/html/chat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netty_websocket 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 |
24 | 26 |
27 |
28 |

29 |
30 |
31 |
32 |
33 |
群聊室
34 |
35 |
36 | 39 |
40 | 41 |
42 |
43 |
44 |
45 |
46 | 47 | 48 | 54 | -------------------------------------------------------------------------------- /src/main/resources/webapp/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 聊天室 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

欢迎来到聊天室

14 |
15 |
16 |
17 |
18 | 19 | 22 | 23 | 24 |
25 |
26 |
27 |
28 | 29 | 43 | -------------------------------------------------------------------------------- /src/main/resources/webapp/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/1.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/10.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/11.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/12.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/13.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/14.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/15.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/16.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/17.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/18.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/19.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/2.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/20.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/21.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/22.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/23.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/24.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/25.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/26.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/27.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/28.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/29.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/3.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/30.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/31.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/32.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/33.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/4.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/5.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/6.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/7.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/8.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/9.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/chatroom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/chatroom.png -------------------------------------------------------------------------------- /src/main/resources/webapp/img/redPoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HowieYuan/chatroom/3ec3915a164fcc5eadbf913921855507e89c0b53/src/main/resources/webapp/img/redPoint.png --------------------------------------------------------------------------------