腾讯的网页即时聊天出现了一段时日了,开始以为腾讯是使用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 }