PageSourceSearch

https://www.taiwanexpoasean.com/chatw/js/chat.js?_v=1791146483630

js taiwanexpoasean.com collected 2026-10-04 20:42:11 UTC 25,119 bytes, 767 lines download raw bytes

1/**
2  chat.js
3  Author     : Peter, Chen
4  Create Date: 2016-10-13
5  Last Modify: 2016-11-04
6**/
7
8var Chat = {};
9
10Chat._socket = null;
11Chat.currentFocusRoomID = "";
12Chat.focus = "N";;
13Chat.url = window.location.hostname + "/chat/ws";
14 
15Chat._isReconnect = false;
16Chat._ReconnectTime = 0;
17Chat.messageHandler = new Array();
18Chat.addMessageHandler = function(id, handler) {
19	Chat.messageHandler[id] = handler;
20};
21
22Chat.messageQueue = new Array();
23Chat.addMessageQueue = function(id, queue){
24    Chat.messageQueue[id] = queue;
25};
26Chat.getMessageQueue = function(id){
27    return Chat.messageQueue[id];
28};
29
30Chat.roomHandler;
31Chat.roomFocusHandler;
32Chat.roomErrorHandler;
33Chat.listEndHandler;
34Chat.connectedHandler;
35
36Chat.videoHandler = new Array();
37Chat.addVideoHandler = function(id, handler) {
38    Chat.videoHandler[id] = handler;
39};
40Chat.videoErrorHandler;
41Chat.webrtcMap = new Map();
42Chat.videoCallStream;
43Chat.camArray = [];
44Chat.currentCamId = "";
45Chat.ackVideo=false;
46
47Chat.deviceConstraints = {"video":false, "audio":false};
48
49Chat._connect = (function(host) {
50    if ("WebSocket" in window) {
51        Chat._socket = new WebSocket(host);
52    } else if ("MozWebSocket" in window) {
53        Chat._socket = new MozWebSocket(host);
54    } else {  
55    	  alert("Error: WebSocket is not supported by this browser."); 
56        console.log("Error: WebSocket is not supported by this browser.");
57        return;
58    }
59    
60    Chat._socket.onopen = function () {
61        console.log(new Date().toTimeString() + "Info: WebSocket connection opened.");
62        if(Chat._isReconnect) {
63        	  Chat._ReconnectTime =0;
64        }
65        Chat._list();
66        if(Chat.connectedHandler){
67            Chat.connectedHandler();	
68        }
69    };
70
71    Chat._socket.onclose = function (e) {
72        console.log(new Date().toTimeString() + "Info: WebSocket closed.");
73        console.log(new Date().toTimeString() + "Info: error:" + e.reason);
74        
75        if(e.reason.indexOf("The web application is stopping")>-1) {
76            return;	
77        }
78        if(e.reason.indexOf("An unrecoverable IOException occurred so the connection was closed")>-1) {
79            return;	
80        }
81        
82        console.log(new Date().toTimeString() + "Info: Reconnect will be attempted in 1 second.");
83        
84        setTimeout(function() {
85              Chat._isReconnect = true;
86              Chat._ReconnectTime = Chat._ReconnectTime +1;
87              if (window.location.protocol == "http:") {
88              	  Chat._connect("ws://" + Chat.url);
89              } else {
90              	  Chat._connect("wss://" + Chat.url);
91              }
92        }, 1000);
93    };
94
95    Chat._socket.onmessage = function (message) {
96    	  //console.log("Info: Chat.socket.onmessage " + message.data);
97    	  var info = JSON.parse(message.data);
98    	  var cmd = info.cmd;
99    	  
100    	  if(cmd.match(/^(timeout)$/)){
101    	     console.log("Info: timeout");
102    	     return;
103    	  }    	  
104    	  
105    	  if(cmd.match(/^(RL)$/) && Chat.roomHandler){ //載入 room 清單
106    	  	
107    	  	  console.log("Info: [receive] RL:" + info.data.length);
108    	  	  for(var i=0; i<info.data.length; i++){
109    	  	  	var room = info.data[i];
110    	  	  	Chat.roomHandler(room); //Ref: ChatRoom
111    	  	  	console.log("Info: [receive] RL:" + JSON.stringify(room));
112    	  	  }
113    	  	  if(Chat.listEndHandler){
114              Chat.listEndHandler();
115              console.log("Info: [receive] RL:end");
116    	      }
117    	      
118    	  }else
119        if(cmd.match(/^(RC)$/) && Chat.roomHandler){ //開啟新的 room 對話
120        	  
121        	  var room = info.data; //Ref: ChatRoom
122            if(Chat.messageHandler[room.id]==null){
123            	 Chat.roomHandler(room);
124            	 console.log("Info: [receive] RC id:" + room.id);
125            }
126             
127    	  }else
128        if(cmd.match(/^(RF)$/) && Chat.roomFocusHandler){ //focus 在 room 對話
129        	  
130        	  var room = info.data; //Ref: ChatRoom
131        	  if(Chat.currentFocusRoomID==room.id){
132                console.log("Info: [receive] RF id:" + room.id + " skip");
133            }else{
134            	  Chat.currentFocusRoomID = room.id
135                Chat.roomFocusHandler(room.id);
136                console.log("Info: [receive] RF id:" + room.id);
137             }
138             
139    	  }else
140    	  if(cmd.match(/^(M|ML|MLUP|MR)$/)){ //m=訊息, ML=訊息清單, MLUP=訊息上捲, MR=已讀
141    	  	
142    	  	  console.log("Info: [receive] " + cmd + ":" + info.data.length);
143            for(var i=0; i<info.data.length; i++){
144            	  var msg = info.data[i]; //Ref: ChatMessage
145            	  var handler = Chat.messageHandler[msg.roomID];
146            	  if(handler){
147            	  	  //console.log("" + JSON.stringify(msg));
148                    handler(cmd, msg);
149                    
150                    //處裡多 tab 終止視訊
151                    if(cmd.match(/^(M)$/) && msg.message.match(/^(call_reject|call_end|call_busy)$/)){
152                    	  var handler = Chat.videoHandler[msg.roomID];
153                    	  Chat.ackVideo = false;                         	  
154                        if(handler){
155                        	  handler(cmd, msg, true);
156                        }
157                    }
158                    
159                }else{
160                	  console.log("Info: [receive] messageHandler is null " + msg.roomID);
161                }
162            }
163            
164        }else
165        if(cmd.match(/^(ERR)$/) && Chat.roomErrorHandler){ //錯誤
166        	
167        	 console.log("Info: [receive] ERR " + info.data);
168        	 if(info.data=="R1"){
169        	     Chat.roomErrorHandler();
170        	 }else
171        	 if(info.data=="L1"){
172        	 	   alert("重複登入!!");
173        	 }
174        	 
175        }else
176        if(Chat.ackVideo && cmd.match(/^(VC_CR_R)$/)){  //視訊指令 B->A 視訊通話請求回應
177        	
178            var msg = info.data[0]; //Ref: ChatMessage
179            console.log("Info: [receive] Video cmd:" + cmd );
180            var handler = Chat.videoHandler[msg.roomID];
181            if(handler){
182                handler(cmd, msg, false);
183            }
184		    }else	
185        if(Chat.ackVideo && cmd.match(/^(VC_CR_CL|VC_BUSY)$/)){  //視訊指令 A->B 視訊通話請求取消/忙線
186        	
187            var msg = info.data[0]; //Ref: ChatMessage
188            console.log("Info: [receive] Video cmd:" + cmd );
189            var handler = Chat.videoHandler[msg.roomID];
190            if(handler){
191            	  Chat.ackVideo = false;
192                handler(cmd, msg, false);
193            }
194		    }else
195        if(cmd.match(/^(VC_CR)$/)){  //視訊指令 B方處理A方的視訊通話請求            
196            
197            var msg = info.data[0]; //Ref: ChatMessage
198            console.log("Info: [receive] Video cmd:" + cmd );
199            if(Chat.webrtcMap.size>0){ //確認是否通話中
200            	  Chat.sendBusy(msg.roomID);
201                return;	
202            }
203            var handler = Chat.videoHandler[msg.roomID];
204            if(handler){      
205            	  Chat.ackVideo = true;      	         	  
206                handler(cmd, msg, false);
207            }
208        }else
209        if(Chat.ackVideo && cmd.match(/^(VC_HG)$/)){  //視訊指令 A方主動掛斷  通知B方掛斷
210        	
211        	  console.log("Info: [receive] Video cmd:" + cmd );
212            // B方處理掛斷
213            var msg = info.data[0]; //Ref: ChatMessage
214            var webrtc = Chat.webrtcMap.get(msg.roomID);
215            if(webrtc) {
216                Chat.webrtcClose(msg.roomID);
217                Chat.stopCam();
218            }
219            //UI 控制
220            var handler = Chat.videoHandler[msg.roomID];
221            if(handler){
222                handler(cmd, msg, false);
223            } 
224        }else
225        if(cmd.match(/^(VC_OFR)$/)){ //視訊指令  A方透過singal server將offer傳給B方
226        	
227        	  console.log("Info: [receive] Video cmd:" + cmd +",ackVideo:"+Chat.ackVideo);
228        	  if(!Chat.ackVideo){
229        	  	 //其他設備接走了
230        	  	 var msg = info.data[0]; //Ref: ChatMessage
231        	  	 var handler = Chat.videoHandler[msg.roomID];
232        	  	 Chat.ackVideo = false;  
233        	  	 if(handler){
234        	  	    handler(cmd, msg, true);        	  	    
235        	  	 }
236        	  	 console.log("call was picked : VC_OFR-1,Chat.ackVideo:" +Chat.ackVideo);
237        	  }else{
238               // B方處理A方請求
239               var msg = info.data[0]; //Ref: ChatMessage
240			         var webrtc = Chat.webrtcMap.get(msg.roomID);
241               if(webrtc) {
242			           console.log('webrtc.handleOffer');
243                 // B方產生回應
244                 var offer = JSON.parse(msg.message);
245                 webrtc.handleOffer(offer, msg.roomID, Chat);
246                
247                 //UI 控制 
248                 var handler = Chat.videoHandler[msg.roomID];
249                 if(handler){
250                    handler(cmd, msg, false);
251                 }
252               }else{
253                 //多個 tab 已接走            
254                 var handler = Chat.videoHandler[msg.roomID];
255                 Chat.ackVideo = false;  
256                 if(handler){
257                   handler(cmd, msg, true);
258                 }
259                 console.log("call was picked : VC_OFR-2 ,Chat.ackVideo:" +Chat.ackVideo);
260               }
261            }
262        }else
263        if(Chat.ackVideo && cmd.match(/^(VC_ANS)$/)){  //視訊指令  B方透過singal server將answer傳給A方
264        	
265        	  console.log("Info: [receive] Video cmd:" + cmd );
266            // A方處理B方回應
267            var msg = info.data[0]; //Ref: ChatMessage
268         	  var webrtc = Chat.webrtcMap.get(msg.roomID);
269         	  if(webrtc) {
270	              var answer = JSON.parse(msg.message);
271	              webrtc.handleAnswer(answer);
272	          }else{
273	          	  console.log("call was picked : VC_ANS");
274	          }
275        }else
276        if(Chat.ackVideo && cmd.match(/^(VC_CAD)$/)){  //視訊指令
277        	
278        	  console.log("Info: [receive] Video cmd:" + cmd );
279            // A方或B方收到candidate
280            var msg = info.data[0]; //Ref: ChatMessage
281	          var webrtc = Chat.webrtcMap.get(msg.roomID);
282	          if(webrtc) {
283	              var candidate = JSON.parse(msg.message);
284	              webrtc.handleCandidate(candidate);
285	          }else{	          	  
286	          	  var handler = Chat.videoHandler[msg.roomID];
287	          	  Chat.ackVideo = false;
288                if(handler){                	  
289                    handler(cmd, msg, true);                    
290                }
291	              console.log("call was picked : VC_CAD");
292	          }
293        }
294    };
295});
296
297Chat.initialize = function() {
298    if (window.location.protocol == "http:") {
299    	  Chat._connect("ws://" + Chat.url);
300    } else {
301    	  Chat._connect("wss://" + Chat.url);
302    }
303};
304
305Chat._list = function(){
306    var info = {};
307    info.cmd = "RL";
308    info.focus = Chat.focus;
309    Chat._socket.send(JSON.stringify(info));	  
310    console.log("Info: [send] " + JSON.stringify(info));
311};
312
313Chat.create = function(exhibitorID){
314    var info = {};
315    info.cmd = "RC";
316    info.exhibitorID = exhibitorID;
317    Chat._socket.send(JSON.stringify(info));
318    console.log("Info: [send] " + JSON.stringify(info));
319};
320
321Chat.open = function(chatID){
322    var info = {};
323    info.cmd = "RO";
324    info.chatID = chatID;
325    Chat._socket.send(JSON.stringify(info));
326    console.log("Info: [send] " + JSON.stringify(info));
327};
328
329Chat.chat = function(chatID, message){
330    var info = {};
331    info.cmd = "M";
332    info.chatID = chatID;
333    info.message = message;
334    Chat._socket.send(JSON.stringify(info));
335    console.log("Info: [send] " + JSON.stringify(info));
336};
337
338Chat.read = function(chatID, msgIndex){
339    var info = {};
340    info.cmd = "MR";
341    info.chatID = chatID;
342    info.msgIndex = msgIndex;
343    Chat._socket.send(JSON.stringify(info));
344    console.log("Info: [send] " + JSON.stringify(info));
345};
346
347Chat.readAll = function(chatID){
348    var info = {};
349    info.cmd = "MRA";
350    info.chatID = chatID;
351    Chat._socket.send(JSON.stringify(info));
352    console.log("Info: [send] " + JSON.stringify(info));
353};
354
355Chat.upmessage = function(chatID, currentMsgIndex){
356    var info = {};
357    info.cmd = "MLUP";
358    info.chatID = chatID;
359    info.currentMsgIndex = currentMsgIndex;
360    Chat._socket.send(JSON.stringify(info));
361    console.log("Info: [send] " + JSON.stringify(info));
362};
363
364//Chat.remove = function(chatID){
365//	  var info = {};
366//	  info.cmd = "RR";
367//	  info.chatID = chatID;
368//	  Chat._socket.send(JSON.stringify(info));
369//	  console.log("Info: [send] " + JSON.stringify(info));
370//};
371
372Chat.hidden = function(chatID){
373    var info = {};
374    info.cmd = "RH";
375    info.chatID = chatID;
376    Chat._socket.send(JSON.stringify(info));
377    console.log("Info: [send] " + JSON.stringify(info));
378};
379
380Chat.mark = function(chatID){
381    var info = {};
382    info.cmd = "RM";
383    info.chatID = chatID;
384    Chat._socket.send(JSON.stringify(info));
385    console.log("Info: [send] " + JSON.stringify(info));
386};
387
388Chat.markRemove = function(chatID){
389    var info = {};
390    info.cmd = "RMR";
391    info.chatID = chatID;
392    Chat._socket.send(JSON.stringify(info));
393    console.log("Info: [send] " + JSON.stringify(info));
394};
395
396Chat.dateFormat = function(s,lang){
397    //var yy = s.substring(0,4);
398    //var MM = s.substring(4,6);
399    //var dd = s.substring(6,8);
400    //var HH = s.substring(8,10);
401    //var mm = s.substring(10,12);
402    //return yy + "/" + MM + "/" + dd + " " + HH + ":" + mm; 
403    return new Date(s).toLocaleString(lang);
404};
405
406
407/***************************************************************************************************************************************/
408
409Chat.videoCallRequest = function(chatID) {
410    // A->B 視訊通話請求
411    var info = {};
412    info.cmd = "VC_CR";
413    info.chatID = chatID;
414    Chat._socket.send(JSON.stringify(info));
415    console.log("Info: [send] " + JSON.stringify(info));
416};
417
418Chat.videoCallRequestCancel = function(chatID){
419    // A->B 視訊通話請求取消
420    var info = {};
421    info.cmd = "VC_CR_CL";
422    info.chatID = chatID;
423    Chat._socket.send(JSON.stringify(info));
424    console.log("Info: [send] " + JSON.stringify(info));
425};
426
427Chat.videoCallRequestResult = function(chatID, message){
428    // B->A 視訊通話請求回應
429    var info = {};
430    info.cmd = "VC_CR_R";
431    info.chatID = chatID;
432    info.message = message;
433    Chat._socket.send(JSON.stringify(info));
434    console.log("Info: [send] " + JSON.stringify(info));
435};
436
437Chat.hangup = function(chatID, callback){
438    // A方主動掛斷
439    // 通知B方掛斷
440    var info = {};
441    info.cmd = "VC_HG";
442    info.chatID = chatID;
443    Chat._socket.send(JSON.stringify(info));
444    console.log("Info: [send] " + JSON.stringify(info));
445    
446    Chat.webrtcClose(chatID, callback);
447    
448    Chat.stopCam();
449};
450
451Chat.webrtcConnect = function(chatID, callback,isCaller){
452    console.log("==============>webrtcConnect...." + chatID);
453    var webrtc = new WebRTC();
454    Chat.webrtcMap.set(chatID, webrtc);
455    webrtc.connectRTC(chatID, this,isCaller);
456    if(callback){
457        callback(chatID);	
458    }
459    return webrtc;
460};
461
462Chat.webrtcClose = function(chatID, callback){
463	  var webrtc = Chat.webrtcMap.get(chatID);
464	  Chat.webrtcMap.delete(chatID);	
465	  webrtc.disconnectRTC();	
466    if(callback){
467        callback(chatID);	
468    }	  
469};
470
471Chat.sendBusy = function(chatID){
472    // A方透過singal server將offer傳給B方
473    var info = {};
474    info.cmd = "VC_BUSY";
475    info.chatID = chatID;
476    Chat._socket.send(JSON.stringify(info));
477    console.log("Info: [send] " + JSON.stringify(info));
478};
479
480Chat.sendOffer = function(chatID, offer){
481    // A方透過singal server將offer傳給B方
482    var info = {};
483    info.cmd = "VC_OFR";
484    info.chatID = chatID;
485    info.message = JSON.stringify(offer);
486    Chat._socket.send(JSON.stringify(info));
487    console.log("Info: [send] " + JSON.stringify(info));
488};
489
490Chat.sendAnswer = function(chatID,answer){
491    // B方透過singal server將answer傳給A方
492  if(Chat.ackVideo){
493    var info = {};
494    info.cmd = "VC_ANS";
495    info.chatID = chatID;
496    info.message = JSON.stringify(answer);
497    Chat._socket.send(JSON.stringify(info));
498    console.log("Info: [send] " + JSON.stringify(info));
499  }
500};
501
502Chat.sendCandidate = function(chatID,candidate){
503    // A方,B方透過singal server交換candidate
504    var info = {};
505    info.cmd = "VC_CAD";
506    info.chatID = chatID;
507    info.message = JSON.stringify(candidate);
508    Chat._socket.send(JSON.stringify(info));
509    console.log("Info: [send] " + JSON.stringify(info));
510};
511
512Chat.rtcFail = function(chatID){
513	  cameraCheckStatus=false;
514    var msg = "webrtc連線失敗,請聯繫單位網管檢查防火牆設定";
515    console.log(msg);
516	  if(Chat.videoErrorHandler){
517	      Chat.videoErrorHandler("rtc_newwork_error");	
518	  }else{
519	      $("#video_error").html(msg);
520	  }
521};
522
523Chat.showDeviceError = function(error){
524	  cameraCheckStatus=false;
525    var msg = "無法使用電腦視訊設備,請確認瀏覽器與作業系統兩者都已設定允許使用電腦視訊設備,且無其他程式正在使用電腦視訊設備:" + error;
526    console.log(msg);	  
527	  if(Chat.videoErrorHandler){
528	      Chat.videoErrorHandler("device_error");	
529	  }else{
530	      $("#video_error").html(msg);
531	  }
532};
533
534Chat.sendRTCMsg = function(chatID,msg){
535    var webrtc = Chat.webrtcMap.get(chatID);
536    webrtc.send(msg);
537};
538
539Chat.updateCamArray = async function(){
540    console.log("Trace: updateCamArray");
541    try{
542    	await navigator.mediaDevices.enumerateDevices().
543    	then((deviceInfos)=>{
544            console.log("updateCamArray-> got deviceInfos");
545            for (let i = 0; i !== deviceInfos.length; ++i) {
546                const deviceInfo = deviceInfos[i];
547                if (deviceInfo.kind === "videoinput") {
548                    console.log("deviceInfo.deviceId:" + deviceInfo.deviceId);
549                    console.log("deviceInfo.label:" + deviceInfo.label);
550                    Chat.camArray.push(deviceInfo.deviceId);
551                }
552            }
553       }).
554       catch((error)=>{
555       	  console.log("navigator.MediaDevices.getUserMedia error:", error.message, error.name);
556       	  Chat.openCam();
557       	}
558       );
559    }
560    catch(error){
561        Chat.showDeviceError(error);
562        // promise reject
563        throw error;
564    }
565};
566
567Chat.openCam = function(){
568	  console.log("Chat.openCam");
569    if (Chat.currentCamId && Chat.currentCamId.length > 0) {
570        return navigator.mediaDevices.getUserMedia( { video: { deviceId: {exact:Chat.currentCamId}, width: 500, height:500}, audio: true});
571    } else {
572        return navigator.mediaDevices.getUserMedia({ video: { width: 500, height: 500 }, audio: true});
573    }
574};
575
576Chat.openAudioOnly = function(){
577	  console.log("Chat.openAudioOnly");
578	  return navigator.mediaDevices.getUserMedia({video: false, audio: true});
579};
580
581Chat.openCamOne = function(resolve){
582    Chat.currentCamId = Chat.camArray[0];
583    Chat.openCam().then((stream1)=>{
584        Chat.videoCallStream = stream1;
585        resolve();
586    }).catch((error1)=>{
587    	 console.log("openCam(with video/audio) error:", error1.message, error1.name);
588         Chat.openAudioOnly().then((stream1)=>{
589			    console.log('audio only');
590				Chat.videoCallStream = stream1;
591				resolve();
592			 }).catch((error)=>{
593			   console.log("openAudio(audio only) error:", error.message, error.name);
594			   Chat.showDeviceError(error);    	 
595			 });
596    });
597};
598
599Chat.stopCam = function(){
600    console.log('stopCam:'+Chat.currentCamId);
601    Chat.videoCallStream.getTracks().forEach(function(track) {
602        track.stop();
603    });
604};
605
606Chat.videoStart = function(chatID, callback){
607    Chat.openMedia().then(()=>{
608       var webrtc = Chat.webrtcConnect(chatID, function(chatID){
609          callback(chatID);
610       },true);
611	   
612       // A方提出申請   
613       //console.log('webrtc.createOffer');
614       //webrtc.createOffer(chatID, Chat);
615    }); 
616};
617
618Chat.videoRemoteAccept = function(chatID,prepareReceiveRemoteVideo){
619	console.log('被呼叫方接受請求');
620    Chat.openMedia().then(()=>{
621		Chat.webrtcConnect(chatID,null,false);
622		prepareReceiveRemoteVideo(chatID);
623        Chat.videoCallRequestResult(chatID, "accept");
624    }); 
625};
626
627Chat.videoRemoteReject = function(chatID){
628    Chat.openMedia().then(()=>{
629        Chat.videoCallRequestResult(chatID, "reject");
630        Chat.stopCam(); 
631    }); 
632};
633
634Chat.videoAttachRemoteStream = function(chatID, remoteVideo){
635	var webrtc = Chat.webrtcMap.get(chatID);
636    webrtc.showRemoteStream(remoteVideo);
637};
638
639Chat.openMedia = async function(){
640    return new Promise(function(resolve, reject) { 
641        console.log("Trace openMedia");
642        if(Chat.camArray.length <= 0){
643            Chat.updateCamArray().then(()=>{
644                Chat.openCamOne(resolve);
645            });
646        }
647        else{
648        	  console.log("Trace: call openCamOne");
649            Chat.openCamOne(resolve);
650        }
651    });
652};
653
654
655Chat.checkDevice = function(callback){  
656console.log("Chat.checkDevice , Chat.ackVideo:"+Chat.ackVideo);
657if(Chat.ackVideo){	
658    if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
659       callback(false);	
660       console.log(navigator.mediaDevices );
661       //return;
662    }
663    
664    navigator.mediaDevices.enumerateDevices().then(function(devices) {
665    	console.log("Chat.checkDevice mediaDevices , Chat.ackVideo:"+Chat.ackVideo); 
666      if(Chat.ackVideo){	  
667        devices.forEach(function(device) {
668            if (device.kind === "videoinput") {
669                Chat.deviceConstraints.video = true;
670            } else if (device.kind === "audioinput") {
671                Chat.deviceConstraints.audio = true;
672            }
673        });
674        
675        console.log("media constraints:" + JSON.stringify(Chat.deviceConstraints) + " try to getUserMedia pormise...");
676        
677        navigator.mediaDevices.getUserMedia(Chat.deviceConstraints)
678           .then(stream => {
679              console.log("Got MediaStream:", stream);
680              callback(true);
681              console.log("========================================= execute call back....");
682              stream.getTracks().forEach(function(track) {
683                track.stop();
684              })
685           })
686           .catch(error => {
687              console.log("Error accessing media devices");
688              Chat.showDeviceError(error);
689              callback(false);
690        });
691      }
692    });
693  }
694};
695
696
697Chat.switchCam = function(chatID, callback){
698    try {
699        Chat.stopCam();
700        if(Chat.camArray.length >= 2){
701            if(Chat.camArray[0]===Chat.currentCamId){
702                Chat.currentCamId = Chat.camArray[1];
703            }else{
704                Chat.currentCamId = Chat.camArray[0];
705            }
706        }
707        console.log("switchCam:" + Chat.currentCamId);
708        Chat.openCam().then((stream1)=>{
709            console.log("openCam then");
710            // 改變傳給對方的視訊源
711            var webrtc = Chat.webrtcMap.get(chatID);
712            if(webrtc){
713                webrtc.replaceVideo(stream1);
714            }
715            
716            //Chat.changeSelfVideoStream(chatID,stream1);
717            
718            if(callback){
719                callback(chatID, stream1);	
720            }
721            
722            Chat.videoCallStream = stream1;
723        });
724    }
725    catch (e) {
726        console.log("切換鏡頭失敗:" + e, "r");
727    }	
728};
729
730Chat.pauseVideo = function(chatID){
731    var webrtc = Chat.webrtcMap.get(chatID);
732    webrtc.pauseVideo();
733};
734
735Chat.resumeVideo = function(chatID){
736    var webrtc = Chat.webrtcMap.get(chatID);
737    webrtc.resumeVideo();
738};
739
740Chat.pauseAudio = function(chatID){
741    var webrtc = Chat.webrtcMap.get(chatID);
742    webrtc.pauseAudio();
743};
744
745Chat.resumeAudio = function(chatID){
746    var webrtc = Chat.webrtcMap.get(chatID);
747    webrtc.resumeAudio();
748};
749
750Chat.testCam = function(chatID){
751    Chat.openMedia().then(()=>{
752        console.log("openMedia then");Chat.openSelfVideo(chatID)}
753    );
754};
755
756//window.onbeforeunload = function() {
757//	  console.log(Chat.webrtcMap.size);
758//    return "";
759//};
760
761
762window.onunload = function() {
763	  for (var [key, value] of Chat.webrtcMap) {
764       console.log(key + " leave");
765       Chat.hangup(key);
766    }
767};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.