瀏覽代碼

Adds mute and kick functoinality avaialable for the focus of the conference.

j8
yanas 11 年之前
父節點
當前提交
56424df0a0
共有 11 個檔案被更改,包括 384 行新增31 行删除
  1. 160
    16
      app.js
  2. 0
    1
      css/main.css
  3. 56
    0
      css/popup_menu.css
  4. 5
    5
      css/videolayout_default.css
  5. 二進制
      images/popupPointer.png
  6. 3
    1
      index.html
  7. 43
    0
      libs/colibri/colibri.focus.js
  8. 1
    1
      libs/colibri/colibri.session.js
  9. 29
    3
      libs/strophe/strophe.jingle.js
  10. 50
    0
      moderatemuc.js
  11. 37
    4
      muc.js

+ 160
- 16
app.js 查看文件

@@ -15,6 +15,7 @@ var ssrc2jid = {};
15 15
  */
16 16
 var ssrc2videoType = {};
17 17
 var videoSrcToSsrc = {};
18
+var mutedAudios = {};
18 19
 
19 20
 var localVideoSrc = null;
20 21
 var flipXLocalVideo = true;
@@ -35,6 +36,8 @@ var getVideoPosition;
35 36
 
36 37
 /* window.onbeforeunload = closePageWarning; */
37 38
 
39
+var preMuted = false;
40
+
38 41
 function init() {
39 42
     RTC = setupRTC();
40 43
     if (RTC === null) {
@@ -179,6 +182,10 @@ function change_local_audio(stream) {
179 182
     RTC.attachMediaStream($('#localAudio'), stream);
180 183
     document.getElementById('localAudio').autoplay = true;
181 184
     document.getElementById('localAudio').volume = 0;
185
+    if (preMuted) {
186
+        toggleAudio();
187
+        preMuted = false;
188
+    }
182 189
 }
183 190
 
184 191
 function change_local_video(stream, flipX) {
@@ -485,8 +492,13 @@ $(document).bind('callactive.jingle', function (event, videoelem, sid) {
485 492
     }
486 493
 });
487 494
 
488
-$(document).bind('callterminated.jingle', function (event, sid, reason) {
489
-    // FIXME
495
+$(document).bind('callterminated.jingle', function (event, sid, jid, reason) {
496
+    // Leave the room if my call has been remotely terminated.
497
+    if (connection.emuc.joined && focus == null && reason === 'kick') {
498
+        connection.emuc.doLeave();
499
+        openMessageDialog(  "Session Terminated",
500
+                            "Ouch! You have been kicked out of the meet!");
501
+    }
490 502
 });
491 503
 
492 504
 $(document).bind('setLocalDescription.jingle', function (event, sid) {
@@ -579,15 +591,25 @@ $(document).bind('entered.muc', function (event, jid, info, pres) {
579 591
 });
580 592
 
581 593
 $(document).bind('left.muc', function (event, jid) {
582
-    console.log('left', jid);
594
+    console.log('left.muc', jid);
595
+    // Need to call this with a slight delay, otherwise the element couldn't be
596
+    // found for some reason.
597
+    window.setTimeout(function () {
598
+        var container = document.getElementById(
599
+                'participant_' + Strophe.getResourceFromJid(jid));
600
+        if (container) {
601
+            // hide here, wait for video to close before removing
602
+            $(container).hide();
603
+            resizeThumbnails();
604
+        }
605
+    }, 10);
606
+
583 607
     connection.jingle.terminateByJid(jid);
584
-    var container = document.getElementById('participant_' + Strophe.getResourceFromJid(jid));
585
-    if (container) {
586
-        // hide here, wait for video to close before removing
587
-        $(container).hide();
588
-        resizeThumbnails();
589
-    }
590
-    if (focus === null && connection.emuc.myroomjid === connection.emuc.list_members[0]) {
608
+
609
+    if (focus == null
610
+            && jid !== connection.emuc.myroomjid
611
+            && connection.emuc.myroomjid === connection.emuc.list_members[0]
612
+            && connection.emuc.list_members.length > 1) {
591 613
         console.log('welcome to our new focus... myself');
592 614
         focus = new ColibriFocus(connection, config.hosts.bridge);
593 615
         if (Object.keys(connection.emuc.members).length > 0) {
@@ -603,7 +625,8 @@ $(document).bind('left.muc', function (event, jid) {
603 625
         focus = new ColibriFocus(connection, config.hosts.bridge);
604 626
     }
605 627
     if (connection.emuc.getPrezi(jid)) {
606
-        $(document).trigger('presentationremoved.muc', [jid, connection.emuc.getPrezi(jid)]);
628
+        $(document).trigger('presentationremoved.muc',
629
+                            [jid, connection.emuc.getPrezi(jid)]);
607 630
     }
608 631
 });
609 632
 
@@ -686,6 +709,11 @@ $(document).bind('audiomuted.muc', function (event, jid, isMuted) {
686 709
         videoSpanId = 'participant_' + Strophe.getResourceFromJid(jid);
687 710
     }
688 711
 
712
+    if (focus) {
713
+        mutedAudios[jid] = isMuted;
714
+        updateRemoteVideoMenu(jid, isMuted);
715
+    }
716
+
689 717
     if (videoSpanId)
690 718
         showAudioIndicator(videoSpanId, isMuted);
691 719
 });
@@ -700,7 +728,7 @@ $(document).bind('videomuted.muc', function (event, jid, isMuted) {
700 728
     }
701 729
 
702 730
     if (videoSpanId)
703
-        showAudioIndicator(videoSpanId, isMuted);
731
+        showVideoIndicator(videoSpanId, isMuted);
704 732
 });
705 733
 
706 734
 /**
@@ -821,8 +849,11 @@ function toggleVideo() {
821 849
  * Mutes / unmutes audio for the local participant.
822 850
  */
823 851
 function toggleAudio() {
824
-    if (!(connection && connection.jingle.localAudio))
852
+    if (!(connection && connection.jingle.localAudio)) {
853
+        preMuted = true;
825 854
         return;
855
+    }
856
+
826 857
     var localAudio = connection.jingle.localAudio;
827 858
     for (var idx = 0; idx < localAudio.getAudioTracks().length; idx++) {
828 859
         var audioEnabled = localAudio.getAudioTracks()[idx].enabled;
@@ -831,6 +862,8 @@ function toggleAudio() {
831 862
         connection.emuc.addAudioInfoToPresence(audioEnabled); //isMuted is the opposite of audioEnabled
832 863
         connection.emuc.sendPresence();
833 864
     }
865
+
866
+    buttonClick("#mute", "icon-microphone icon-mic-disabled");
834 867
 }
835 868
 
836 869
 /**
@@ -1167,6 +1200,22 @@ function buttonClick(id, classname) {
1167 1200
     $(id).toggleClass(classname); // add the class to the clicked element
1168 1201
 }
1169 1202
 
1203
+/**
1204
+ * Shows a message to the user.
1205
+ *
1206
+ * @param titleString the title of the message
1207
+ * @param messageString the text of the message
1208
+ */
1209
+function openMessageDialog(titleString, messageString) {
1210
+    console.log("OPEN MESSAGE DIALOG");
1211
+    $.prompt(messageString,
1212
+        {
1213
+            title: titleString,
1214
+            persistent: false
1215
+        }
1216
+    );
1217
+}
1218
+
1170 1219
 /**
1171 1220
  * Opens the lock room dialog.
1172 1221
  */
@@ -1448,7 +1497,7 @@ function ensurePeerContainerExists(peerJid) {
1448 1497
         return;
1449 1498
     }
1450 1499
 
1451
-    var container = addRemoteVideoContainer(videoSpanId);
1500
+    var container = addRemoteVideoContainer(peerJid, videoSpanId);
1452 1501
 
1453 1502
     var nickfield = document.createElement('span');
1454 1503
     nickfield.className = "nick";
@@ -1457,11 +1506,15 @@ function ensurePeerContainerExists(peerJid) {
1457 1506
     resizeThumbnails();
1458 1507
 }
1459 1508
 
1460
-function addRemoteVideoContainer(id) {
1509
+function addRemoteVideoContainer(peerJid, spanId) {
1461 1510
     var container = document.createElement('span');
1462
-    container.id = id;
1511
+    container.id = spanId;
1463 1512
     container.className = 'videocontainer';
1464 1513
     var remotes = document.getElementById('remoteVideos');
1514
+
1515
+    if (focus)
1516
+        addRemoteVideoMenu(peerJid, container);
1517
+
1465 1518
     remotes.appendChild(container);
1466 1519
     return container;
1467 1520
 }
@@ -1476,6 +1529,97 @@ function createFocusIndicatorElement(parentElement) {
1476 1529
     parentElement.appendChild(focusIndicator);
1477 1530
 }
1478 1531
 
1532
+function addRemoteVideoMenu(jid, parentElement) {
1533
+    var spanElement = document.createElement('span');
1534
+    spanElement.className = 'remotevideomenu';
1535
+
1536
+    parentElement.appendChild(spanElement);
1537
+
1538
+    var menuElement = document.createElement('i');
1539
+    menuElement.className = 'fa fa-angle-down';
1540
+    menuElement.title = 'Remote user controls';
1541
+    spanElement.appendChild(menuElement);
1542
+
1543
+//    <ul class="popupmenu">
1544
+//    <li><a href="#">Mute</a></li>
1545
+//    <li><a href="#">Eject</a></li>
1546
+//    </ul>
1547
+    var popupmenuElement = document.createElement('ul');
1548
+    popupmenuElement.className = 'popupmenu';
1549
+    popupmenuElement.id = 'remote_popupmenu_' + Strophe.getResourceFromJid(jid);
1550
+    spanElement.appendChild(popupmenuElement);
1551
+
1552
+    var muteMenuItem = document.createElement('li');
1553
+    var muteLinkItem = document.createElement('a');
1554
+
1555
+    var mutedIndicator = "<i class='icon-mic-disabled'></i>";
1556
+
1557
+    if (!mutedAudios[jid]) {
1558
+        muteLinkItem.innerHTML = mutedIndicator + 'Mute';
1559
+        muteLinkItem.className = 'mutelink';
1560
+    }
1561
+    else {
1562
+        muteLinkItem.innerHTML = mutedIndicator + ' Muted';
1563
+        muteLinkItem.className = 'mutelink disabled';
1564
+    }
1565
+
1566
+    muteLinkItem.onclick = function(){
1567
+        if ($(this).attr('disabled') != undefined) {
1568
+            event.preventDefault();
1569
+        }
1570
+        var isMute = !mutedAudios[jid];
1571
+        connection.moderate.setMute(jid, isMute);
1572
+        popupmenuElement.setAttribute('style', 'display:none;');
1573
+
1574
+        if (isMute) {
1575
+            this.innerHTML = mutedIndicator + ' Muted';
1576
+            this.className = 'mutelink disabled';
1577
+        }
1578
+        else {
1579
+            this.innerHTML = mutedIndicator + ' Mute';
1580
+            this.className = 'mutelink';
1581
+        }
1582
+    };
1583
+
1584
+    muteMenuItem.appendChild(muteLinkItem);
1585
+    popupmenuElement.appendChild(muteMenuItem);
1586
+
1587
+    var ejectIndicator = "<i class='fa fa-eject'></i>";
1588
+
1589
+    var ejectMenuItem = document.createElement('li');
1590
+    var ejectLinkItem = document.createElement('a');
1591
+    ejectLinkItem.innerHTML = ejectIndicator + ' Kick out';
1592
+    ejectLinkItem.onclick = function(){
1593
+        connection.moderate.eject(jid);
1594
+        popupmenuElement.setAttribute('style', 'display:none;');
1595
+    };
1596
+
1597
+    ejectMenuItem.appendChild(ejectLinkItem);
1598
+    popupmenuElement.appendChild(ejectMenuItem);
1599
+}
1600
+
1601
+function updateRemoteVideoMenu(jid, isMuted) {
1602
+    var muteMenuItem
1603
+        = $('#remote_popupmenu_'
1604
+                + Strophe.getResourceFromJid(jid)
1605
+                + '>li>a.mutelink');
1606
+
1607
+    var mutedIndicator = "<i class='icon-mic-disabled'></i>";
1608
+
1609
+    if (muteMenuItem.length) {
1610
+        var muteLink = muteMenuItem.get(0);
1611
+
1612
+        if (isMuted === 'true') {
1613
+            muteLink.innerHTML = mutedIndicator + ' Muted';
1614
+            muteLink.className = 'mutelink disabled';
1615
+        }
1616
+        else {
1617
+            muteLink.innerHTML = mutedIndicator + ' Mute';
1618
+            muteLink.className = 'mutelink';
1619
+        }
1620
+    }
1621
+}
1622
+
1479 1623
 /**
1480 1624
  * Toggles the application in and out of full screen mode
1481 1625
  * (a.k.a. presentation mode in Chrome).

+ 0
- 1
css/main.css 查看文件

@@ -205,7 +205,6 @@ button {
205 205
 }
206 206
 
207 207
 button, input, select, textarea {
208
-    font-size: 100%;
209 208
     margin: 0;
210 209
     vertical-align: baseline;
211 210
 }

+ 56
- 0
css/popup_menu.css 查看文件

@@ -0,0 +1,56 @@
1
+/*Initialize*/
2
+ul.popupmenu {
3
+    display:none;
4
+    position: absolute;
5
+    padding:0;
6
+    margin: 0;
7
+    bottom: 20px;
8
+    padding-bottom: 5px;
9
+    padding-top: 5px;
10
+    right: 10px;
11
+    left: 0px;
12
+    width: 100px;
13
+    background-color: rgba(0,0,0,1);
14
+    -webkit-box-shadow: 0 0 2px #000000, 0 0 10px #000000;
15
+    border-radius:8px;
16
+}
17
+
18
+ul.popupmenu:after {
19
+    content: url('../images/popupPointer.png');
20
+    display: block;
21
+    position: absolute;
22
+    bottom: -9px;
23
+    left: 13px;
24
+}
25
+
26
+ul.popupmenu li {
27
+    list-style-type: none;
28
+    text-align: left;
29
+}
30
+
31
+ul.popupmenu li:hover {
32
+    background-color: rgba(82, 82, 82, .7);
33
+    border-radius:2px;
34
+}
35
+
36
+/*Link Appearance*/
37
+ul.popupmenu li a {
38
+    text-decoration: none;
39
+    color: #fff;
40
+    padding: 5px;
41
+    display: inline-block;
42
+    font-size: 9pt;
43
+}
44
+
45
+ul.popupmenu li a i {
46
+    width: 12px;
47
+}
48
+
49
+span.remotevideomenu:hover ul.popupmenu {
50
+    display:block !important;
51
+}
52
+
53
+a.disabled {
54
+    color: gray !important;
55
+    pointer-events: none;
56
+}

+ 5
- 5
css/videolayout_default.css 查看文件

@@ -16,9 +16,8 @@
16 16
     left: 0;
17 17
     right: 0;
18 18
     width:auto;
19
-    overflow: hidden;
20 19
     border:1px solid transparent;
21
-    z-index: 2;
20
+    z-index: 5;
22 21
 }
23 22
 
24 23
 .videocontainer {
@@ -41,8 +40,8 @@
41 40
     content:"";
42 41
     cursor: pointer;
43 42
     cursor: hand;
44
-    transform:scale(1.08, 1.08);
45
-    -webkit-transform:scale(1.08, 1.08);
43
+    /* transform:scale(1.08, 1.08);
44
+    -webkit-transform:scale(1.08, 1.08); */
46 45
     transition-duration: 0.5s;
47 46
     -webkit-transition-duration: 0.5s;
48 47
     -webkit-animation-name: greyPulse;
@@ -107,7 +106,8 @@
107 106
     display: none;
108 107
 }
109 108
 
110
-#remoteVideos .videocontainer>span.focusindicator {
109
+#remoteVideos .videocontainer>span.focusindicator,
110
+#remoteVideos .videocontainer>span.remotevideomenu {
111 111
     display: inline-block;
112 112
     position: absolute;
113 113
     color: #FFFFFF;

二進制
images/popupPointer.png 查看文件


+ 3
- 1
index.html 查看文件

@@ -31,6 +31,7 @@
31 31
     <script src="prezi.js?v=2"></script><!-- prezi plugin -->
32 32
     <script src="smileys.js?v=1"></script><!-- smiley images -->
33 33
     <script src="replacement.js?v=5"></script><!-- link and smiley replacement -->
34
+    <script src="moderatemuc.js?v=1"></script><!-- moderator plugin -->
34 35
     <script src="analytics.js?v=1"></script><!-- google analytics plugin -->
35 36
     <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
36 37
     <link rel="stylesheet" href="css/font.css"/>
@@ -38,6 +39,7 @@
38 39
     <link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=4" id="videolayout_default"/>
39 40
     <link rel="stylesheet" href="css/jquery-impromptu.css?v=4">
40 41
     <link rel="stylesheet" href="css/modaldialog.css?v=3">
42
+    <link rel="stylesheet" href="css/popup_menu.css?v=1">
41 43
     <!--
42 44
         Link used for inline installation of chrome desktop streaming extension,
43 45
         is updated automatically from the code with the value defined in config.js -->
@@ -49,7 +51,7 @@
49 51
   <body>
50 52
     <div id="header">
51 53
         <span id="toolbar">
52
-            <a class="button" onclick='buttonClick("#mute", "icon-microphone icon-mic-disabled");toggleAudio();'>
54
+            <a class="button" onclick='toggleAudio();'>
53 55
                 <i id="mute" title="Mute / unmute" class="icon-microphone"></i></a>
54 56
             <div class="header_button_separator"></div>
55 57
             <a class="button" onclick='buttonClick("#video", "icon-camera icon-camera-disabled");toggleVideo();'>

+ 43
- 0
libs/colibri/colibri.focus.js 查看文件

@@ -848,3 +848,46 @@ ColibriFocus.prototype.terminate = function (session, reason) {
848 848
     delete this.remotessrc[session.peerjid];
849 849
     this.modifySources();
850 850
 };
851
+
852
+ColibriFocus.prototype.sendTerminate = function (session, reason, text) {
853
+    var term = $iq({to: session.peerjid, type: 'set'})
854
+        .c('jingle',
855
+            {xmlns: 'urn:xmpp:jingle:1',
856
+            action: 'session-terminate',
857
+            initiator: session.me,
858
+            sid: session.sid})
859
+        .c('reason')
860
+        .c(reason || 'success');
861
+
862
+    if (text) {
863
+        term.up().c('text').t(text);
864
+    }
865
+
866
+    this.connection.sendIQ(term,
867
+        function () {
868
+            if (!session)
869
+                return;
870
+
871
+            if (session.peerconnection) {
872
+                session.peerconnection.close();
873
+                session.peerconnection = null;
874
+            }
875
+
876
+            session.terminate();
877
+            var ack = {};
878
+            ack.source = 'terminate';
879
+            $(document).trigger('ack.jingle', [session.sid, ack]);
880
+        },
881
+        function (stanza) {
882
+            var error = ($(stanza).find('error').length) ? {
883
+                code: $(stanza).find('error').attr('code'),
884
+                reason: $(stanza).find('error :first')[0].tagName,
885
+            }:{};
886
+            $(document).trigger('ack.jingle', [self.sid, error]);
887
+        },
888
+        10000);
889
+    if (this.statsinterval !== null) {
890
+        window.clearInterval(this.statsinterval);
891
+        this.statsinterval = null;
892
+    }
893
+};

+ 1
- 1
libs/colibri/colibri.session.js 查看文件

@@ -90,5 +90,5 @@ ColibriSession.prototype.sendAnswer = function (sdp, provisional) {
90 90
 };
91 91
 
92 92
 ColibriSession.prototype.sendTerminate = function (reason, text) {
93
-    console.log('ColibriSession.sendTerminate');
93
+    this.colibri.sendTerminate(this, reason, text);
94 94
 };

+ 29
- 3
libs/strophe/strophe.jingle.js 查看文件

@@ -112,17 +112,27 @@ Strophe.addConnectionPlugin('jingle', {
112 112
                 $(document).trigger('callaccepted.jingle', [sess.sid]);
113 113
                 break;
114 114
             case 'session-terminate':
115
-                console.log('terminating...');
115
+                // If this is not the focus sending the terminate, we have
116
+                // nothing more to do here.
117
+                if (Object.keys(this.sessions).length < 1
118
+                    || !(this.sessions[Object.keys(this.sessions)[0]]
119
+                        instanceof JingleSession))
120
+                {
121
+                    break;
122
+                }
123
+                console.log('terminating...', sess.sid);
116 124
                 sess.terminate();
117 125
                 this.terminate(sess.sid);
118 126
                 if ($(iq).find('>jingle>reason').length) {
119 127
                     $(document).trigger('callterminated.jingle', [
120 128
                         sess.sid,
129
+                        sess.peerjid,
121 130
                         $(iq).find('>jingle>reason>:first')[0].tagName,
122 131
                         $(iq).find('>jingle>reason>text').text()
123 132
                     ]);
124 133
                 } else {
125
-                    $(document).trigger('callterminated.jingle', [sess.sid]);
134
+                    $(document).trigger('callterminated.jingle',
135
+                                        [sess.sid, sess.peerjid]);
126 136
                 }
127 137
                 break;
128 138
             case 'transport-info':
@@ -192,6 +202,7 @@ Strophe.addConnectionPlugin('jingle', {
192 202
             delete this.sessions[sid];
193 203
         }
194 204
     },
205
+    // Used to terminate a session when an unavailable presence is received.
195 206
     terminateByJid: function (jid) {
196 207
         if (this.jid2session.hasOwnProperty(jid)) {
197 208
             var sess = this.jid2session[jid];
@@ -200,7 +211,22 @@ Strophe.addConnectionPlugin('jingle', {
200 211
                 console.log('peer went away silently', jid);
201 212
                 delete this.sessions[sess.sid];
202 213
                 delete this.jid2session[jid];
203
-                $(document).trigger('callterminated.jingle', [sess.sid, 'gone']);
214
+                $(document).trigger('callterminated.jingle',
215
+                                    [sess.sid, jid], 'gone');
216
+            }
217
+        }
218
+    },
219
+    terminateRemoteByJid: function (jid, reason) {
220
+        if (this.jid2session.hasOwnProperty(jid)) {
221
+            var sess = this.jid2session[jid];
222
+            if (sess) {
223
+                sess.sendTerminate(reason || (!sess.active()) ? 'kick' : null);
224
+                sess.terminate();
225
+                console.log('terminate peer with jid', sess.sid, jid);
226
+                delete this.sessions[sess.sid];
227
+                delete this.jid2session[jid];
228
+                $(document).trigger('callterminated.jingle',
229
+                                    [sess.sid, jid, 'kicked']);
204 230
             }
205 231
         }
206 232
     },

+ 50
- 0
moderatemuc.js 查看文件

@@ -0,0 +1,50 @@
1
+/**
2
+ * Moderate connection plugin.
3
+ */
4
+Strophe.addConnectionPlugin('moderate', {
5
+    connection: null,
6
+    roomjid: null,
7
+    myroomjid: null,
8
+    members: {},
9
+    list_members: [], // so we can elect a new focus
10
+    presMap: {},
11
+    preziMap: {},
12
+    joined: false,
13
+    isOwner: false,
14
+    init: function (conn) {
15
+        this.connection = conn;
16
+
17
+        this.connection.addHandler( this.onMute.bind(this),
18
+                                    'http://jitsi.org/jitmeet/audio',
19
+                                    'iq',
20
+                                    'set',
21
+                                    null,
22
+                                    null);
23
+    },
24
+    setMute: function(jid, mute) {
25
+        var iq = $iq({to: jid, type: 'set'})
26
+                    .c('mute', {xmlns: 'http://jitsi.org/jitmeet/audio'})
27
+                    .t(mute.toString())
28
+                    .up();
29
+
30
+        this.connection.sendIQ(
31
+                iq,
32
+                function (result) {
33
+                    console.log('set mute', result);
34
+                },
35
+                function (error) {
36
+                    console.log('set mute error', error);
37
+                });
38
+    },
39
+    onMute: function(iq) {
40
+        var mute = $(iq).find('mute');
41
+        if (mute.length) {
42
+            toggleAudio();
43
+        }
44
+        return true;
45
+    },
46
+    eject: function(jid) {
47
+        connection.jingle.terminateRemoteByJid(jid, 'kick');
48
+        connection.emuc.kick(jid);
49
+    }
50
+});

+ 37
- 4
muc.js 查看文件

@@ -36,6 +36,12 @@ Strophe.addConnectionPlugin('emuc', {
36 36
         }
37 37
         this.sendPresence();
38 38
     },
39
+    doLeave: function() {
40
+        console.log("DO LEAVE", this.myroomjid);
41
+        var pres = $pres({to: this.myroomjid, type: 'unavailable' });
42
+        this.presMap.length = 0;
43
+        this.connection.send(pres);
44
+    },
39 45
     onPresence: function (pres) {
40 46
         var from = pres.getAttribute('from');
41 47
         var type = pres.getAttribute('type');
@@ -125,9 +131,22 @@ Strophe.addConnectionPlugin('emuc', {
125 131
     },
126 132
     onPresenceUnavailable: function (pres) {
127 133
         var from = pres.getAttribute('from');
128
-        delete this.members[from];
129
-        this.list_members.splice(this.list_members.indexOf(from), 1);
130
-        $(document).trigger('left.muc', [from]);
134
+        // Status code 110 indicates that this notification is "self-presence".
135
+        if (!$(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="110"]').length) {
136
+            delete this.members[from];
137
+            this.list_members.splice(this.list_members.indexOf(from), 1);
138
+            $(document).trigger('left.muc', [from]);
139
+        }
140
+        // If the status code is 110 this means we're leaving and we would like
141
+        // to remove everyone else from our view, so we trigger the event.
142
+        else if (this.list_members.length > 1) {
143
+            for (var i = 0; i < this.list_members.length; i++) {
144
+                var member = this.list_members[i];
145
+                delete this.members[i];
146
+                this.list_members.splice(i, 1);
147
+                $(document).trigger('left.muc', member);
148
+            }
149
+        }
131 150
         return true;
132 151
     },
133 152
     onPresenceError: function (pres) {
@@ -188,6 +207,21 @@ Strophe.addConnectionPlugin('emuc', {
188 207
             }
189 208
         );
190 209
     },
210
+    kick: function (jid) {
211
+        var kickIQ = $iq({to: this.roomjid, type: 'set'})
212
+            .c('query', {xmlns: 'http://jabber.org/protocol/muc#admin'})
213
+            .c('item', {nick: Strophe.getResourceFromJid(jid), role: 'none'})
214
+            .c('reason').t('You have been kicked.').up().up().up();
215
+
216
+        this.connection.sendIQ(
217
+                kickIQ,
218
+                function (result) {
219
+                    console.log('Kick participant with jid: ' + jid, result);
220
+                },
221
+                function (error) {
222
+                    console.log('Kick participant error: ', error);
223
+                });
224
+    },
191 225
     sendPresence: function () {
192 226
         var pres = $pres({to: this.presMap['to'] });
193 227
         pres.c('x', {xmlns: this.presMap['xns']});
@@ -210,7 +244,6 @@ Strophe.addConnectionPlugin('emuc', {
210 244
         }
211 245
 
212 246
         if (this.presMap['videons']) {
213
-            console.log("SEND VIDEO MUTED", this.presMap['videomuted']);
214 247
             pres.c('videomuted', {xmlns: this.presMap['videons']})
215 248
                 .t(this.presMap['videomuted']).up();
216 249
         }

Loading…
取消
儲存