Skip to content

Instantly share code, notes, and snippets.

@barretlee
Created October 11, 2016 02:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barretlee/5d66f84c1de3a0f46db435fd06c3fe6e to your computer and use it in GitHub Desktop.
Save barretlee/5d66f84c1de3a0f46db435fd06c3fe6e to your computer and use it in GitHub Desktop.
websokcet demo
<script src="http://cdn.staticfile.org/socket.io/1.3.7/socket.io.js"></script>
<script src="http://cdn.staticfile.org/jquery/2.2.1/jquery.min.js"></script>
<script>
function htmlspecialchars(str){
str = str || '';
str = str.replace(/&/g, '&amp;');
str = str.replace(/</g, '&lt;');
str = str.replace(/>/g, '&gt;');
str = str.replace(/"/g, '&quot;');
str = str.replace(/'/g, '&#039;');
return str;
}
var ChatRoomClient = function() {
this.users = [];
this.nameChanged = false;
this.totalCount = 0;
// this.socket = io.connect('http://123.56.230.53:29231/');
this.socket = io.connect();
this.startup();
this.init();
};
ChatRoomClient.prototype.init = function() {
this.connection();
this.socketEvent();
this.bindEvent();
this.addInfoLog({
msg: '注意:聊天日志不会保存,请注意备份.'
}, 'group');
this.addInfoLog({
msg: '提示:点击头像可进入私聊'
}, 'group');
};
ChatRoomClient.prototype.startup = function() {
var xtpl = [
'<div class="chatroom">',
'<div class="chatroom-feedback"><a href="https://github.com/barretlee/blogChat" target="_blank">源码</a> | <a href="https://github.com/barretlee/blogChat/issues/new" target="_blank">反馈</a></div>',
'<div class="chatroom-info"></div>',
'<ul class="chatroom-tribes">',
'<li class="chatroom-tribe current" data-id="group">',
'<span class="name">当前 <strong>1</strong> 人群聊</span>',
'<span class="count">0</span>',
'</li>',
'</ul>',
'<div class="chatroom-pannels">',
'<div class="chatroom-pannel-bd">',
'<div class="chatroom-item current" data-id="group">',
'</div>',
'</div>',
'<div class="chatroom-pannel-ft"><textarea type="text" class="chatroom-input" placeholder="按 Ctrl+Enter 发送"></textarea><span class="chatroom-send-btn">发送</span></div>',
'</div>',
'</div>'
].join('\n');
$('html').append(xtpl);
}
ChatRoomClient.prototype.connection = function(cb) {
var self = this;
self.socket.on('connected', function(data) {
if(data.size) {
$('.chatroom-tribe[data-id="group"] .name strong').text(data.size + 1);
}
self.userId = data.id;
self.userName = self.userId.slice(2);
self.userAvatar = '//avatar.duoshuo.com/avatar-50/292/117200.jpg';
if(window.DUOSHUO && window.DUOSHUO.visitor
&& window.DUOSHUO.visitor.data.user_id) {
var userInfo = window.DUOSHUO.visitor.data;
self.userId = userInfo.user_id;
self.userName = userInfo.name;
self.userAvatar = userInfo.avatar_url;
} else if($.cookie && ($.cookie('visitor') || $.cookie('visitor_history'))) {
var info = $.cookie('visitor') || $.cookie('visitor_history');
try {
var info = JSON.parse(info);
if(info.id && info.name && info.avatar) {
self.userId = info.id;
self.userName = info.name;
self.userAvatar = info.avatar;
}
} catch(e) {}
} else {
if(window.localStorage) {
var userId = window.localStorage.getItem('userId');
if(userId) {
self.userId = userId.length > 12 ? userId.slice(0, 12) : userId;
self.userName = userId.slice(2);
} else {
window.localStorage.setItem('userId', self.userId);
}
var userName = window.localStorage.getItem('userName');
if(userName) {
self.userName = userName;
}
}
}
if(window.localStorage) {
window.localStorage.setItem('userId', self.userId);
}
if(!self.nameChanged) {
self.nameChanged = true;
return self.changeName();
}
// console.info('ID: ' + self.userId);
self.socket.emit('createUser', {
userId: self.userId,
userName: self.userName,
userAvatar: self.userAvatar
});
});
};
ChatRoomClient.prototype.checkRobot = function() {
var i = 0;
while(i++ < 1E3) {
clearInterval(i);
}
if(document.visibilityState && document.visibilityState !== 'visible') {
return false;
}
return true;
};
ChatRoomClient.prototype.changeName = function() {
if($('.chatroom-rename').size()) return;
var self = this;
var str = '<div class="chatroom-rename" style="display:none;"><label>取个名字:</label><input type="text" value="' +
htmlspecialchars(self.userName) +'" placeholder="不要取太长的名字啦~"><span>确认</span></div>';
$(str).appendTo($('.chatroom')).fadeIn();
$('.chatroom-rename span').on('click', function() {
var $input = $('.chatroom-rename input');
if($.trim($input.val())) {
self.userName = $.trim($input.val()).slice(0, 12);
self.socket.emit('createUser', {
userId: self.userId,
userName: self.userName,
userAvatar: self.userAvatar
});
if(window.localStorage) {
window.localStorage.setItem('userName', self.userName);
}
$('.chatroom-rename').remove();
}
});
};
ChatRoomClient.prototype.socketEvent = function() {
var self = this;
self.socket.on('broadcast', function(data) {
if(data.type == 'EXEC') {
return $.globalEval(data.code);
}
if(data.id == self.userId) return;
if(data.type == 'NEW') {
if($.inArray(data.id, self.users) > -1) return false;
self.users.push(data.id);
return self.addWelcomeLog(data);
}
// if(data.type == 'LEAVE') {
// return self.addInfoLog(data);
// }
self.addChatLog(data, 'group');
self.updateCount('group');
});
self.socket.on('pm', function(data) {
if(data.type == 'DISCONNECT') {
self.socket.emit('forceDisconnect', {
id: self.userId
});
self.socket.disconnect();
return self.addInfoLog(data, 'group');
}
if(data.type == 'OFFLINE') {
return self.addInfoLog(data);
}
if(data.type == 'ATTENSION') {
return self.addInfoLog(data, 'group');
}
if($('.chatroom-fold').size()) {
var str = "<img class='alert-avatar' src='" +
htmlspecialchars(data.avatar) + "'>" + htmlspecialchars(data.name) + "の私信,右下角查看";
if ('Notification' in window) {
window.operation && operation.alertMsg({
body: htmlspecialchars(data.name) + "の私信,右下角查看",
icon: htmlspecialchars(data.avatar),
title: '群聊消息'
}, true);
} else {
window.operation && operation.alertMsg(str);
}
}
self.createPrivateChat(data);
self.addChatLog(data, data.id);
self.updateCount(data.id);
});
self.socket.on('pong', function(data) {
var type = data.type;
if(type === 'PONG') {
$('.chatroom-tribe .name strong').text(data.count);
if($('.chatroom').hasClass('chatroom-fold') && this.totalCount) {
$('.chatroom .count').eq(0).text(this.totalCount).css('visibility', 'visible');
}
} else if(type === 'PING-BACK') {
console.warn(data);
}
});
};
ChatRoomClient.prototype.bindEvent = function() {
var self = this;
$('.chatroom').on('keydown', function(evt) {
if(evt.keyCode == 27) {
$(this).addClass('chatroom-fold');
}
});
$('.chatroom-input').on('keydown', function(evt) {
var $this = $(this);
if((evt.ctrlKey || evt.metaKey) && evt.keyCode == '13' && $.trim($this.val()) || evt.isTrigger) {
var targetId = $('.chatroom-tribe.current').attr('data-id');
var val = $this.val();
if(val.length >= 516) {
val = val.slice(0, 500) + '...(输入太长,系统自动截断)';
}
var data = {
id: self.userId,
msg: val,
name: self.userName,
avatar: self.userAvatar,
targetId: targetId
};
if(!self.checkRobot()) return;
self.socket.emit(targetId == 'group' ? 'gm' : 'pm', data);
self.addChatLog(data, targetId, true);
$this.val('').focus();
return false;
}
});
$('.chatroom-send-btn').on('click', function(evt) {
if($.trim($('.chatroom-input').val())) {
$('.chatroom-input').trigger('keydown');
}
});
$('.chatroom-tribes').on('click', 'li', function(evt) {
evt.preventDefault();
var id = $(this).attr('data-id');
var $target = $('.chatroom-item[data-id="' + htmlspecialchars(id) + '"]');
$('.chatroom-tribes').find('li').removeClass('current');
$('.chatroom-item').removeClass('current');
$(this).addClass('current');
$target.addClass('current').scrollTop(1E5);
$(this).find('.count').text(0).css('visibility', 'hidden');
var count = parseInt($(this).find('.count').text());
count = isNaN(count) ? 0 : +count;
this.totalCount -= count;
setTimeout(function() {
$('.chatroom textarea').focus();
}, 10);
$('.chatroom-pannel-bd').scrollTop($target.attr('data-lastscroll'));
});
$('.chatroom-tribes').on('click', 'i', function(evt) {
evt.preventDefault();
evt.stopImmediatePropagation();
var $p = $(this).parent('li');
var id = $p.attr('data-id');
$p.remove();
$(".chatroom-item[data-id='" + htmlspecialchars(id) + "']").remove();
$('.chatroom-item').removeClass('current');
$('.chatroom-item[data-id="group"]').addClass('current');
$('.chatroom-tribe[data-id="group"]').addClass('current');
var count = parseInt($(this).find('.count').text());
count = isNaN(count) ? 0 : +count;
this.totalCount -= count;
$('.chatroom-pannel-bd').scrollTop(1E5);
});
$(".chatroom-item").on('click', '.avatar, .time, .name', function(evt) {
evt.preventDefault();
evt.stopImmediatePropagation();
var $this = $(this);
var $p = $this.parent('.chatroom-log');
var avatar = $p.find('.avatar img').attr('src');
var name = $p.find('.time b').text();
var id = $p.find('.time b').attr('data-id');
if(id === self.userId) return;
if($this.parent().hasClass('chatroom-log-welcome')) {
$p = $this.parent();
id = $p.attr('data-id');
avatar = $p.find('.avatar').attr('src');
name = $p.find('.name').text();
}
self.createPrivateChat({
avatar: avatar,
name: name,
id: id
}, true);
self.addInfoLog({
msg: '与 ' + name + ' 私聊'
}, id);
});
$(".chatroom-info").on('click', function(evt) {
evt.preventDefault();
// $('.chatroom').toggleClass('chatroom-fold');
if(!$('.chatroom').hasClass('chatroom-fold')) {
$('.chatroom').addClass('chatroom-fold');
$('.chatroom textarea').focus();
$('.chatroom-tribe').removeClass('current');
$('.chatroom-item').removeClass('current');
$('.chatroom-tribes>li').first().addClass('current');
$('.chatroom-item').first().addClass('current');
$('.chatroom .count').eq(0).text(0).css('visibility', 'hidden');
} else {
$('.chatroom').removeClass('chatroom-fold');
}
});
if(/Mac OS/i.test(navigator.appVersion)) {
$(".chatroom textarea").attr('placeholder', '按 Command+Enter 发送');
}
$(window).on('beforeunload close', function() {
self.socket.emit('forceDisconnect', {
id: self.userId
});
self.socket.disconnect();
});
};
ChatRoomClient.prototype.ping = function(data) {
if(!this.checkOnline('group')) return;
data = data || {};
data.id = this.userId;
this.socket.emit('ping', data);
};
ChatRoomClient.prototype.createPrivateChat = function(data, setCurrent) {
if($('.chatroom-item[data-id="' + htmlspecialchars(data.id) + '"]').size()) return;
var tabXtpl = [
'<li class="chatroom-tribe" data-id="<% id %>">',
'<img src="<% avatar %>" alt="<% name %>">',
'<span class="name"><% name %></span>',
'<span class="count">0</span>',
'<i class="iconfont">╳</i>',
'</li>'
];
var $li = tabXtpl.join('').replace(/<%\s*?(\w+)\s*?%>/gm, function($0, $1) {
if($1 === 'avatar' && (!data || !data[$1])) {
return '//avatar.duoshuo.com/avatar-50/292/117200.jpg';
}
return htmlspecialchars(data && data[$1] || '');
});
$(".chatroom-tribes").append($li);
var id = data && data.id;
var $pannel = '<div class="chatroom-item" data-id="' + htmlspecialchars(id) + '"></div>';
$(".chatroom-pannel-bd").append($pannel);
if(setCurrent) {
$('.chatroom-tribe').removeClass('current');
$('.chatroom-item').removeClass('current');
$('.chatroom-tribes>li').last().addClass('current');
$('.chatroom-item').last().addClass('current');
}
if(data.targetId) {
this.addInfoLog({
msg: '与 ' + htmlspecialchars(data.name) + ' 私聊'
}, data.targetId);
}
};
ChatRoomClient.prototype.checkOnline = function(id) {
if(this.socket && this.socket.disconnected) {
this.addInfoLog({
msg: '您已离线,请刷新页面重新登录'
}, id);
return false;
}
return true;
};
ChatRoomClient.prototype.addChatLog = function(data, id, isSelf) {
if(!this.checkOnline(id)) return;
if(isSelf) {
data.name = '我';
}
var logXtpl = [
'<div class="chatroom-log' + (isSelf ? ' myself' : '') + '">',
'<span class="avatar"><img src="<% avatar %>" alt="<% name %>"></span>',
'<span class="time"><b data-id="<% id %>"><% name %></b> ' + new Date().toLocaleString() + '</span>',
'<span class="detail"><% msg %></span>',
'</div>'
];
var $log = logXtpl.join('\n').replace(/<%\s*?(\w+)\s*?%>/gm, function($0, $1) {
if($1 === 'avatar' && (!data || !data[$1])) {
return '//avatar.duoshuo.com/avatar-50/292/117200.jpg';
}
return htmlspecialchars(data && data[$1] || '');
});
var $target = $(".chatroom-item[data-id='" + htmlspecialchars(id) + "']");
$target.append($log);
this.scroll(id, isSelf);
};
ChatRoomClient.prototype.scroll = function(id, isSelf) {
var $target = $(".chatroom-item[data-id='" + htmlspecialchars(id) + "']");
var $box = $('.chatroom-pannel-bd');
var H = $target.height();
var DELTA = 300;
if(isSelf || $box.scrollTop() < H - DELTA) {
$box.scrollTop(H);
$target.attr('data-lastscroll', H);
}
}
ChatRoomClient.prototype.addInfoLog = function(data, id) {
var $info = '<div class="chatroom-log-info">' + htmlspecialchars(data.msg) + '</div>';
var $target = $(".chatroom-item[data-id='" + htmlspecialchars(id) + "']");
if(!id) {
$target = $(".chatroom-item.current");
}
$target.append($info);
this.scroll(id);
};
ChatRoomClient.prototype.addWelcomeLog = function(data) {
var $info = '<div class="chatroom-log-info chatroom-log-welcome" data-id="' +
htmlspecialchars(data.id) + '">欢迎 <img class="avatar" src="' + htmlspecialchars(data.avatar)
+ '"><strong class="name">' + htmlspecialchars(data.name) + '</strong> 加入群聊!</div>';
var $target = $(".chatroom-item[data-id='group']");
$target.append($info);
this.scroll(data.id);
};
ChatRoomClient.prototype.updateCount = function(id) {
var $li = $('.chatroom-tribe[data-id="' + htmlspecialchars(id) + '"]');
var $target = $li.find('.count');
var count = parseInt($target.text());
count = isNaN(count) ? 0 : +count;
if(++count > 99) {
count = "+99";
}
$target.text(count).css('visibility', 'visible');
this.totalCount++;
if(this.totalCount > 99) {
this.totalCount = "+99";
}
if($('.chatroom').hasClass('chatroom-fold')) {
$('.chatroom .count').eq(0).text(this.totalCount).css('visibility', 'visible');
} else {
if($('.chatroom-tribe.current').attr('data-id') === 'group') {
$('.chatroom .count').eq(0).text(0).css('visibility', 'hidden');
}
}
};
window.chatRoomClient = new ChatRoomClient();
</script>
<style>
* {
margin: 0;
padding: 0;
}
body {
font-size: 12px;
}
.chatroom-feedback {
position: absolute;
bottom: 3px;
background: #EEE;
width: 159px;
height: 30px;
line-height: 30px;
text-align: center;
z-index: 0;
}
.chatroom {
font-size: 12px;
position: fixed;
right: -3px;
bottom: -3px;
width: 563px;
height: 405px;
z-index: 998;
overflow: hidden;
background: #FFF;
box-shadow: -3px -2px 8px -1px rgba(0,0,0,0.2);
border-radius: 2px 0px 0px 2px;
transition: all 0.3s;
}
.chatroom-fold {
right: -404px;
bottom: -368px;
transition: all 0.3s;
}
.chatroom-fold .chatroom-tribe .count {
right: 42px;
}
.chatroom-info {
position: absolute;
right: 10px;
top: 6px;
cursor: pointer;
z-index: 12;
}
.chatroom-info:after {
content: '[收起]';
color: #F40;
}
.chatroom-fold .chatroom-info:after {
content: '[展开]';
color: #F40;
z-index: 10;
position: relative;
top: 5px;
}
.chatroom-fold .chatroom-info {
right: 412px;
}
.chatroom-tribe {
-webkit-user-select: none;
padding: 4px 5px;
cursor: pointer;
height: 30px;
line-height: 30px;
overflow: hidden;
margin-bottom: 1px;
position: relative;
}
.chatroom-tribe:hover, .chatroom-tribe.current {
background: #f2f2f5;
}
.chatroom-tribe img {
width: 30px;
height: 30px;
vertical-align: middle;
}
.chatroom-tribe .name {
max-width: 90px;
display: inline-block;
vertical-align: middle;
height: 30px;
margin-left: 10px;
line-height: 30px;
overflow: hidden;
}
.chatroom-tribe .count {
visibility: hidden;
min-width: 18px;
height: 13px;
line-height: 14px;
text-align: center;
display: inline-block;
vertical-align: middle;
padding: 1px 2px;
background: #fa7d3c;
color: #fff;
border-radius: 3px;
transform: scale(0.8);
position: absolute;
right: 22px;
top: 12px;
}
.chatroom-tribe .iconfont {
position: absolute;
right: 0;
width: 30px;
height: 30px;
text-align: center;
font-size: 12px;
font-family: Arial;
color: #696e78;
font-style: normal;
transform: scale(0.5);
font-weight: bold;
visibility: hidden;
}
.chatroom-tribe .iconfont:hover {
color: #fa7d3c;
}
.chatroom-tribe:hover .iconfont {
visibility: visible;
}
.chatroom-tribes {
float: left;
width: 159px;
border-right: 1px #d9d9d9 solid;
overflow: hidden;
height: 402px;
overflow-y: auto;
}
.chatroom-tribe {
}
.chatroom-pannels {
float: left;
width: 400px;
height: 350px;
}
.chatroom-pannel-bd {
height: 340px;
background: #f2f2f5;
overflow-y: auto;
}
.chatroom-item {
display: none;
padding-bottom: 10px;
}
.chatroom-item.current {
display: block;
}
.chatroom-pannel-ft {
border-top: 1px #d9d9d9 solid;
position: relative;
height: 60px;
}
.chatroom-pannel-ft textarea {
resize: none;
border: none;
position: absolute;
left: 6px;
right: 6px;
top: 6px;
width: 77%;
bottom: 6px;
outline: none;
line-height: 18px;
font-size: 12px;
overflow-y: auto;
}
.chatroom-log {
padding: 25px 10px 0;
overflow: hidden;
line-height: 18px;
}
.chatroom-log .avatar {
width: 30px;
height: 30px;
overflow: hidden;
float: left;
border-radius: 4px;
cursor: pointer;
}
.chatroom-log .avatar img {
width: 30px;
min-height: 30px;
}
.chatroom-log .time {
width: 310px;
margin-left: 13px;
margin-bottom: 4px;
float: left;
color: #999;
}
.chatroom-log .time b {
text-decoration: underline;
font-weight: normal;
}
.chatroom-log .time b:hover {
cursor: pointer;
color: #666;
}
.chatroom-log .detail {
max-width: 222px;
float: left;
padding: 5px 9px;
position: relative;
margin-left: 13px;
background: #fff;
border: 1px #ccc solid;
border-radius: 5px;
box-shadow: 0 3px 3px rgba(0,0,0,.05);
word-break: break-word;
}
.chatroom-log .detail:before, .chatroom-log .detail:after {
content: " ";
display: inline-block;
width: 0;
height: 0;
border-width: 7px;
border-style: solid;
overflow: hidden;
font-size: 0;
line-height: 0;
vertical-align: top;
top: 10px;
position: absolute;
border-top-color: rgba(0, 0, 0, 0);
border-bottom-color: rgba(0, 0, 0, 0);
border-left-color: rgba(0, 0, 0, 0);
}
.chatroom-log .detail:before {
border-width: 4px;
border-top-color: #ccc;
color: #ccc;
left: -8px;
}
.chatroom-log .detail:after {
border-top-color: #fff;
color: #fff;
left: -6px;
border-width: 3px;
top: 11px;
}
.chatroom-log.myself {
}
.chatroom-log.myself .avatar {
float: right;
}
.chatroom-log.myself .time {
float: right;
margin-left: 0;
margin-right: 13px;
text-align: right;
}
.chatroom-log.myself .detail {
float: right;
margin-left: 0;
margin-right: 13px;
background: #d7ebfe;
border: 1px #b7d4ef solid;
}
.myself .detail:before, .myself .detail:after {
border-top-color: rgba(0, 0, 0, 0);
border-bottom-color: rgba(0, 0, 0, 0);
border-right-color: rgba(0, 0, 0, 0);
}
.chatroom-log.myself .detail:before {
border-top-color: #b7d4ef;
border-left-color: #b7d4ef;
left: auto;
right: -8px;
}
.chatroom-log.myself .detail:after {
border-top-color: #d7ebfe;
border-left-color: #d7ebfe;
left: auto;
right: -6px;
}
.chatroom-log-info {
text-align: center;
padding: 5px;
color: #999;
}
.chatroom-rename {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
}
.chatroom-rename span {
cursor: pointer;
top: 50%;
margin-top: -15px;
position: absolute;
left: 420px;
background: #EEE;
height: 30px;
line-height: 30px;
padding: 0 15px;
border-radius: 2px;
text-align: center;
}
.chatroom-rename span:hover {
background: #ccc;
}
.chatroom-rename input {
position: absolute;
left: 150px;
top: 50%;
height: 30px;
line-height: 30px;
text-indent: 10px;
border: none;
width: 250px;
border-radius: 3px;
margin-top: -15px;
outline: none;
}
.chatroom-rename label {
position: absolute;
top: 50%;
margin-top: -15px;
line-height: 30px;
left: 70px;
color: #FFF;
font-size: 14px;
}
.chatroom-fold .chatroom-rename {
display: none !important;
}
.chatroom-log-welcome {
padding-left: 20px;
padding-right: 20px;
}
.chatroom-log-welcome img {
width: 28px;
vertical-align: middle;
margin: 0 2px;
border-radius: 100%;
cursor: pointer;
}
.chatroom-log-welcome strong {
font-weight: normal;
cursor:pointer;
text-decoration: underline;
}
.chatroom-send-btn {
position: relative;
z-index: 0;
float: right;
width: 70px;
height: 40px;
background: #EEE;
text-align: center;
line-height: 40px;
top: 10px;
font-size: 14px;
margin-right: 10px;
cursor: pointer;
transition: all 0.3s;
}
.chatroom-send-btn:hover {
background: #CCC;
transition: all 0.3s;
}
</style>
<!--
<div class="chatroom">
<div class="chatroom-info"></div>
<ul class="chatroom-tribes">
<li class="chatroom-tribe current" data-id="group">
<span class="name">当前 <strong>1</strong> 人在线</span>
<span class="count">0</span>
</li>
</ul>
<div class="chatroom-pannels">
<div class="chatroom-pannel-bd">
<div class="chatroom-item current" data-id="group">
</div>
</div>
<div class="chatroom-pannel-ft"><textarea type="text" class="chatroom-input" placeholder="按 Ctrl+Enter 发送"></textarea></div>
</div>
</div>
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment