腾讯的网页即时聊天出现了一段时日了,开始以为腾讯是使用flash xmlsocket来建立服务器“推”消息到客户端的,但是通过对客户端代码的分析,以及使用httpwatch监视,发现也是使用按刷新率来“拉”消息。为什么不使用flash做接口呢?参考了许多网上的例子,以及各方面的反应,估计有以下几点
(
个人观点,欢迎指证)。我不是flash程序员
- flash与浏览器JS脚本交互效率不高,经常出现无响应的假死状态.
- flash xmlsocket使用socket协议来通信,这需要服务器端socket监听程序来支持。
flash的安全性要求连接的主机必须在同域或配置安全性许可文件(security.xml)?,种种实现起来还是较复杂的。我不是不推行这种方式,但是对于我们这些一直没有真正包含AS脚本编程经验的同志,实现起来有点难度。
什么是ajax?大家可以去百度或Google查询下怎么定义的。总结一下就几个关键词语:
异步回调
,后台请求
现在网上到处都是ajax技术的文章。其实说的再多,反而让你觉得神秘莫测。
一,原理分析
目前的即时聊天技术实现,使用按设定的时间来刷新获取服务器的新消息,通常使用反复
查询数据库来实现。但是这种效率和服务器资源消耗率都不是很理想。
如果我们在服务器内存,设定某种消息标识机制。用来标识某个客户端是否有新消息,这比反复查询数据库的效率要高的多。但是消息格式是怎样的呢?我们一步一 步来分析和实现一下。和各位朋友来一同探讨。
这是我目前的项目所包含的一个聊天模块:
聊天窗口很简单,上面是内容显示区,下面是消息发送区。客户端使用javascript(jquery框架)来处理消息的发送和接收。
如果构建这样的窗口只需要一些css布局方面的知识就足够了。我们只讲核心的实现方式
提示:jquery框架封装了很多ajax相关的方法以及方便对DOM操作的一系列函数。
客户端聊天代码:
chatplugin.js
1
var emotes=['微笑','撇嘴','色','发呆','得意','流泪','害羞','睡','大哭','尴尬','发怒','调皮','呲牙','惊讶',
2
'难过','酷','冷汗','抓狂','吐','偷笑','可笑','傲慢','饥饿','困','惊恐','流汗','憨笑','大兵',
3
'奋斗','咒骂','疑问','嘘','晕','折磨','衰','敲打','再见','擦汗','扣鼻','鼓掌','糗大了','坏笑',
4
'左哼哼','右哼哼','哈欠','鄙视','委屈','快哭了','阴险','吓','可怜','菜刀','西瓜','脾酒','篮球','乒乓',
5
'咖啡','饭','猪','玫瑰','凋谢','示爱','爱心','蛋糕','闪电','炸弹','刀','足球','瓢虫','便便',
6
'月亮','太阳','礼物','拥抱','强','弱','握手','抱拳','勾引','拳头','差劲','爱你','NO','OK'];
7
var __messageThread=null;
8
var __targetid=0;
9
var __lastMessageTime='';
10
var __constTime=5*1000;
11
var __closed=false;
12
var __loading=true;
13
$(document).ready(function()
{
14
$("#loading").bind("contextmenu",function()
{return false;});
15
$(window).load(function()
{
16
openeditor();
17
__targetid=1;
18
startchat();
19
});
20
$('#sendbutton').hover(
21
function()
{$(this).removeClass('normalsend').addClass('activesend')},
22
function()
{$(this).removeClass('activesend').addClass('normalsend')}
23
)
24
.click(function()
{
25
var mc=getsafehtml();
26
mc=replaceunsafe(mc);
27
if($.trim(mc)!="")
{
28
$(this).css(
{disabled:true});
29
sendMessage(__targetid,mc);
30
}
31
else
{
32
showError('发送的信息不能为空');
33
Editor.focus();
34
}
35
});
36
$('#pagec').find('a').each(function(i)
{
37
$(this).click(function(e)
{
38
$(this).blur();
39
var epageindex=parseInt($("#emote-layer").attr('class').replace(/[^\d]/gi,''));
40
var npageindex=i==0?(epageindex-1):(epageindex+1);
41
if(npageindex<1||npageindex>4)
{e.stopPropagation();return false;}
42
$("#emote-layer").fadeOut(100,function()
{
43
$(this).fadeIn(100,function()
{
44
$("#emote-layer").attr('class',"emote-p"+npageindex);
45
})
46
})
47
48
$('#pagec > span').text(npageindex+" / 4");
49
checkEmotepages(npageindex);
50
e.stopPropagation();//防止上升到document.click
51
});
52
});
53
$("#funclist").find("a").each(function(i)
{
54
$(this).click(function()
{
55
if(i==0)
{//打开表情
56
if($("#emp").is(':hidden'))
{
57
$(document).bind('click',function(event)
{
58
if($(event.target).attr('class')!='a1')
{
59
$("#emp").hide();
60
}
61
});
62
$("#emp").show();
63
}
64
}
65
else if(i==1)
{//清除屏幕
66
$("#chatwin").empty();
67
}
68
});
69
});
70
$("#emp").click(function(e)
{
71
e.stopPropagation();
72
});
73
$("a.closeby").click(function()
{
74
try
{top.existchat();}catch(e)
{}
75
})
76
.focus(function()
{$(this).blur()});
77
$("#emote-layer")
78
.hover(function()
{
79
$(this).find('.maskbox').show();
80
},function()
{
81
$(this).find('.maskbox').hide();$("#epageinfo").text("");
82
$('#previewbox').hide();
83
})
84
.mousemove(function(event)
{
85
var lfpos=event.pageX-$(this).offset().left;
86
var tppos=event.pageY-$(this).offset().top;
87
var lindex=lfpos/30+"";
88
var tindex=tppos/30+"";
89
var row=tindex.replace(/\.\d+/gi,'');
90
var coll= lindex.replace(/\.\d+/gi,'');
91
var r_c=row+","+coll;
92
if($(this).find('.maskbox').data('r_c'))
{
93
if($(this).find('.maskbox').data('r_c').r==r_c)
94
return false;
95
}
96
//-----
97
var pageindex=parseInt($.trim($('#pagec > span').text().split('/')[0]));
98
var pcurPostion=$(this).find('#previewbox').attr('class');
99
var startIndex=pageindex<3?((pageindex-1)*7+parseInt(row)*14):42+(pageindex-3)*7+parseInt(row)*14;
100
var imageindex=startIndex+parseInt(coll)+1;
101
var imagedesc="/"+emotes[imageindex-1];
102
var imagesrc = "images/emote/"+imageindex+".gif";
103
$("#epageinfo").text(imagedesc);
104
$(this).attr('title',imagedesc);
105
$('#previewbox').find('img').attr(
{src:imagesrc}).data('em',
{desc:imagedesc,index:imageindex});
106
if($('#previewbox').is(":hidden"))
{$('#previewbox').show()}
107
////============
108
var prepostion=coll<4?'inleft':'inright';
109
if(prepostion==pcurPostion)
{//发现遮盖
110
$('#previewbox').removeClass(prepostion).addClass(prepostion=='inleft'?'inright':'inleft');
111
}
112
var top_ = row*30-tindex+1;
113
var left_ = coll*30-(lindex-1);
114
$(this).find('.maskbox').css(
{top:top_,left:left_}).data('r_c',r_c);
115
})
116
.click(function(e)
{
117
var imgubb = $("#previewbox").find("img").data('em').desc;
118
insert("<img class=gif border='0' src='"+ $("#previewbox").find("img").attr('src') + "'/>");
119
$("#emp").hide();
120
$(document).unbind('click');
121
e.stopPropagation();
122
});
123
});
124
function changecaption(m)
{
125
$("#chattitle").text(m);
126
}
127
function startchat()
{
128
__closed=false;
129
$("#lstxt").text("正在加载聊天信息.");
130
scrolltoend();
131
getMessage(User.id,__targetid,__lastMessageTime);
132
}
133
function flashMessage()
{
134
var maxt=16;
135
var flag=$(".ctr2").find(".lo2").data("flag");
136
if(!flag) flag =0;
137
if(flag%2==0)
138
$(".ctr2").find(".lo2").addClass("noico");
139
else
140
$(".ctr2").find(".lo2").removeClass("noico");
141
flag++;
142
$(".ctr2").find(".lo2").data("flag",flag);
143
if(flag<maxt)
{
144
setTimeout(flashMessage,400);
145
}
146
else
{
147
$(".ctr2").find(".lo2").removeClass("noico");
148
$(".ctr2").find(".lo2").data("flag",0);
149
}
150
}
151
function checkEmotepages(epageindex)
{
152
$('#pagec').find('a:eq(0)').attr(
{disabled:epageindex==1});
153
$('#pagec').find('a:eq(1)').attr(
{disabled:epageindex==4});
154
}
155
function ubbTag(txa, markup)
{
156
txa.focus();
157
var strEnd=markup.replace(/\[/ig,'[/');
158
if (strEnd.indexOf('=')>-1)
{
159
strEnd=strEnd.replace(/(.*?)\=.*?\]/,'$1]');
160
}
161
if(document.selection&&document.selection.type== "Text")
{
162
// IE, Opera
163
var oStr=document.selection.createRange();
164
oStr.text=markup+oStr.text+strEnd;
165
} else if(window.getSelection&&txa.selectionStart>-1)
{
166
// Netscape
167
var st=txa.selectionStart;
168
var ed=txa.selectionEnd;
169
txa.value=txa.value.substring(0,st)+markup+
170
txa.value.substring(st,ed)+strEnd+
171
txa.value.slice(ed);
172
} else
{
173
txa.value+=markup+strEnd;
174
}
175
}
176
function sendMessage(targetid,content)
{
177
var eco=encode64(content);
178
Async('chat/send/',
{
179
target:targetid,
180
uid:User.id,
181
m:eco
182
},function(r)
{
183
var t=date2str(new Date());
184
if(!r)
{showError('与服务器通讯发生错误。',t);return false;}
185
if(!r.st)
{showError(r.err,t);return false;}
186
addMessage(User.nick,User.id,content,t,r.dbid,true);
187
clearhtml();
188
$("#sendbutton").css(
{disabled:false});
189
},'json','POST');
190
}
191
function getMessage(userid,targetid)
{
192
//changecaption(__lastMessageTime);
193
if(__closed)
{
194
$("#loading").show();
195
return false;
196
}
197
Async('chat/get/',
{
198
uid:userid,
199
target:targetid,
200
last:(typeof __lastMessageTime=='undefined')?'':__lastMessageTime
201
},function(r)
{
202
var t=date2str(new Date());
203
if(!r)
{showError('网络错误或数据传输失败。',t);return false;}
204
if(!r.st)
{showError(r.err,t);return false;}
205
var mtime='';
206
if(r.datas.length>0)
{
207
//按messageid顺序排序
208
r.datas=r.datas.sort(function(a,b)
{
209
return a.mid-b.mid;
210
});
211
$.each(r.datas,function(i,n)
{
212
var b=true;
213
if(__lastMessageTime=='')
{
214
if($("#msg"+n.mid).length!=0)
{
215
b=false;
216
}
217
}
218
if(b)addMessage(n.uname,n.uid,decode64(n.m),n.t,n.mid,n.uid==User.id);
219
mtime=n.t;
220
});
221
if(mtime!='')
222
__lastMessageTime=mtime;
223
flashMessage();
224
}
225
//清除LOADING
226
if(!$("#loading").is(":hidden"))
{ __loading=false;$("#loading").hide();}
227
setTimeout(function()
{getMessage(User.id,__targetid);},__constTime)
228
},'json','GET');
229
}
230
function scrolltoend()
{
231
$("#chatwin").get(0).scrollTop=$("#chatwin").get(0).scrollHeight;
232
}
233
var errtimer=null;
234
function showError(errmess,t)
{
235
//$("#chatwin").find(".merr:last").fadeOut(100);
236
if(!t) t="";
237
if(__loading)
{
238
$("#lstxt").html("<font color=red>"+errmess+"</font>");
239
}
240
if($("#chatwin > .msgitem:last").find('.err').length>0)
{//错误消息已经存在
241
$("#chatwin > .msgitem:last").find('.err > span').html(errmess);
242
$("#chatwin > .msgitem:last").find('.err > font').html(t);
243
}
244
else
{
245
$("#chatwin").find(".merr").remove();
246
$("#chatwin").append(String.format("<div class=\"msgitem merr\"><div class='err'><b disabled='disabled'>[{2}]</b> <span>{0}</span> <font>{1}</font></div></div>",errmess,t,"提示"));
247
}
248
$("#chatwin").find(".merr:last").fadeIn(100);
249
scrolltoend();
250
if(errtimer) clearTimeout(errtimer);
251
errtimer = setTimeout(function()
{
252
$("#chatwin").find(".merr:last").fadeOut(200);
253
errtimer=null;
254
},3000)
255
}
256
function addMessage(username,userid,msghtml,msgtime,dbid,bMyMessage)
{
257
var sb = new StringBuilder();
258
sb.append("<div class=\"msgitem\" id='msg{4}'>");
259
sb.append("<h2{3}>{0} {1}</h2>");
260
sb.append("<div class=\"p\">");
261
sb.append("{2}");
262
sb.append("</div>");
263
sb.append("</div>");
264
var c=String.format(sb.toString(),username,msgtime,msghtml,bMyMessage?" class='me'":"",dbid);
265
$("#chatwin").append(c);
266
scrolltoend();
267
sb=null;
268
}
二。消息结构
上面的客户端代码,重点在于包装消息请求,以及对服务器端响应的JSON数据进行处理。
Async函数是封装的AJAX请求函数
Async(请求地址,请求参数json结构,响应函数,内容响应格式,发送方式)
如果你对jquery有了解,其实这和JQUERY的ajax发送请求方式非常相似,这里进一步封装只是稍微方便一些。
1.发送消息(请求):
1
function
sendMessage(targetid,content){
2
3
var
eco
=
encode64(content);
4
5
Async(
'
chat/send/
'
,{
6
7
target:targetid,
8
9
uid:User.id,
10
11
m:eco
12
13
},
function
(r){
14
15
var
t
=
date2str(
new
Date());
16
17
if
(
!
r){showError(
'
与服务器通讯发生错误。
'
,t);
return
false
;}
18
19
if
(
!
r.st){showError(r.err,t);
return
false
;}
20
21
addMessage(User.nick,User.id,content,t,r.dbid,
true
);
//
添加消息到聊天窗口
22
23
clearhtml();
//
清空发送消息框的内容
24
25
$(
"
#sendbutton
"
).css({disabled:
false
});
26
27
},
'
json
'
,
'
POST
'
);
28
29
}
解释:
实际请求url : /chat/send.aspx 上面的url使用urlrewrite过
sendMessage(targetid,content) 函数
targetid:目标ID(可以是用户,聊天室ID等) 在服务器端代码里会解释这个ID
uid:用户ID
m:使用BASE64客户端加密的消息内容
function(r){//json响应回调
….//这个AJAX请求发送后服务器的响应处理
}
服务器JSON消息响应:
{
st:true,
dbid:123,
err:”…..”
}
解释:
st:消息发送是否成功(true/false),dbid:消息所存储在数据库中的ID,err: 如果出现错误,服务器返回的错误信息。如果成功则留空字符串。
2.服务器消息的获取:
对于消息的获取,我们使用setTimeout函数启动一个“线程”隔5s去服务器端请求一次。然后处理服务器响应的内容。这里的内容可能是有新消息,或者没有任何新内容。
1
function
getMessage(userid,targetid){
2
//
changecaption(__lastMessageTime);
3
if
(__closed){
4
$(
"
#loading
"
).show();
5
return
false
;
6
}
7
Async(
'
chat/get/
'
,{
8
uid:userid,
9
target:targetid,
10
last:(
typeof
__lastMessageTime
==
'
undefined
'
)
?
''
:__lastMessageTime
11
},
function
(r){
12
var
t
=
date2str(
new
Date());
13
if
(
!
r){showError(
'
网络错误或数据传输失败。
'
,t);
return
false
;}
14
if
(
!
r.st){showError(r.err,t);
return
false
;}
15
var
mtime
=
''
;
16
if
(r.datas.length
>
0
){
17
//
按messageid顺序排序
18
r.datas
=
r.datas.sort(
function
(a,b){
19
return
a.mid
-
b.mid;
20
});
21
$.each(r.datas,
function
(i,n){
22
var
b
=
true
;
23
if
(__lastMessageTime
==
''
){
24
if
($(
"
#msg
"
+
n.mid).length
!=
0
){
25
b
=
false
;
26
}
27
}
28
if
(b)addMessage(n.uname,n.uid,decode64(n.m),n.t,n.mid,n.uid
==
User.id);
29
mtime
=
n.t;
30
});
31
if
(mtime
!=
''
)
32
__lastMessageTime
=
mtime;
33
flashMessage();
34
}
35
//
清除LOADING
36
if
(
!
$(
"
#loading
"
).is(
"
:hidden
"
)){ __loading
=
false
;$(
"
#loading
"
).hide();}
37
setTimeout(
function
(){getMessage(User.id,__targetid);},__constTime)
38
},
'
json
'
,
'
GET
'
);
39
}
上面的getMessage(userid,targetid)函数 参数userid:用户ID,targetid:目标ID
请求服务器地址:chat/get.aspx
发送到服务器端的参数:
uid:userid, //用户id
target:targetid,//目标ID
last:(typeof __lastMessageTime=='undefined')?'':__lastMessageTime //最后消息接收时间(全局变量)如果第一次请求消息,则为空。
请求发送后服务器返回JSON消息格式数据
{
st: true,
datas:[
{…},
{…},
{…}
]
}
服务器只返回非自身发送的消息,换句话说就是别人给我发送的消息,才返回消息数据。
同样:st表示消息的失败标志位:true表成功,false表失败
Datas:是一个数组。包含一条条的消息对象,里面的元素同样是json对象
{…}的最终结构:
{
mid:消息ID,
uid:发送者用户id,
uname:发送者用户名,
tid:目标ID,
m:’消息内容(使用base64加密,须解码)’,
t:’消息发送时间’
}
一个标准的消息返回示例:
{
st:true.
datas:[
{
mid:1,uid:2,uname=’用户A’,tid:1,m:”--encode---”,t:’2009-8-7 21:38:20’
},
{
mid:2,uid:3,uname=’用户B’,tid:1,m:”---encode--”,t:’2009-8-7 21:38:22’
},
{
mid:3,uid:2,uname=’用户A’,tid:1,m:”--encode---”,t:’2009-8-7 21:38:33’
}
]
}
对服务器响应的消息,除了排序并添加到聊天内容框,还有一个比较重要的变量t变量
对于成功有效的消息,这个t时间变量表明了我最后一条接收的消息时间是t,然后将它存储在__lastMessageTime=t;全局变量中,在下次获取新消息的时候会加上这个变量作为请求参数,服务器端判断这个请求,检查内存application变量或asp.net中HttpApplication静态类变量(也叫服务器全局变量)中有没有大于这个时间的消息,并且发送者不是我自己。
服务器端处理代码将在下一节提供。
在消息处理后继续隔__constTime所设定的时间,继续请求服务器端获取新内容,达到一个循环。
setTimeout(function(){getMessage(User.id,__targetid);},__constTime)
如果获取消息失败会自动停止再次请求(这里可以修改成,失败继续模式)。
当然,安全性方面我们需要过滤客户端的一些非法代码或攻击脚本,提高服务器安全性。这里可以使用正则表达式进行过滤。具体代码在稍候提供的压缩包中。
客户端的消息发送格式和服务器的响应格式大体就这样子,下一篇日志中,我会给大家提供服务器端的实现,以及数据库方面的内容。
《基于AJAX的即时聊天(2)-服务器端》
1
var emotes=['微笑','撇嘴','色','发呆','得意','流泪','害羞','睡','大哭','尴尬','发怒','调皮','呲牙','惊讶',
2
'难过','酷','冷汗','抓狂','吐','偷笑','可笑','傲慢','饥饿','困','惊恐','流汗','憨笑','大兵',
3
'奋斗','咒骂','疑问','嘘','晕','折磨','衰','敲打','再见','擦汗','扣鼻','鼓掌','糗大了','坏笑',
4
'左哼哼','右哼哼','哈欠','鄙视','委屈','快哭了','阴险','吓','可怜','菜刀','西瓜','脾酒','篮球','乒乓',
5
'咖啡','饭','猪','玫瑰','凋谢','示爱','爱心','蛋糕','闪电','炸弹','刀','足球','瓢虫','便便',
6
'月亮','太阳','礼物','拥抱','强','弱','握手','抱拳','勾引','拳头','差劲','爱你','NO','OK'];
7
var __messageThread=null;
8
var __targetid=0;
9
var __lastMessageTime='';
10
var __constTime=5*1000;
11
var __closed=false;
12
var __loading=true;
13
$(document).ready(function()
{
14
$("#loading").bind("contextmenu",function()
{return false;});
15
$(window).load(function()
{
16
openeditor();
17
__targetid=1;
18
startchat();
19
});
20
$('#sendbutton').hover(
21
function()
{$(this).removeClass('normalsend').addClass('activesend')},
22
function()
{$(this).removeClass('activesend').addClass('normalsend')}
23
)
24
.click(function()
{
25
var mc=getsafehtml();
26
mc=replaceunsafe(mc);
27
if($.trim(mc)!="")
{
28
$(this).css(
{disabled:true});
29
sendMessage(__targetid,mc);
30
}
31
else
{
32
showError('发送的信息不能为空');
33
Editor.focus();
34
}
35
});
36
$('#pagec').find('a').each(function(i)
{
37
$(this).click(function(e)
{
38
$(this).blur();
39
var epageindex=parseInt($("#emote-layer").attr('class').replace(/[^\d]/gi,''));
40
var npageindex=i==0?(epageindex-1):(epageindex+1);
41
if(npageindex<1||npageindex>4)
{e.stopPropagation();return false;}
42
$("#emote-layer").fadeOut(100,function()
{
43
$(this).fadeIn(100,function()
{
44
$("#emote-layer").attr('class',"emote-p"+npageindex);
45
})
46
})
47
48
$('#pagec > span').text(npageindex+" / 4");
49
checkEmotepages(npageindex);
50
e.stopPropagation();//防止上升到document.click
51
});
52
});
53
$("#funclist").find("a").each(function(i)
{
54
$(this).click(function()
{
55
if(i==0)
{//打开表情
56
if($("#emp").is(':hidden'))
{
57
$(document).bind('click',function(event)
{
58
if($(event.target).attr('class')!='a1')
{
59
$("#emp").hide();
60
}
61
});
62
$("#emp").show();
63
}
64
}
65
else if(i==1)
{//清除屏幕
66
$("#chatwin").empty();
67
}
68
});
69
});
70
$("#emp").click(function(e)
{
71
e.stopPropagation();
72
});
73
$("a.closeby").click(function()
{
74
try
{top.existchat();}catch(e)
{}
75
})
76
.focus(function()
{$(this).blur()});
77
$("#emote-layer")
78
.hover(function()
{
79
$(this).find('.maskbox').show();
80
},function()
{
81
$(this).find('.maskbox').hide();$("#epageinfo").text("");
82
$('#previewbox').hide();
83
})
84
.mousemove(function(event)
{
85
var lfpos=event.pageX-$(this).offset().left;
86
var tppos=event.pageY-$(this).offset().top;
87
var lindex=lfpos/30+"";
88
var tindex=tppos/30+"";
89
var row=tindex.replace(/\.\d+/gi,'');
90
var coll= lindex.replace(/\.\d+/gi,'');
91
var r_c=row+","+coll;
92
if($(this).find('.maskbox').data('r_c'))
{
93
if($(this).find('.maskbox').data('r_c').r==r_c)
94
return false;
95
}
96
//-----
97
var pageindex=parseInt($.trim($('#pagec > span').text().split('/')[0]));
98
var pcurPostion=$(this).find('#previewbox').attr('class');
99
var startIndex=pageindex<3?((pageindex-1)*7+parseInt(row)*14):42+(pageindex-3)*7+parseInt(row)*14;
100
var imageindex=startIndex+parseInt(coll)+1;
101
var imagedesc="/"+emotes[imageindex-1];
102
var imagesrc = "images/emote/"+imageindex+".gif";
103
$("#epageinfo").text(imagedesc);
104
$(this).attr('title',imagedesc);
105
$('#previewbox').find('img').attr(
{src:imagesrc}).data('em',
{desc:imagedesc,index:imageindex});
106
if($('#previewbox').is(":hidden"))
{$('#previewbox').show()}
107
////============
108
var prepostion=coll<4?'inleft':'inright';
109
if(prepostion==pcurPostion)
{//发现遮盖
110
$('#previewbox').removeClass(prepostion).addClass(prepostion=='inleft'?'inright':'inleft');
111
}
112
var top_ = row*30-tindex+1;
113
var left_ = coll*30-(lindex-1);
114
$(this).find('.maskbox').css(
{top:top_,left:left_}).data('r_c',r_c);
115
})
116
.click(function(e)
{
117
var imgubb = $("#previewbox").find("img").data('em').desc;
118
insert("<img class=gif border='0' src='"+ $("#previewbox").find("img").attr('src') + "'/>");
119
$("#emp").hide();
120
$(document).unbind('click');
121
e.stopPropagation();
122
});
123
});
124
function changecaption(m)
{
125
$("#chattitle").text(m);
126
}
127
function startchat()
{
128
__closed=false;
129
$("#lstxt").text("正在加载聊天信息.");
130
scrolltoend();
131
getMessage(User.id,__targetid,__lastMessageTime);
132
}
133
function flashMessage()
{
134
var maxt=16;
135
var flag=$(".ctr2").find(".lo2").data("flag");
136
if(!flag) flag =0;
137
if(flag%2==0)
138
$(".ctr2").find(".lo2").addClass("noico");
139
else
140
$(".ctr2").find(".lo2").removeClass("noico");
141
flag++;
142
$(".ctr2").find(".lo2").data("flag",flag);
143
if(flag<maxt)
{
144
setTimeout(flashMessage,400);
145
}
146
else
{
147
$(".ctr2").find(".lo2").removeClass("noico");
148
$(".ctr2").find(".lo2").data("flag",0);
149
}
150
}
151
function checkEmotepages(epageindex)
{
152
$('#pagec').find('a:eq(0)').attr(
{disabled:epageindex==1});
153
$('#pagec').find('a:eq(1)').attr(
{disabled:epageindex==4});
154
}
155
function ubbTag(txa, markup)
{
156
txa.focus();
157
var strEnd=markup.replace(/\[/ig,'[/');
158
if (strEnd.indexOf('=')>-1)
{
159
strEnd=strEnd.replace(/(.*?)\=.*?\]/,'$1]');
160
}
161
if(document.selection&&document.selection.type== "Text")
{
162
// IE, Opera
163
var oStr=document.selection.createRange();
164
oStr.text=markup+oStr.text+strEnd;
165
} else if(window.getSelection&&txa.selectionStart>-1)
{
166
// Netscape
167
var st=txa.selectionStart;
168
var ed=txa.selectionEnd;
169
txa.value=txa.value.substring(0,st)+markup+
170
txa.value.substring(st,ed)+strEnd+
171
txa.value.slice(ed);
172
} else
{
173
txa.value+=markup+strEnd;
174
}
175
}
176
function sendMessage(targetid,content)
{
177
var eco=encode64(content);
178
Async('chat/send/',
{
179
target:targetid,
180
uid:User.id,
181
m:eco
182
},function(r)
{
183
var t=date2str(new Date());
184
if(!r)
{showError('与服务器通讯发生错误。',t);return false;}
185
if(!r.st)
{showError(r.err,t);return false;}
186
addMessage(User.nick,User.id,content,t,r.dbid,true);
187
clearhtml();
188
$("#sendbutton").css(
{disabled:false});
189
},'json','POST');
190
}
191
function getMessage(userid,targetid)
{
192
//changecaption(__lastMessageTime);
193
if(__closed)
{
194
$("#loading").show();
195
return false;
196
}
197
Async('chat/get/',
{
198
uid:userid,
199
target:targetid,
200
last:(typeof __lastMessageTime=='undefined')?'':__lastMessageTime
201
},function(r)
{
202
var t=date2str(new Date());
203
if(!r)
{showError('网络错误或数据传输失败。',t);return false;}
204
if(!r.st)
{showError(r.err,t);return false;}
205
var mtime='';
206
if(r.datas.length>0)
{
207
//按messageid顺序排序
208
r.datas=r.datas.sort(function(a,b)
{
209
return a.mid-b.mid;
210
});
211
$.each(r.datas,function(i,n)
{
212
var b=true;
213
if(__lastMessageTime=='')
{
214
if($("#msg"+n.mid).length!=0)
{
215
b=false;
216
}
217
}
218
if(b)addMessage(n.uname,n.uid,decode64(n.m),n.t,n.mid,n.uid==User.id);
219
mtime=n.t;
220
});
221
if(mtime!='')
222
__lastMessageTime=mtime;
223
flashMessage();
224
}
225
//清除LOADING
226
if(!$("#loading").is(":hidden"))
{ __loading=false;$("#loading").hide();}
227
setTimeout(function()
{getMessage(User.id,__targetid);},__constTime)
228
},'json','GET');
229
}
230
function scrolltoend()
{
231
$("#chatwin").get(0).scrollTop=$("#chatwin").get(0).scrollHeight;
232
}
233
var errtimer=null;
234
function showError(errmess,t)
{
235
//$("#chatwin").find(".merr:last").fadeOut(100);
236
if(!t) t="";
237
if(__loading)
{
238
$("#lstxt").html("<font color=red>"+errmess+"</font>");
239
}
240
if($("#chatwin > .msgitem:last").find('.err').length>0)
{//错误消息已经存在
241
$("#chatwin > .msgitem:last").find('.err > span').html(errmess);
242
$("#chatwin > .msgitem:last").find('.err > font').html(t);
243
}
244
else
{
245
$("#chatwin").find(".merr").remove();
246
$("#chatwin").append(String.format("<div class=\"msgitem merr\"><div class='err'><b disabled='disabled'>[{2}]</b> <span>{0}</span> <font>{1}</font></div></div>",errmess,t,"提示"));
247
}
248
$("#chatwin").find(".merr:last").fadeIn(100);
249
scrolltoend();
250
if(errtimer) clearTimeout(errtimer);
251
errtimer = setTimeout(function()
{
252
$("#chatwin").find(".merr:last").fadeOut(200);
253
errtimer=null;
254
},3000)
255
}
256
function addMessage(username,userid,msghtml,msgtime,dbid,bMyMessage)
{
257
var sb = new StringBuilder();
258
sb.append("<div class=\"msgitem\" id='msg{4}'>");
259
sb.append("<h2{3}>{0} {1}</h2>");
260
sb.append("<div class=\"p\">");
261
sb.append("{2}");
262
sb.append("</div>");
263
sb.append("</div>");
264
var c=String.format(sb.toString(),username,msgtime,msghtml,bMyMessage?" class='me'":"",dbid);
265
$("#chatwin").append(c);
266
scrolltoend();
267
sb=null;
268
}