标签:
最近研究了一下WebIM,现将学习笔记记录于此。
本篇实现的WebIM是对现有技术的整合,它包含了如下技术:
seajs:用于JavaScript模块化编程,seajs简介及用途可以看这儿:http://blog.csdn.net/fengshuiyue/article/details/51177458
layim:阿里大牛贤心制作的一款webim聊天界面,很美观,源码下载地为http://sentsin.com/layui/layim/
JsJac:基于jabber/xmpp的javascript实现,在此完成与后台servlet(JabberHttpBind)的数据交互。下载地址为https://github.com/sstrigler/JSJaC/
2)后台:
JabberHTTPBind:用于和Openfire进行通信,下载地址为http://stefan-strigler.de/jhb/
3)服务器:
openfire:开源的聊天服务器
1)webIM采用的是标准通信协议XMPP(Extensible Messageing and Presence Protocol:可扩展消息与存在协议),它目前是主流的四种IM(IM:instant messaging,即时消息)协议之一,其他三种分别为:即时信息和空间协议(IMPP)、空间和即时信息协议(PRIM)、针对即时通讯和空间平衡扩充的进程开始协议SIP(SIMPLE)。
2)xmpp是采用TCP进行传输的xml流,这不同于QQ的方式(QQ是用二进制流进行传输的)、不同于MSN的方式(MSN采用的是纯文本指令加空格加参数加换行苻的方式),下图是传输XMPP经过的协议栈:
3)xmpp地址格式:
一个实体在xmpp网络结构中被称为一个接点,它有唯一的标示符jabber identifier(JID),即实体地址,用来表示一个Jabber用户,但是也可以表示其他内容,例如一个聊天室。一个有效的JID包括下列元素:
域名(domain identifier);
节点(node identifier);
源(resource identifier);
JID完整格式: node@domain/resource
4)xmpp格式
xmpp中定义了3个顶层XML元素:
Message:用于在两个jabber用户之间发送信息
Presence:用来表明用户的状态
IQ:一种请求/响应机制,从一个实体从发送请求,另外一个实体接受请求,并进行响应.例如,client在stream的上下文中插入一个元素,向Server请求得到自己的好友列表,Server返回一个里面是请求的结果.
① Message实例
发送端发送的信息:
<?xml version="1.0" encoding="utf-8"?>
<body xmlns="http://jabber.org/protocol/httpbind" rid="424681" sid="Okig9EHLCTA15a-BZ3NRDqOW" key="08ca4f4beeaa588cd170d700fa78a01c8d469df1">
<message xmlns="jabber:client"
to="webchattest@openfire402/webim"
type="chat">
<body>您好,webchattest</body>
</message>
</body>
接收端接收到的信息:
<!--webchattest接收到的admin的离线信息 -->
<?xml version="1.0" encoding="utf-8"?>
<body xmlns="http://jabber.org/protocol/httpbind">
<message xmlns="jabber:client"
from="admin@openfire402/webim"
to="webchattest@openfire402/webim"
type="chat">
<body>您好,webchattest</body>
<delay xmlns="urn:xmpp:delay" from="openfire402" stamp="2016-06-11T04:52:23.153Z"></delay>
</message>
</body>
<!--webchattest在线时接收到的admin发送的信息 -->
<?xml version="1.0" encoding="utf-8"?>
<body xmlns="http://jabber.org/protocol/httpbind">
<message xmlns="jabber:client"
from="admin@openfire402/webim"
to="webchattest@openfire402/webim"
type="chat">
<body>webchattest上线后admin发的信息</body>
</message>
</body>
message属性介绍:
from属性:设置消息发送方自身的FullJID(node@domain/resource)
to属性:设置消息接收方的Bare JID(node@domain),通常第一次发送方无法确知接收方的Full JID,通过服务器中转路由时由服务器根据Base JID映射接收方的Full JID;但如果这个消息是在回复之前接收到的消息,则to属性应该包含对方完整的Full JID;如此设计的好处在于:当to属性设定为Full JID时可以帮助服务器省却了接收者资源定位(接入定位),在一个IM服务集群环境中这种定位通常意味着一次分布式缓存读取操作。
type属性:XMPP约定了type的枚举值,包括:
- chat:表明在一个点对点会话环境中的聊天消息。
- groupchat:表明在一个多人会话环境中的聊天消息。
- headline: 通常一些系统通知、警告、实时数据更新采用此类型,这类消息不期待客户端回复或响应,具有很高的实时性,不需要离线存储。
- normal: 默认的消息类型(缺乏type属性时),通常表达一种要求接收方必须确认的消息,一般用于系统提示强制用户确认或取消等。
- error: 表示一个错误消息,可能由服务端发送给客户端,也可能是另一个客户接收端回应给客户发送端,此类消息也不需要离线存储。
message子元素:
<subject>表明一个消息主题,通常客户端实现显示在聊天窗口标题栏处
<body>消息内容部分
<subject>和<subject>都允许包含多个元素标签,不同的标签根据xml:lang表达了不同的语言(XMPP可是一个国际化协议)
下面这是完整的message消息格式:
<message
from=‘node@domain/resource‘
to=‘node@domain‘
type=‘chat‘
xml:lang=‘en‘>
<subject>hello!</subject>
<subject xml:lang=‘zh‘>
你好
</subject>
<body>welcome to meet you</body>
<body xml:lang=‘zh‘>
很高兴认识你
</body>
</message>
② Presence
用来表明用户的状态,如:online、away、dnd(请勿打扰)等。当用户离线或改变自己的状态时,就会在stream的上下文中插入一个Presence元素,来表明自身的状态.结构如下所示:
<presence>
from=‘node@domain/resource‘
to=‘node@domain‘
<status>online</status>
</presence>
presence 元素可以取下面几种值:
③ IQ
一种请求/响应机制,从一个实体从发送请求,另外一个实体接受请求,并进行响应.例如,client在stream的上下文中插入一个元素,向Server请求得到自己的好友列表,Server返回一个里面是请求的结果。结构如下所示:
<iq xmlns="jabber:client"
from="openfire402"
id="537-29"
to="webchattest@openfire402/webim"
type="get">
<ping xmlns="urn:xmpp:ping"></ping>
</iq>
IQ 主要的属性是type。包括:
本文采用的是openfire4.0.2版本的,openfire的安装配置简单记录如下:
1)在官网http://www.igniterealtime.org/downloads/index.jsp#openfire 下载openfire,安装即可
2)在“openfire安装目录/bin”目录下运行openfired.exe程序,运行效果如下图:
3)第一次登陆http://localhost:9090/ 会进入openfire配置页面,基本上使用默认值即可,下面介绍需要注意的几个地方
a.服务器设置
需要注意的是 域 的设置,这块儿会关系到 JID(node@domain/resource) domain的取值
b.数据库设置
如果选择 标准数据库连接 ,则需要导入openfire的sql文件,该文件存储在:openfire安装目录\resources\database 目录下,如下图:
由于使用的都是现成的技术,故本篇的webIM重在前端的编程
该类采用了该博主(http://www.cnblogs.com/hoojo)js端 通信类,该类定义如下:
define(function(require, exports,module){
require(‘../base_module/jsjac/jsjac.js‘);
require(‘../base_module/jquery-1.11.1.min.js‘);
var util = require(‘../util/util.js‘);
var
chatConnConfig = {
httpbase: ‘‘,//window.contextPath + "/JHB/", //请求后台http-bind服务器url
domain: ‘‘, //window["serverDomin"], //"192.168.5.231", // 192.168.5.231 当前有效域名
username: "",
pass: "",
timerval: 2000, // 设置请求超时
resource: "webim", // 链接资源标识
register: false // 是否注册
},
chatObj = null, //聊天实体
//writeReceiveMessage = null;
chatConnection = {},
onlineStatus = {
available: ‘在线‘,
chat: ‘欢迎聊天‘,
away: ‘离开‘,
xa: ‘不可用‘,
dnd: ‘请勿打扰‘,
invisible: ‘隐身‘,
unavailable: ‘离线‘
};
chatConnection.connected = function(){
}
/**
* chatConnCfg = {
* httpbase: ‘‘,//window.contextPath + "/JHB/", //请求后台http-bind服务器url
domain: ‘‘, //window["serverDomin"], //"192.168.5.231", // 192.168.5.231 当前有效域名
timerval: 2000, // 设置请求超时
resource: ‘‘
}
*/
init = function(chatConnCfg,chatobjparam){
chatObj = chatobjparam;
//chatConnConfig.oDbg = new JSJaCConsoleLogger(4);
if(chatConnCfg){
$.extend(chatConnConfig, chatConnConfig, chatConnCfg);
//chatConnConfig.httpbase = chatConnCfg.httpbase;
//chatConnConfig.domain = chatConnCfg.domain;
//chatConnConfig.resource = chatConnCfg.resource;
//chatConnConfig.timerval = chatConnCfg.timerval;
//chatConnConfig.writeReceiveMessage = chatConnCfg.writeReceiveMessage;
}
// Debugger plugin
if (typeof (Debugger) == "function") {
chatConnConfig.oDbg = new Debugger(2, chatConnConfig.resource);
chatConnConfig.oDbg.start();
} else {
chatConnConfig.oDbg = function () {
};
chatConnConfig.oDbg.log = function () {
};
}
try {
if (JSJaCCookie.read("btype").getValue() == "binding") {
chatConnection = new JSJaCHttpBindingConnection({ "oDbg": chatConnConfig.oDbg});
//rdbgerjac.chat.setupEvent(remote.connection);
setupEvent(chatConnection)
if (chatConnection.resume()) {
}
}
} catch (e) {
} // reading cookie failed - never mind
}
/**
* loginPerson为对象
* loginPerson = {
username:‘‘,
password:‘‘,
register:false //是否注册改用户
}
*/
login = function (loginPerson) {
try {
// 链接参数
var connectionConfig = chatConnConfig;
// Debugger console
if (typeof (oDbg) != "undefined") {
connectionConfig.oDbg = oDbg;
}
chatConnection = new JSJaCHttpBindingConnection(connectionConfig);
// 安装(注册)Connection事件模型
setupEvent(chatConnection);
// setup args for connect method
if (loginPerson) {
connectionConfig.username = loginPerson.username;
connectionConfig.pass = loginPerson.password;
connectionConfig.register = loginPerson.register;
}
// 连接服务器
chatConnection.connect(connectionConfig);
changeStatus("away", "away", 1, "chat");
} catch (e) {
//登录错误信息显示
alert(e.toString());
} finally {
return false;
}
},
setupEvent = function (con) {
con.registerHandler(‘message‘, handleMessage);
con.registerHandler(‘presence‘, handlePresence);
con.registerHandler(‘iq‘, handleIQ);
con.registerHandler(‘onconnect‘, handleConnected);
con.registerHandler(‘onerror‘, handleError);
con.registerHandler(‘status_changed‘, handleStatusChanged);
con.registerHandler(‘ondisconnect‘, handleDisconnected);
con.registerIQGet(‘query‘, NS_VERSION, handleIqVersion);
con.registerIQGet(‘query‘, NS_TIME, handleIqTime);
},
// 改变用户状态
changeStatus = function (type, status, priority, show) {
type = type || "unavailable";
status = status || "online";
priority = priority || "1";
show = show || "chat";
var presence = new JSJaCPresence();
presence.setType(type); // unavailable invisible
presence.setStatus(status); // online
presence.setPriority(priority); // 1
presence.setShow(show); // chat
if (chatConnection) {
chatConnection.send(presence);
}
},
// 发送远程消息
sendMessage = function (msg, to) {
try {
if (msg == "") {
return false;
}
var tosendstr = "";
if (to) {
tosendstr= to + ‘@‘ + chatConnConfig.domain
+ ‘/‘ +chatConnConfig.resource;
} else {
// 向chat接收信息区域写消息
if (chatObj.writeReceiveMessage) {
// var html = "你没有指定发送者的名称";
// var msgobj = {
// receiveType:‘noSendUser‘,
// objtime:‘‘,
// objname:‘‘,
// objface:‘‘,
// content:html
// };
// chatObj.writeReceiveMessage(msgobj);
}
return false;
}
// 构建jsjac的message对象
var message = new JSJaCMessage();
message.setTo(new JSJaCJID(tosendstr));
message.setType("chat"); // 单独聊天,默认为广播模式
message.setBody(msg);
// 发送消息
chatConnection.send(message);
return false;
} catch (e) {
//var html = "<div class=‘msg error‘‘>Error: " + e.message + "</div>";
alert(e.message);
return false;
}
},
// 退出、断开链接
logout = function () {
var presence = new JSJaCPresence();
presence.setType("unavailable");
if (chatConnection) {
chatConnection.send(presence);
chatConnection.disconnect();
}
},
errorHandler = function (event) {
var e = event || window.event;
//alert(e);显示错误信息
if (chatConnection && chatConnection.connected()) {
chatConnection.disconnect();
}
return false;
},
unloadHandler = function () {
var con = chatConnection;
if (typeof con != "undefined" && con && con.connected()) {
// save backend type
if (con._hold) { // must be binding
(new JSJaCCookie("btype", "binding")).write();
}
if (con.suspend) {
con.suspend();
}
}
},
// 重新连接服务器
reconnection = function () {
chatConnConfig.register = false;
if (chatConnection.connected()) {
chatConnection.disconnect();
}
login();
}
/* ########################### Handler Event ############################# */
handleIQ = function (aIQ) {
chatConnection.send(aIQ.errorReply(ERR_FEATURE_NOT_IMPLEMENTED));
var content = aIQ.xml().htmlEnc();
var nowstr = util.getDatetime();
var msgobj = {
receiveType:‘handleIQ‘,
objtime:nowstr,
objname:‘‘,
objface:‘‘,
content:content
};
chatObj.writeReceiveMessage(msgobj);
},
handleMessage = function (aJSJaCPacket) {
var user = aJSJaCPacket.getFromJID().toString();
var userName = user.split("@")[0];
var content = aJSJaCPacket.getBody();
var nowstr = util.getDatetime();
var msgobj = {
receiveType:‘handleMessage‘,
objtime:nowstr,
objname:userName,
objface:‘‘,
content:content
};
if(content == undefined || content == null || content == ‘‘){
}else{
chatObj.writeReceiveMessage(msgobj);
}
},
handlePresence = function (aJSJaCPacket) {
var user = aJSJaCPacket.getFromJID();
var userName = user.toString().split("@")[0];
var status = ‘‘;
var html = "<div class=\"msg\">";
if (!aJSJaCPacket.getType() && !aJSJaCPacket.getShow()) {
html += "<b>" + userName + " 上线了.</b>";
} else {
html += "<b>" + userName + " 设置 presence 为: ";
if (aJSJaCPacket.getType()) {
html += aJSJaCPacket.getType() + ".</b>";
} else {
html += aJSJaCPacket.getShow() + ".</b>";
}
if (aJSJaCPacket.getStatus()) {
html += " (" + aJSJaCPacket.getStatus().htmlEnc() + ")";
}
}
html += "</div>";
// 向chat接收信息区域写消息
var nowstr = util.getDatetime();
var msgobj = {
receiveType:‘handlePresence‘,
objtime:nowstr,
objname:userName,
objface:‘‘,
content:html
};
chatObj.writeReceiveMessage(msgobj);
},
handleError = function (event) {
var e = event || window.event;
var html = "An error occured:<br />"
+ ("Code: " + e.getAttribute("code")
+ "\nType: " + e.getAttribute("type")
+ "\nCondition: " + e.firstChild.nodeName).htmlEnc();
var content = "";
switch (e.getAttribute("code")) {
case "401":
content = "登陆验证失败!";
break;
// 当注册发现重复,表明该用户已经注册,那么直接进行登陆操作
case "409":
//content = "注册失败!\n\n请换一个用户名!";
chatConnection.reconnection();
break;
case "503":
content = "无法连接到IM服务器,请检查相关配置!";
break;
case "500":
var content = "服务器内部错误!\n\n连接断开!<br/><a href=‘javascript: self.parent.remote.jsjac.chat.reconnection();‘>重新连接</a>";
// 向chat接收信息区域写消息
break;
default:
break;
}
if (content) {
var nowstr = util.getDatetime();
var msgobj = {
receiveType:‘handleError‘,
objtime:nowstr,
objname:chatConnConfig.username,
objface:‘‘,
content:content
};
chatObj.writeReceiveMessage(msgobj);
}
if (chatConnection.connected()) {
chatConnection.disconnect();
}
},
// 状态变化触发事件
handleStatusChanged = function (status) {
//remote.console.info("<div>当前用户状态: " + status + "</div>");
//remote.dbger.log("当前用户状态: " + status);
if (status == "disconnecting") {
var html = "<b style=‘color:red;‘>你离线了!</b>";
// 向chat接收信息区域写消息
var nowstr = util.getDatetime();
var msgobj = {
receiveType:‘handleStatusChanged‘,
objtime:nowstr,
objname:chatConnConfig.username,
objface:‘‘,
content:html
};
chatObj.writeReceiveMessage(msgobj);
}
},
// 建立链接触发事件方法
handleConnected = function () {
chatConnection.send(new JSJaCPresence());
},
// 断开链接触发事件方法
handleDisconnected = function () {
},
handleIqVersion = function (iq) {
chatConnection.send(iq.reply([
iq.buildNode("name", chatConnConfig.resource),
iq.buildNode("version", JSJaC.Version),
iq.buildNode("os", navigator.userAgent)
]));
return true;
},
handleIqTime = function (iq) {
var now = new Date();
chatConnection.send(iq.reply([
iq.buildNode("display", now.toLocaleString()),
iq.buildNode("utc", now.jabberDate()),
iq.buildNode("tz", now.toLocaleString().substring(now.toLocaleString().lastIndexOf(" ") + 1))
]));
return true;
}
//exports.writeReceiveMessage = writeReceiveMessage;
exports.init = init;
exports.login = login;
exports.sendMessage = sendMessage;
exports.changeStatus = changeStatus;
exports.logout = logout;
exports.chatObj = chatObj;
$(window).bind({
unload: unloadHandler,
error: errorHandler,
beforeunload: logout
});
});
define(function(require, exports,module){
//require(‘../base_module/lay/lib.js‘);
require(‘../base_module/jquery-1.11.1.min.js‘);
require(‘../base_module/lay/layer/layer.min.js‘);
serverChat = require(‘./serverchat.js‘);
var util = require(‘../util/util.js‘);
var config = {
msgurl: ‘私信地址‘,
chatlogurl: ‘聊天记录url前缀‘,
aniTime: 200,
right: -232,
api: {
friend: ‘js/layim/friend.json‘, //好友列表接口
group: ‘js/layim/group.json‘, //群组列表接口
chatlog: ‘js/layim/chatlog.json‘, //聊天记录接口
groups: ‘js/layim/groups.json‘, //群组成员接口
sendurl: ‘‘ //发送消息接口
},
user: { //当前用户信息
name: ‘测试用户‘,
face: ‘http://tp3.sinaimg.cn/3850354634/180/40048989275/0‘
},
//自动回复内置文案,也可动态读取数据库配置
autoReplay: [
‘您好,我现在有事不在,一会再和您联系。‘,
‘你没发错吧?‘,
‘洗澡中,请勿打扰,偷窥请购票,个体四十,团体八折,订票电话:一般人我不告诉他!‘,
‘你好,我是主人的美女秘书,有什么事就跟我说吧,等他回来我会转告他的。‘,
‘我正在拉磨,没法招呼您,因为我们家毛驴去动物保护协会把我告了,说我剥夺它休产假的权利。‘,
‘<(@ ̄︶ ̄@)>‘,
‘你要和我说话?你真的要和我说话?你确定自己想说吗?你一定非说不可吗?那你说吧,这是自动回复。‘,
‘主人正在开机自检,键盘鼠标看好机会出去凉快去了,我是他的电冰箱,我打字比较慢,你慢慢说,别急……‘,
‘(*^__^*) 嘻嘻,是贤心吗?‘
],
chating: {},
hosts: (function(){
var dk = location.href.match(/\:\d+/);
dk = dk ? dk[0] : ‘‘;
return ‘http://‘ + document.domain + dk + ‘/‘;
})(),
json: function(url, data, callback, error){
return $.ajax({
type: ‘POST‘,
url: url,
data: data,
dataType: ‘json‘,
success: callback,
error: error
});
},
stopMP: function(e){
e ? e.stopPropagation() : e.cancelBubble = true;
}
}, dom = [$(window), $(document), $(‘html‘), $(‘body‘)], xxim = {};
/**
* obj = {
receiveType: //handleIQ,handleMessage,handlePresence,handleError,handleStatusChanged
content:
}
*/
//主界面tab
xxim.tabs = function(index){
var node = xxim.node;
node.tabs.eq(index).addClass(‘xxim_tabnow‘).siblings().removeClass(‘xxim_tabnow‘);
node.list.eq(index).show().siblings(‘.xxim_list‘).hide();
if(node.list.eq(index).find(‘li‘).length === 0){
xxim.getDates(index);
}
};
//节点
xxim.renode = function(){
var node = xxim.node = {
tabs: $(‘#xxim_tabs>span‘),
list: $(‘.xxim_list‘),
online: $(‘.xxim_online‘),
setonline: $(‘.xxim_setonline‘),
onlinetex: $(‘#xxim_onlinetex‘),
xximon: $(‘#xxim_on‘),
layimFooter: $(‘#xxim_bottom‘),
xximHide: $(‘#xxim_hide‘),
xximSearch: $(‘#xxim_searchkey‘),
searchMian: $(‘#xxim_searchmain‘),
closeSearch: $(‘#xxim_closesearch‘),
layimMin: $(‘#layim_min‘)
};
};
//主界面缩放
xxim.expend = function(){
var node = xxim.node;
if(xxim.layimNode.attr(‘state‘) !== ‘1‘){
xxim.layimNode.stop().animate({right: config.right}, config.aniTime, function(){
node.xximon.addClass(‘xxim_off‘);
try{
localStorage.layimState = 1;
}catch(e){}
xxim.layimNode.attr({state: 1});
node.layimFooter.addClass(‘xxim_expend‘).stop().animate({marginLeft: config.right}, config.aniTime/2);
node.xximHide.addClass(‘xxim_show‘);
});
} else {
xxim.layimNode.stop().animate({right: 1}, config.aniTime, function(){
node.xximon.removeClass(‘xxim_off‘);
try{
localStorage.layimState = 2;
}catch(e){}
xxim.layimNode.removeAttr(‘state‘);
node.layimFooter.removeClass(‘xxim_expend‘);
node.xximHide.removeClass(‘xxim_show‘);
});
node.layimFooter.stop().animate({marginLeft: 0}, config.aniTime);
}
};
//初始化窗口格局
xxim.layinit = function(){
var node = xxim.node;
//主界面
try{
if(!localStorage.layimState){
config.aniTime = 0;
localStorage.layimState = 1;
}
if(localStorage.layimState === ‘1‘){
xxim.layimNode.attr({state: 1}).css({right: config.right});
node.xximon.addClass(‘xxim_off‘);
node.layimFooter.addClass(‘xxim_expend‘).css({marginLeft: config.right});
node.xximHide.addClass(‘xxim_show‘);
}
}catch(e){
layer.msg(e.message, 5, -1);
}
};
//聊天窗口
xxim.popchat = function(param){
var node = xxim.node, log = {};
log.success = function(layero){
layer.setMove();
xxim.chatbox = layero.find(‘#layim_chatbox‘);
log.chatlist = xxim.chatbox.find(‘.layim_chatmore>ul‘);
log.chatlist.html(‘<li data-id="‘+ param.id +‘" type="‘+ param.type +‘" id="layim_user‘+ param.type + param.id +‘"><span>‘+ param.name +‘</span><em>×</em></li>‘)
xxim.tabchat(param, xxim.chatbox);
//最小化聊天窗
xxim.chatbox.find(‘.layer_setmin‘).on(‘click‘, function(){
var indexs = layero.attr(‘times‘);
layero.hide();
node.layimMin.text(xxim.nowchat.name).show();
});
//关闭窗口
xxim.chatbox.find(‘.layim_close‘).on(‘click‘, function(){
var indexs = layero.attr(‘times‘);
layer.close(indexs);
xxim.chatbox = null;
config.chating = {};
config.chatings = 0;
});
//关闭某个聊天
log.chatlist.on(‘mouseenter‘, ‘li‘, function(){
$(this).find(‘em‘).show();
}).on(‘mouseleave‘, ‘li‘, function(){
$(this).find(‘em‘).hide();
});
log.chatlist.on(‘click‘, ‘li em‘, function(e){
var parents = $(this).parent(), dataType = parents.attr(‘type‘);
var dataId = parents.attr(‘data-id‘), index = parents.index();
var chatlist = log.chatlist.find(‘li‘), indexs;
config.stopMP(e);
delete config.chating[dataType + dataId];
config.chatings--;
parents.remove();
$(‘#layim_area‘+ dataType + dataId).remove();
if(dataType === ‘group‘){
$(‘#layim_group‘+ dataType + dataId).remove();
}
if(parents.hasClass(‘layim_chatnow‘)){
if(index === config.chatings){
indexs = index - 1;
} else {
indexs = index + 1;
}
xxim.tabchat(config.chating[chatlist.eq(indexs).attr(‘type‘) + chatlist.eq(indexs).attr(‘data-id‘)]);
}
if(log.chatlist.find(‘li‘).length === 1){
log.chatlist.parent().hide();
}
});
//聊天选项卡
log.chatlist.on(‘click‘, ‘li‘, function(){
var othis = $(this), dataType = othis.attr(‘type‘), dataId = othis.attr(‘data-id‘);
xxim.tabchat(config.chating[dataType + dataId]);
});
//发送热键切换
log.sendType = $(‘#layim_sendtype‘), log.sendTypes = log.sendType.find(‘span‘);
$(‘#layim_enter‘).on(‘click‘, function(e){
config.stopMP(e);
log.sendType.show();
});
log.sendTypes.on(‘click‘, function(){
log.sendTypes.find(‘i‘).text(‘‘)
$(this).find(‘i‘).text(‘√‘);
});
xxim.transmit();
};
log.html = ‘<div class="layim_chatbox" id="layim_chatbox">‘
+‘<h6>‘
+‘<span class="layim_move"></span>‘
+‘ <a href="‘+ param.url +‘" class="layim_face" target="_blank"><img src="‘+ param.face +‘" ></a>‘
+‘ <a href="‘+ param.url +‘" class="layim_names" target="_blank">‘+ param.name +‘</a>‘
+‘ <span class="layim_rightbtn">‘
+‘ <i class="layer_setmin"></i>‘
+‘ <i class="layim_close"></i>‘
+‘ </span>‘
+‘</h6>‘
+‘<div class="layim_chatmore" id="layim_chatmore">‘
+‘ <ul class="layim_chatlist"></ul>‘
+‘</div>‘
+‘<div class="layim_groups" id="layim_groups"></div>‘
+‘<div class="layim_chat">‘
+‘ <div class="layim_chatarea" id="layim_chatarea">‘
+‘ <ul class="layim_chatview layim_chatthis" id="layim_area‘+ param.type + param.id +‘"></ul>‘
+‘ </div>‘
+‘ <div class="layim_tool">‘
+‘ <i class="layim_addface" title="发送表情"></i>‘
+‘ <a href="javascript:;"><i class="layim_addimage" title="上传图片"></i></a>‘
//+‘ <a href="javascript:;"><i class="layim_addfile" title="上传附件"></i></a>‘
+‘ <a href="" target="_blank" class="layim_seechatlog"><i></i>聊天记录</a>‘
+‘ </div>‘
+‘ <textarea class="layim_write" id="layim_write"></textarea>‘
+‘ <div class="layim_send">‘
+‘ <div class="layim_sendbtn" id="layim_sendbtn">发送<span class="layim_enter" id="layim_enter"><em class="layim_zero"></em></span></div>‘
+‘ <div class="layim_sendtype" id="layim_sendtype">‘
+‘ <span><i>√</i>按Enter键发送</span>‘
+‘ <span><i></i>按Ctrl+Enter键发送</span>‘
+‘ </div>‘
+‘ </div>‘
+‘</div>‘
+‘</div>‘;
if(config.chatings < 1){
$.layer({
type: 1,
border: [0],
title: false,
shade: [0],
area: [‘620px‘, ‘493px‘],
move: [‘.layim_chatbox .layim_move‘, true],
moveType: 1,
closeBtn: false,
offset: [(($(window).height() - 493)/2)+‘px‘, ‘‘],
page: {
html: log.html
}, success: function(layero){
log.success(layero);
}
})
} else {
log.chatmore = xxim.chatbox.find(‘#layim_chatmore‘);
log.chatarea = xxim.chatbox.find(‘#layim_chatarea‘);
log.chatmore.show();
log.chatmore.find(‘ul>li‘).removeClass(‘layim_chatnow‘);
log.chatmore.find(‘ul‘).append(‘<li data-id="‘+ param.id +‘" type="‘+ param.type +‘" id="layim_user‘+ param.type + param.id +‘" class="layim_chatnow"><span>‘+ param.name +‘</span><em>×</em></li>‘);
log.chatarea.find(‘.layim_chatview‘).removeClass(‘layim_chatthis‘);
log.chatarea.append(‘<ul class="layim_chatview layim_chatthis" id="layim_area‘+ param.type + param.id +‘"></ul>‘);
xxim.tabchat(param);
}
//群组
log.chatgroup = xxim.chatbox.find(‘#layim_groups‘);
if(param.type === ‘group‘){
log.chatgroup.find(‘ul‘).removeClass(‘layim_groupthis‘);
log.chatgroup.append(‘<ul class="layim_groupthis" id="layim_group‘+ param.type + param.id +‘"></ul>‘);
xxim.getGroups(param);
}
//点击群员切换聊天窗
log.chatgroup.on(‘click‘, ‘ul>li‘, function(){
xxim.popchatbox($(this));
});
};
//定位到某个聊天队列
xxim.tabchat = function(param){
var node = xxim.node, log = {}, keys = param.type + param.id;
xxim.nowchat = param;
xxim.chatbox.find(‘#layim_user‘+ keys).addClass(‘layim_chatnow‘).siblings().removeClass(‘layim_chatnow‘);
xxim.chatbox.find(‘#layim_area‘+ keys).addClass(‘layim_chatthis‘).siblings().removeClass(‘layim_chatthis‘);
xxim.chatbox.find(‘#layim_group‘+ keys).addClass(‘layim_groupthis‘).siblings().removeClass(‘layim_groupthis‘);
xxim.chatbox.find(‘.layim_face>img‘).attr(‘src‘, param.face);
xxim.chatbox.find(‘.layim_face, .layim_names‘).attr(‘href‘, param.href);
xxim.chatbox.find(‘.layim_names‘).text(param.name);
xxim.chatbox.find(‘.layim_seechatlog‘).attr(‘href‘, config.chatlogurl + param.id);
log.groups = xxim.chatbox.find(‘.layim_groups‘);
if(param.type === ‘group‘){
log.groups.show();
} else {
log.groups.hide();
}
$(‘#layim_write‘).focus();
};
//弹出聊天窗
xxim.popchatbox = function(othis){
var node = xxim.node, dataId = othis.attr(‘data-id‘), param = {
id: dataId, //用户ID
type: othis.attr(‘type‘),
name: othis.find(‘.xxim_onename‘).text(), //用户名
face: othis.find(‘.xxim_oneface‘).attr(‘src‘), //用户头像
href: config.hosts + ‘user/‘ + dataId //用户主页
}, key = param.type + dataId;
if(!config.chating[key]){
xxim.popchat(param);
config.chatings++;
} else {
xxim.tabchat(param);
}
config.chating[key] = param;
var chatbox = $(‘#layim_chatbox‘);
if(chatbox[0]){
node.layimMin.hide();
chatbox.parents(‘.xubox_layer‘).show();
}
};
//请求群员
xxim.getGroups = function(param){
var keys = param.type + param.id, str = ‘‘,
groupss = xxim.chatbox.find(‘#layim_group‘+ keys);
groupss.addClass(‘loading‘);
config.json(config.api.groups, {}, function(datas){
if(datas.status === 1){
var ii = 0, lens = datas.data.length;
if(lens > 0){
for(; ii < lens; ii++){
str += ‘<li data-id="‘+ datas.data[ii].id +‘" type="one"><img src="‘+ datas.data[ii].face +‘"><span class="xxim_onename">‘+ datas.data[ii].name +‘</span></li>‘;
}
} else {
str = ‘<li class="layim_errors">没有群员</li>‘;
}
} else {
str = ‘<li class="layim_errors">‘+ datas.msg +‘</li>‘;
}
groupss.removeClass(‘loading‘);
groupss.html(str);
}, function(){
groupss.removeClass(‘loading‘);
groupss.html(‘<li class="layim_errors">请求异常</li>‘);
});
};
//聊天模版
xxim.msgTpl = function(param, type){
return ‘<li class="‘+ (type === ‘me‘ ? ‘layim_chateme‘ : ‘‘) +‘">‘
+‘<div class="layim_chatuser">‘
+ function(){
if(type === ‘me‘){
return ‘<span class="layim_chattime">‘+ param.time +‘</span>‘
+‘<span class="layim_chatname">‘+ param.name +‘</span>‘
+‘<img src="‘+ param.face +‘" >‘;
} else {
return ‘<img src="‘+ param.face +‘" >‘
+‘<span class="layim_chatname">‘+ param.name +‘</span>‘
+‘<span class="layim_chattime">‘+ param.time +‘</span>‘;
}
}()
+‘</div>‘
+‘<div class="layim_chatsay">‘+ param.content +‘<em class="layim_zero"></em></div>‘
+‘</li>‘;
};
//消息接收
xxim.writeReceiveMessage = function(obj){
var node = xxim.node, log = {};
node.sendbtn = $(‘#layim_sendbtn‘);
node.imwrite = $(‘#layim_write‘);
var keys = xxim.nowchat.type + xxim.nowchat.id;
var imarea = xxim.chatbox.find(‘#layim_area‘+ keys);
if(xxim.nowchat.id === obj.objname){
obj.objface = xxim.nowchat.face;
}else{
obj.objface = ‘images/3.png‘;
}
if(obj.receiveType === ‘handleIQ‘){
}else if(obj.receiveType === ‘handleMessage‘){
xxim.messageTip(1);
imarea.append(xxim.msgTpl({
time: obj.objtime,
name: obj.objname,
face: obj.objface,
content: obj.content
}));
}else if(obj.receiveType === ‘handlePresence‘){
//对聊天对象to的头像状态的修改
}else if(obj.receiveType === ‘handleError‘){
imarea.append(webxxim.xxim.msgTpl({
time: obj.objtime,
name: obj.objname,
face: obj.objface,
content: obj.content
}),‘me‘);
}else if(obj.receiveType === ‘handleStatusChanged‘){
//对自己头像状态的修改
}else{
}
node.imwrite.val(‘‘).focus();
imarea.scrollTop(imarea[0].scrollHeight);
}
//消息传输
xxim.transmit = function(){
var node = xxim.node, log = {};
node.sendbtn = $(‘#layim_sendbtn‘);
node.imwrite = $(‘#layim_write‘);
//发送
log.send = function(){
var data = {
content: node.imwrite.val(),
id: xxim.nowchat.id,
sign_key: ‘‘, //密匙
_: +new Date
};
if(data.content.replace(/\s/g, ‘‘) === ‘‘){
layer.tips(‘说点啥呗!‘, ‘#layim_write‘, 2);
node.imwrite.focus();
} else {
//此处皆为模拟
var keys = xxim.nowchat.type + xxim.nowchat.id;
var imarea = xxim.chatbox.find(‘#layim_area‘+ keys);
var nowstr = util.getDatetime();
imarea.append(xxim.msgTpl({
time: nowstr,
name: config.user.name,
face: config.user.face,
content: data.content
}, ‘me‘));
node.imwrite.val(‘‘).focus();
imarea.scrollTop(imarea[0].scrollHeight);
/////////////////////////////////////发送信息到服务器///////////////////////////////////////////////////////////
serverChat.sendMessage(data.content,xxim.nowchat.id);
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// setTimeout(function(){
// imarea.append(xxim.msgTpl({
// time: ‘2014-04-26 0:38‘,
// name: xxim.nowchat.name,
// face: xxim.nowchat.face,
// content: config.autoReplay[(Math.random()*config.autoReplay.length) | 0]
// }));
// imarea.scrollTop(imarea[0].scrollHeight);
// }, 500);
/*
that.json(config.api.sendurl, data, function(datas){
});
*/
}
};
node.sendbtn.on(‘click‘, log.send);
node.imwrite.keyup(function(e){
if(e.keyCode === 13){
log.send();
}
});
};
//事件
xxim.event = function(){
var node = xxim.node;
//主界面tab
node.tabs.eq(0).addClass(‘xxim_tabnow‘);
node.tabs.on(‘click‘, function(){
var othis = $(this), index = othis.index();
xxim.tabs(index);
});
//列表展收
node.list.on(‘click‘, ‘h5‘, function(){
var othis = $(this), chat = othis.siblings(‘.xxim_chatlist‘), parentss = othis.parent();
if(parentss.hasClass(‘xxim_liston‘)){
chat.hide();
parentss.removeClass(‘xxim_liston‘);
} else {
chat.show();
parentss.addClass(‘xxim_liston‘);
}
});
//设置在线隐身
node.online.on(‘click‘, function(e){
config.stopMP(e);
node.setonline.show();
});
node.setonline.find(‘span‘).on(‘click‘, function(e){
var index = $(this).index();
config.stopMP(e);
if(index === 0){
node.onlinetex.html(‘在线‘);
node.online.removeClass(‘xxim_offline‘);
} else if(index === 1) {
node.onlinetex.html(‘隐身‘);
node.online.addClass(‘xxim_offline‘);
}
node.setonline.hide();
});
node.xximon.on(‘click‘, xxim.expend);
node.xximHide.on(‘click‘, xxim.expend);
//搜索
node.xximSearch.keyup(function(){
var val = $(this).val().replace(/\s/g, ‘‘);
if(val !== ‘‘){
node.searchMian.show();
node.closeSearch.show();
//此处的搜索ajax参考xxim.getDates
node.list.eq(3).html(‘<li class="xxim_errormsg">没有符合条件的结果</li>‘);
} else {
node.searchMian.hide();
node.closeSearch.hide();
}
});
node.closeSearch.on(‘click‘, function(){
$(this).hide();
node.searchMian.hide();
node.xximSearch.val(‘‘).focus();
});
//弹出聊天窗
config.chatings = 0;
node.list.on(‘click‘, ‘.xxim_childnode‘, function(){
var othis = $(this);
xxim.popchatbox(othis);
});
//点击最小化栏
node.layimMin.on(‘click‘, function(){
$(this).hide();
$(‘#layim_chatbox‘).parents(‘.xubox_layer‘).show();
});
//document事件
dom[1].on(‘click‘, function(){
node.setonline.hide();
$(‘#layim_sendtype‘).hide();
});
};
//请求列表数据
xxim.getDates = function(index){
var api = [config.api.friend, config.api.group, config.api.chatlog],
node = xxim.node, myf = node.list.eq(index);
myf.addClass(‘loading‘);
config.json(api[index], {}, function(datas){
if(datas.status === 1){
var i = 0, myflen = datas.data.length, str = ‘‘, item;
if(myflen > 1){
if(index !== 2){
for(; i < myflen; i++){
str += ‘<li data-id="‘+ datas.data[i].id +‘" class="xxim_parentnode">‘
+‘<h5><i></i><span class="xxim_parentname">‘+ datas.data[i].name +‘</span><em class="xxim_nums">(‘+ datas.data[i].nums +‘)</em></h5>‘
+‘<ul class="xxim_chatlist">‘;
item = datas.data[i].item;
for(var j = 0; j < item.length; j++){
if(item[j].name == config.user.name)
continue;
str += ‘<li data-id="‘+ item[j].id +‘" class="xxim_childnode" type="‘+ (index === 0 ? ‘one‘ : ‘group‘) +‘"><img src="‘+ item[j].face +‘" class="xxim_oneface"><span class="xxim_onename">‘+ item[j].name +‘</span></li>‘;
}
str += ‘</ul></li>‘;
}
} else {
str += ‘<li class="xxim_liston">‘
+‘<ul class="xxim_chatlist">‘;
for(; i < myflen; i++){
str += ‘<li data-id="‘+ datas.data[i].id +‘" class="xxim_childnode" type="one"><img src="‘+ datas.data[i].face +‘" class="xxim_oneface"><span class="xxim_onename">‘+ datas.data[i].name +‘</span><em class="xxim_time">‘+ datas.data[i].time +‘</em></li>‘;
}
str += ‘</ul></li>‘;
}
myf.html(str);
} else {
myf.html(‘<li class="xxim_errormsg">没有任何数据</li>‘);
}
myf.removeClass(‘loading‘);
} else {
myf.html(‘<li class="xxim_errormsg">‘+ datas.msg +‘</li>‘);
}
}, function(){
myf.html(‘<li class="xxim_errormsg">请求失败</li>‘);
myf.removeClass(‘loading‘);
});
};
//渲染骨架
xxim.view = function(){
var xximNode = xxim.layimNode = $(‘<div id="xximmm" class="xxim_main">‘
+‘<div class="xxim_top" id="xxim_top">‘
+‘ <div class="xxim_search"><i></i><input id="xxim_searchkey" /><span id="xxim_closesearch">×</span></div>‘
+‘ <div class="xxim_tabs" id="xxim_tabs"><span class="xxim_tabfriend" title="好友"><i></i></span><span class="xxim_tabgroup" title="群组"><i></i></span><span class="xxim_latechat" title="最近聊天"><i></i></span></div>‘
+‘ <ul class="xxim_list" style="display:block"></ul>‘
+‘ <ul class="xxim_list"></ul>‘
+‘ <ul class="xxim_list"></ul>‘
+‘ <ul class="xxim_list xxim_searchmain" id="xxim_searchmain"></ul>‘
+‘</div>‘
+‘<ul class="xxim_bottom" id="xxim_bottom">‘
+‘<li class="xxim_online" id="xxim_online">‘
+‘<i class="xxim_nowstate"></i><span id="xxim_onlinetex">在线</span>‘
+‘<div class="xxim_setonline">‘
+‘<span><i></i>在线</span>‘
+‘<span class="xxim_setoffline"><i></i>隐身</span>‘
+‘</div>‘
+‘</li>‘
+‘<li class="xxim_mymsg" id="xxim_mymsg" title="我的私信"><i></i><a href="‘+ config.msgurl +‘" target="_blank"></a></li>‘
+‘<li class="xxim_seter" id="xxim_seter" title="设置">‘
+‘<i></i>‘
+‘<div class="">‘
+‘</div>‘
+‘</li>‘
+‘<li class="xxim_hide" id="xxim_hide"><i></i></li>‘
+‘<li id="xxim_on" class="xxim_icon xxim_on"></li>‘
+‘<div class="layim_min" id="layim_min"></div>‘
+‘</ul>‘
+‘</div>‘);
dom[3].append(xximNode);
xxim.renode();
xxim.getDates(0);
xxim.event();
xxim.layinit();
};
// 消息提示
xxim.messageTip = function (countparam) {
if (countparam % 4 != 0) {
window.focus();
document.title = "你来了新消息,请查收!";
countparam ++;
window.setTimeout(function(){
xxim.messageTip(countparam)
}, 1000);
} else {
document.title = "";
}
}
//exports.webim = xxim;
/**
* obj = {
username:
password:
* }
*/
exports.initWebim = function(obj){
xxim.view();
var chatConnConfig = {
httpbase: "http://localhost:8080/webim/JHB/", //请求后台http-bind服务器url
domain: ‘openfire402‘, // //"192.168.5.231", // 192.168.5.231 当前有效域名
username: "",
pass: "",
timerval: 2000, // 设置请求超时
resource: "webim", // 链接资源标识
register: true
}
//serverChat.writeReceiveMessage = xxim.writeReceiveMessage;
serverChat.init(chatConnConfig,xxim);
var loginPerson = {
username:obj.username,
password:obj.password,
register:false //是否注册改用户
}
serverChat.login(loginPerson);
if(obj.username == ‘webchattest‘)
{
config.user.face = ‘http://tp1.sinaimg.cn/1571889140/180/40030060651/1‘;
}
config.user.name = loginPerson.username;
}
});
实现效果如下图:
源码可在此下载。
标签:
原文地址:http://blog.csdn.net/fengshuiyue/article/details/51635666