소스 검색

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. BIN
      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;

BIN
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…
취소
저장