chat.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. //在页面添加在线聊天功能入口,因为这是一个单独功能,所以使用动态代码,以后不要这个功能了直接去除掉chat.js引用就行
  2. $("html").append('<link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">');
  3. $("html").append('<script src="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>');
  4. $("html").append('<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>');
  5. $("body").append(
  6. '<div class="btn-group pull-right" style="position: absolute;right: 20px;top:20px;">'+
  7. '<button type="button" class="btn dropdown-toggle btn-sm " data-toggle="dropdown" style="opacity:0.6;">'+
  8. '在线视频聊天(最好使用谷歌浏览器) <span class="glyphicon glyphicon-align-justify"></span>'+
  9. '</button>'+
  10. '<ul class="dropdown-menu" id="chat_socketCon" role="menu" id="chat_users">'+
  11. '<li id="rct_con"><a href="javascript:void(0)">未连接(点击连接获取用户列表)</a></li>'+
  12. '<li class="divider"></li>'+
  13. '</ul>'+
  14. '</div>'
  15. );
  16. $("body").append(
  17. '<div class="modal fade" id="chat_dialog" tabindex="-1" role="dialog" data-keyboard="false" aria-hidden="true" data-backdrop="static" aria-labelledby="myModalLabel">'+
  18. '<div class="modal-dialog">'+
  19. '<div class="modal-content">'+
  20. '<div class="modal-header">'+
  21. '<h4 class="modal-title">消息</h4>'+
  22. '</div>'+
  23. '<div class="modal-body"><span id="chat_ready_id" data-uid=""></span>,与你发起对话申请,你是否同意</div>'+
  24. '<div class="modal-footer">'+
  25. '<button type="button" class="btn btn-default" onclick="chat_ready(0)">关闭</button>'+
  26. '<button type="button" class="btn btn-primary" onclick="chat_ready(1)">确定</button>'+
  27. '</div>'+
  28. '</div>'+
  29. '</div>'+
  30. '</div>'
  31. );
  32. $("body").append(
  33. '<div class="modal fade" id="chat_name_modal" tabindex="-1" role="dialog" data-keyboard="true" data-backdrop="static" aria-labelledby="myModalLabel">'+
  34. '<div class="modal-dialog">'+
  35. '<div class="modal-content">'+
  36. '<div class="modal-header">'+
  37. '<h4 class="modal-title">提示</h4>'+
  38. '</div>'+
  39. '<div class="modal-body"><input type="text" id="chat_name" class="form-control" placeholder="请输入你昵称"></div>'+
  40. '<div class="modal-footer">'+
  41. '<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>'+
  42. '<button type="button" class="btn btn-primary" onclick="chat_setName()">确定</button>'+
  43. '</div>'+
  44. '</div>'+
  45. '</div>'+
  46. '</div>'
  47. );
  48. $("body").append(
  49. '<div class="modal fade" id="chat_dialogForOne" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static" style="overflow:auto;">' +
  50. '<div class="modal-dialog" style="width: 904px;">' +
  51. '<div class="modal-content">' +
  52. '<div class="modal-header">' +
  53. '<h4 class="modal-title">你正在与[<label name="name"></label>]对话</h4>' +
  54. '</div>' +
  55. '<!-- 聊天框 -->' +
  56. '<div class="modal-body" style="margin: 0px;padding: 0px;">' +
  57. '<div style="height: 647px;">' +
  58. '<!-- 聊天信息展示 -->' +
  59. '<div style="width: 600px;height: 647px;float: left;">' +
  60. '<div style="overflow-y:auto;height: 500px;" class="overflow-3">' +
  61. '<!-- 每一条聊天记录都加在 li里面 -->' +
  62. '<ul class="bubbleDiv overflow-3" name="bubbleDiv"></ul>' +
  63. '</div>' +
  64. '<!-- 聊天输入 -->' +
  65. '<div style="height: 166px;">' +
  66. '<!-- 功能框 -->' +
  67. '<div style="height: 25px;">' +
  68. '<!-- 发送附件功能 -->' +
  69. '<div style="margin-left: 5px;">' +
  70. '<label for="chat_fileMsgForOne">' +
  71. '<span><!--<i class="glyphicon glyphicon-link"></i>--></span>' +
  72. '</label>' +
  73. '<!--<form><input type="file" id="chat_fileMsgForOne" style="position:absolute;clip:rect(0 0 0 0);"></form>-->' +
  74. '</div>' +
  75. '</div>' +
  76. '<!-- 输入框 -->' +
  77. '<div style="height: 142px;width: 100%;">' +
  78. '<textarea class="overflow-3" name = "message" style="width: 100%;height: 100%;resize:none;border: 0px;background-color: #EEEEEE;" placeholder="请输入需要发送的内容"></textarea>' +
  79. '</div>' +
  80. '</div>' +
  81. '</div>' +
  82. '<!-- 右侧视频语音聊天展示 -->' +
  83. '<div style="width: 300px;height: 647px;float: left;">' +
  84. '<!-- 本地视频框 -->' +
  85. '<div class="panel panel-default" style="margin: 0px;padding: 0px;">' +
  86. '<div class="panel-body">' +
  87. '<video style="height:250px;width:250px;margin: 0px;padding: 0px;" name="video" autoplay>' +'</video>' +
  88. '</div>' +
  89. '<div class="panel-footer" align="center">' +
  90. '<button type="button" class="btn btn-default btn-lg btn-xs" name="openVideo" data-use="false"><i class="glyphicon glyphicon-facetime-video"></i><span>开始视频</span></button>' +
  91. '<button type="button" class="btn btn-default btn-lg btn-xs" name="openAudio" data-use="false"><i class="glyphicon glyphicon-earphone"></i><span>开始语音</span></button>' +
  92. '</div>' +
  93. '</div>' +
  94. '<!-- 远端视频框 -->' +
  95. '<div class="panel panel-default" style="margin: 0px;padding: 0px;">' +
  96. '<div class="panel-body">' +
  97. '<video style="height:250px;width:250px;" name="remote" autoplay></video>' +
  98. '</div>' +
  99. '<div class="panel-footer" align="center">好友视频展示</div>' +
  100. '</div>' +
  101. '</div>' +
  102. '</div>' +
  103. '</div>' +
  104. '<div class="modal-footer">' +
  105. '<button type="button" class="btn btn-default" data-dismiss="modal">关闭对话</button>' +
  106. '<button type="button" class="btn btn-success" id="chat_sendString">发送消息[Enter]</button>' +
  107. '</div>' +
  108. '</div>' +
  109. '</div>'
  110. );
  111. var chat_socket = null;
  112. var rtc = null;
  113. var chat_name = null;
  114. /**
  115. * 起名
  116. */
  117. var chat_setName = function(){
  118. var name = $("#chat_name").val();
  119. if(name){
  120. chat_name = name;
  121. chat_socketCon();//并不是每次都创建
  122. chat_sendMsg(1,"");//请求获取其他在线用户的信息,排除掉自己
  123. $("#chat_name_modal").modal('hide');
  124. }
  125. }
  126. /**
  127. * 创建websocket
  128. */
  129. var chat_socketCon = function(){
  130. if(chat_socket != null){
  131. return;
  132. }
  133. chat_socket = 0;//相当于暂时锁住,防止点击太快创建两次socket
  134. var t_socket = createWebSocket("webrtc",websocketPath,
  135. function(){//连接成功,向服务端发送身份
  136. chat_socket = t_socket;
  137. chat_sendMsg(0,chat_name);
  138. },function(){//连接关闭
  139. chat_socket = null;
  140. $("#chat_socketCon li a").eq(0).html("未连接(点击连接获取用户列表)");
  141. var u = $("#chat_socketCon");
  142. var li = u.find("li");
  143. for(var i = 2;i < li.length; i++){
  144. li[i].remove();
  145. }
  146. $("#chat_ready_id").html("");
  147. $("#chat_ready_id").data("uid","");
  148. $("#chat_dialog").modal('hide');
  149. $("#chat_dialogForOne").modal("hide");
  150. },function(){//连接异常
  151. chat_socket = null;
  152. $("#chat_socketCon li a").eq(0).html("未连接(点击连接获取用户列表)");
  153. var u = $("#chat_socketCon");
  154. var li = u.find("li");
  155. for(var i = 2;i < li.length; i++){
  156. li[i].remove();
  157. }
  158. },function(msg){//收到消息
  159. var data = JSON.parse(msg);
  160. chat_response(data.type,data.message);
  161. });
  162. }
  163. /**
  164. * 处理服务端响应的消息
  165. * 定义请求或响应类型,如果是发出,则是发送该类型的请求,如果是响应,则是收到了该类型的请求
  166. * type
  167. * 0(向服务端上报身份,或者收到服务端返回的身份id)
  168. * 1(向服务端请求获取其他在线用户id数组,或者收到服务端返回的在线用户数组)
  169. * 2(发起与一个用户的对话准备,或收到一个用户的准备)
  170. * 3(类型2发起的用户是否准备的响应结果)
  171. * 4(取消准备并且给对方发送取消准备,或者接受对方取消准备的消息)
  172. * @param {*} type
  173. * @param {*} msg
  174. */
  175. var chat_response = function(type,msg){
  176. if(type == "0"){//收到服务端响应的身份id
  177. $("#chat_socketCon li a").eq(0).html("已连接(<span id='uid'>"+msg+"</span>)再次点击刷新");
  178. chat_sendMsg(1,"");//请求获取其他在线用户的信息,排除掉自己
  179. }else if(type == "1"){//收到服务端响应的其他在线用户信息
  180. if(msg.length != 0){
  181. var users = JSON.parse(msg);
  182. var u = $("#chat_socketCon");
  183. var li = u.find("li");
  184. for(var i = 2;i < li.length; i++){
  185. li[i].remove();
  186. }
  187. if(users.length == 0){//没有一个其他用户
  188. u.append('<li><a href="javascript:void(0)">暂未有其他用户连接</a></li>');
  189. u.append('<li><a href="javascript:void(0)">可以开两个浏览器模拟使用(暂时只支持谷歌)</a></li>');
  190. u.append('<li><a href="javascript:void(0)">不是第一次访问,请先清除浏览器文件缓存</a></li>');
  191. return;
  192. }
  193. for(var i in users){
  194. var uid = users[i]["sessionId"];
  195. var name = users[i]["name"];
  196. var status = users[i]["status"];
  197. var ready = users[i]["callId"] == uid ? 2 : 1;//2正在与自己对话,1正在与别人对话
  198. u.append("<li name='chat_user'><a href=\"javascript:void(0)\"><span class='glyphicon glyphicon-user'></span>&nbsp;&nbsp;"+name+"&nbsp;&nbsp;<button data-userid='"+uid+"' type='button' class='btn "+(status == 0 ? "btn-success" : ready == 2 ? "btn-primary" : "btn-warning")+" btn-xs' id='u"+uid+"'>"+(status == 0 ? "申请对话" : ready == 2 ? "取消对话" : "正在对话")+"</button></a></li>");
  199. }
  200. /**
  201. * 给每个用户号绑定发起访问请求的事件
  202. */
  203. $("#chat_socketCon li[name='chat_user']").on("click",function(e){
  204. e.stopPropagation();
  205. });
  206. $("#chat_socketCon button").off("click").on("click",function(e){
  207. e.stopPropagation();
  208. var userid = $(this).data("userid");
  209. if(userid){
  210. var status = $(this).html();
  211. if(status == "申请对话"){
  212. //检查是否正在跟其他人对话
  213. var bts = $("#chat_socketCon button");
  214. for(var i = 0;i < bts.length; i++){
  215. if($(bts[i]).html() == "取消对话"){
  216. return;
  217. }
  218. }
  219. chat_sendMsg(2,userid);//发起对话准备
  220. $(this).html("取消对话");
  221. $(this).toggleClass("btn-success");
  222. $(this).toggleClass("btn-primary");
  223. }else if(status == "取消对话"){
  224. chat_sendMsg(4,"");//向服务端发起取消对话
  225. }
  226. }
  227. });
  228. }
  229. }else if(type == "2"){//收到了一个用户的准备
  230. if(msg.length != 0){
  231. var users = JSON.parse(msg);
  232. $("#chat_ready_id").html(users.name);
  233. $("#chat_ready_id").data("uid",users.sessionId);
  234. $("#chat_dialog").modal('show');
  235. }
  236. }else if(type == "3"){//响应一个用户的准备
  237. if(msg.length != 0){
  238. var users = JSON.parse(msg);
  239. var ready = users.ready;
  240. var uid = users.sessionId;
  241. if(ready == "1"){//对方同意对话,发起webrtc信令,建立webrtc连接
  242. var dialogfor = $("#chat_dialogForOne");
  243. var dialogforname = dialogfor.find("label[name='name']");
  244. rtc = rtc_getTool(onmessage,onaddstream,function(){
  245. dialogfor.modal("hide");
  246. },function(){
  247. dialogforname.html(users.name);
  248. dialogfor.modal("show");
  249. });
  250. rtc.createPeerConnection();
  251. rtc.sendOffer();
  252. }else if(ready == "0"){
  253. var bt = $("#u" + uid);
  254. bt.html("申请对话");
  255. bt.removeClass("btn-primary");
  256. bt.addClass("btn-success");
  257. }
  258. }
  259. }else if(type == "4"){//收到服务端发过来的取消对话,或者关闭对话
  260. if(msg.length != 0){
  261. $("#chat_dialog").modal('hide');
  262. $("#chat_dialogForOne").modal("hide");
  263. var u = $("#u" + msg);
  264. u.html("申请对话");
  265. u.removeClass("btn-primary");
  266. u.addClass("btn-success");
  267. $("#chat_ready_id").html("");
  268. $("#chat_ready_id").data("uid","");
  269. if(rtc != null){
  270. rtc.close();
  271. }
  272. }
  273. }else if(type == "6"){//收到服务端发过来的 offer信令
  274. if(msg.length != 0){
  275. var json = JSON.parse(msg);
  276. rtc.signallingHandle(json);
  277. }
  278. }else if(type == "7"){//收到服务端发过来的 answer信令
  279. if(msg.length != 0){
  280. var json = JSON.parse(msg);
  281. rtc.signallingHandle(json);
  282. }
  283. }else if(type == "8"){//收到服务端发过来的 候选信令
  284. if(msg.length != 0){
  285. var json = JSON.parse(msg);
  286. rtc.signallingHandle(json);
  287. }
  288. }
  289. }
  290. /**
  291. * 点击连接websocket,或者刷新其他用户
  292. */
  293. $("#rct_con").on("click",function(e){
  294. e.stopPropagation();
  295. if(chat_socket == null){
  296. //先起名
  297. $("#chat_name_modal").modal('show');
  298. }else{
  299. chat_sendMsg(1,"");//请求获取其他在线用户的信息,排除掉自己
  300. }
  301. });
  302. var chat_sendClick = function(){
  303. var value = $("#chat_dialogForOne textarea[name='message']").val();
  304. var msg = {"data":value,"type":"text","id":$("#uid").html()};
  305. showMessage("chat_dialogForOne",msg,"right");
  306. rtc.send(JSON.stringify(msg));
  307. }
  308. /**
  309. * 发送字符串内容
  310. */
  311. $("#chat_sendString").on("click",function(e){
  312. chat_sendClick();
  313. });
  314. /**
  315. * 按回车发送
  316. */
  317. $("#chat_dialogForOne textarea[name='message']").on("keydown",function(e){
  318. if(e.keyCode == 13 && e.ctrlKey){
  319. $(this).val($(this).val() + "\n");
  320. }else if(e.keyCode == 13){
  321. e.preventDefault();
  322. chat_sendClick();
  323. }
  324. });
  325. /**
  326. * 发送文件
  327. */
  328. $("#chat_fileMsgForOne").on("change",function(){
  329. //todo 现在不实现
  330. });
  331. /**
  332. * 接收webrtc通道发过来的数据回调
  333. * @param {*} event
  334. */
  335. var filequeue = {};//文件队列,可以应对多个文件同时传送
  336. var onmessage = function(event){
  337. var msg = JSON.parse(event.data);
  338. if(msg.type == "text"){//文字内容
  339. showMessage("chat_dialogForOne",msg,"left");
  340. }else if(msg.type == "file"){//文件,
  341. //todo 现在不实现
  342. }
  343. }
  344. /**
  345. * 接收webrtc通道发过来的视频,语音流
  346. * @param {*} event
  347. */
  348. var onaddstream = function(remoteStream){
  349. var video = $("#chat_dialogForOne video[name='remote']")[0];
  350. video.srcObject = remoteStream;
  351. video.onloadedmetadata = function(e) {
  352. video.play();
  353. };
  354. }
  355. /**
  356. * 重置视频,语音按钮状态
  357. */
  358. var resetVideoButton = function(){
  359. var openVideo = $("#chat_dialogForOne button[name='openVideo']");
  360. var openAudio = $("#chat_dialogForOne button[name='openAudio']");
  361. openVideo.find(" > span").html("开始视频");
  362. openAudio.show();
  363. openAudio.find(" > span").html("开始语音");
  364. openVideo.show();
  365. openVideo.removeClass("active");
  366. openAudio.removeClass("active");
  367. openAudio.data("use",false);
  368. openVideo.data("use",false);
  369. }
  370. /**
  371. * 打开,关闭视频聊天按钮
  372. */
  373. $("#chat_dialogForOne button[name='openVideo']").on("click",function(){
  374. $(this).toggleClass("active");
  375. if(!$(this).data("use")){//开启视频语音聊天
  376. $(this).data("use",true);
  377. rtc.openVideoAudioLocal(function(localStream){//创建本地视频流,绑定到控件上
  378. var video = $("#chat_dialogForOne video[name='video']")[0]; //获取到展现视频的标签
  379. video.srcObject=localStream;
  380. video.onloadedmetadata = function(e) {
  381. video.play();
  382. };
  383. rtc.sendAddStream(localStream);
  384. },true,true);//为了防止自己能听到自己发出的声音,可以只启动视频,不启动音频,然后再创建一个新的开启语音,视频的对象发给远程
  385. $(this).find(" > span").html("结束视频");
  386. $("#chat_dialogForOne button[name='openAudio']").hide();
  387. }else{//关闭视频语音聊天
  388. rtc.closeStream();
  389. resetVideoButton();
  390. }
  391. });
  392. /**
  393. * 打开,关闭语音聊天按钮
  394. */
  395. $("#chat_dialogForOne button[name='openAudio']").on("click",function(){
  396. $(this).toggleClass("active");
  397. $(this).data("use",$(this).data("use") ? false : true);
  398. if($(this).data("use")){//开启语音聊天
  399. rtc.openVideoAudioLocal(function(localStream){//创建本地视频流,绑定到控件上
  400. var video = $("#chat_dialogForOne video[name='video']")[0]; //获取到展现视频的标签
  401. video.srcObject=localStream;
  402. video.onloadedmetadata = function(e) {
  403. video.play();
  404. };
  405. rtc.sendAddStream(localStream);
  406. },false,true);//为了防止自己能听到自己发出的声音,可以启动音频后不添加到本地,只发给远程
  407. $(this).find(" > span").html("结束语音");
  408. $("#chat_dialogForOne button[name='openVideo']").hide();
  409. }else{//关闭语音聊天
  410. rtc.closeStream();
  411. resetVideoButton();
  412. }
  413. });
  414. /**
  415. * 关闭对话框
  416. */
  417. $("#chat_dialogForOne").on("hidden.bs.modal",function(e){
  418. resetVideoButton();
  419. if(rtc != null){
  420. rtc.close();
  421. rtc = null;
  422. chat_sendMsg(4,"");//向服务端发起取消对话
  423. }
  424. //清除聊天记录 chat_dialogForOne bubbleDiv
  425. var lis = $(this).find("ul[name='bubbleDiv'] > li");
  426. for (var i = 0 ; i < lis.length; i++){
  427. $(lis[i]).remove();
  428. }
  429. });
  430. /**
  431. * 像服务端发送数据,返回是否发送成功
  432. * @param {Int} type ,发送数据类型,
  433. * type=0发送身份标识,
  434. * @param {string} msg
  435. */
  436. var chat_sendMsg = function(type,msg){
  437. if(chat_socket == null || chat_socket == 0){
  438. return false;
  439. }
  440. chat_socket.send(type,msg);
  441. return true;
  442. }
  443. /**
  444. * 是否同意对话 i 1是 0 否
  445. * @param {*} i
  446. */
  447. var chat_ready = function(i){
  448. chat_sendMsg("3",i);//回复对方
  449. if(i == 1){//同意
  450. var uid = $("#chat_ready_id").data("uid");
  451. var dialogfor = $("#chat_dialogForOne");
  452. var dialogforname = dialogfor.find("label[name='name']");
  453. rtc = rtc_getTool(onmessage,onaddstream,function(){
  454. dialogfor.modal("hide");
  455. },function(){
  456. dialogforname.html($("#chat_ready_id").html());
  457. dialogfor.modal("show");
  458. });
  459. rtc.createPeerConnection();
  460. var bt = $("#u" + uid);
  461. bt.html("取消对话");
  462. bt.removeClass("btn-success");
  463. bt.addClass("btn-primary");
  464. }
  465. $("#chat_dialog").modal('hide');
  466. }
  467. /**
  468. * 添加好友发送的内容到聊天面板
  469. * @param {*} showId 聊天框id
  470. * @param {*} message 消息json对象
  471. * @param {*} is_i right or left代表别人说的话
  472. */
  473. var showMessage = function(showId,message,is_i){
  474. if(message.data.length != 0){
  475. var li = $('<li class="bubbleItem clearfix">');
  476. var img = $('<img src="https://tse4-mm.cn.bing.net/th?id=OIP.hfxD_t92dafBqI_1EADiHAHaFG&p=0&o=5&pid=1.1" height="35px;" style="float: '+is_i+';">');
  477. var span = $('<span class="bubble '+is_i+'Bubble">');
  478. var you_msg = $('<span>');
  479. you_msg.html(message.data);
  480. span.append(you_msg);
  481. var bottomLevel = $('<span class="bottomLevel">');
  482. span.append(bottomLevel);
  483. var bottomLevel = $('<span class="topLevel">');
  484. span.append(bottomLevel);
  485. li.append(img);
  486. var div = $("<div style='float:"+is_i+";max-width: 60%;'>");
  487. var name = $('<label style="font-size:12px;float:'+is_i+';margin-'+is_i+':10px;">昵称['+message.id+']</label>');
  488. div.append(name);
  489. div.append($('<br/>'));
  490. div.append(span);
  491. li.append(div);
  492. $("#"+showId+" ul[name='bubbleDiv']").append(li);
  493. $("#"+showId + " textarea[name='message']").val("");
  494. }
  495. }