├── LICENSE.txt ├── README.md ├── lib ├── strophe.muc.js └── strophe.muc.js.map ├── package-lock.json ├── package.json ├── rollup.config.js ├── src └── strophe.muc.coffee └── test ├── buster.js ├── helpers.js └── room-test.js /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Andreas Guth, Dominik Renzel, RWTH Aachen University, 2 | Chair of Computer Science 5 (Databases & Information Systems), ACIS Group 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # strophe.muc.js 2 | 3 | Plugin for [strophe.js](https://www.npmjs.com/package/strophe.js) to provide Multi-User Chat ([XEP-0045]( http://xmpp.org/extensions/xep-0045.html)). 4 | 5 | ## Install 6 | 7 | npm install strophejs-plugin-muc 8 | 9 | ## Usage 10 | 11 | 12 | ## Contributing 13 | 14 | This plugin is written in CoffeeScript. 15 | 16 | Run `npm run build` to transpile to JavaScript. 17 | -------------------------------------------------------------------------------- /lib/strophe.muc.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('strophe.js')) : 3 | typeof define === 'function' && define.amd ? define(['strophe.js'], factory) : 4 | (factory(global.window)); 5 | }(this, (function (strophe_js) { 'use strict'; 6 | 7 | /* 8 | *Plugin to implement the MUC extension. 9 | http://xmpp.org/extensions/xep-0045.html 10 | *Previous Author: 11 | Nathan Zorn 12 | *Complete CoffeeScript rewrite: 13 | Andreas Guth 14 | */ 15 | 16 | var Occupant; 17 | var RoomConfig; 18 | var XmppRoom; 19 | var hasProp = {}.hasOwnProperty; 20 | var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; 21 | 22 | strophe_js.Strophe.addConnectionPlugin('muc', { 23 | _connection: null, 24 | rooms: {}, 25 | roomNames: [], 26 | 27 | /*Function 28 | Initialize the MUC plugin. Sets the correct connection object and 29 | extends the namesace. 30 | */ 31 | init: function(conn) { 32 | this._connection = conn; 33 | this._muc_handler = null; 34 | strophe_js.Strophe.addNamespace('MUC_OWNER', strophe_js.Strophe.NS.MUC + "#owner"); 35 | strophe_js.Strophe.addNamespace('MUC_ADMIN', strophe_js.Strophe.NS.MUC + "#admin"); 36 | strophe_js.Strophe.addNamespace('MUC_USER', strophe_js.Strophe.NS.MUC + "#user"); 37 | strophe_js.Strophe.addNamespace('MUC_ROOMCONF', strophe_js.Strophe.NS.MUC + "#roomconfig"); 38 | return strophe_js.Strophe.addNamespace('MUC_REGISTER', "jabber:iq:register"); 39 | }, 40 | 41 | /*Function 42 | Join a multi-user chat room 43 | Parameters: 44 | (String) room - The multi-user chat room to join. 45 | (String) nick - The nickname to use in the chat room. Optional 46 | (Function) msg_handler_cb - The function call to handle messages from the 47 | specified chat room. 48 | (Function) pres_handler_cb - The function call back to handle presence 49 | in the chat room. 50 | (Function) roster_cb - The function call to handle roster info in the chat room 51 | (String) password - The optional password to use. (password protected 52 | rooms only) 53 | (Object) history_attrs - Optional attributes for retrieving history 54 | (XML DOM Element) extended_presence - Optional XML for extending presence 55 | */ 56 | join: function(room, nick, msg_handler_cb, pres_handler_cb, roster_cb, password, history_attrs, extended_presence) { 57 | var msg, room_nick; 58 | room_nick = this.test_append_nick(room, nick); 59 | msg = strophe_js.$pres({ 60 | from: this._connection.jid, 61 | to: room_nick 62 | }).c("x", { 63 | xmlns: strophe_js.Strophe.NS.MUC 64 | }); 65 | if (history_attrs != null) { 66 | msg = msg.c("history", history_attrs).up(); 67 | } 68 | if (password != null) { 69 | msg.cnode(strophe_js.Strophe.xmlElement("password", [], password)); 70 | } 71 | if (extended_presence != null) { 72 | msg.up().cnode(extended_presence); 73 | } 74 | if (this._muc_handler == null) { 75 | this._muc_handler = this._connection.addHandler((function(_this) { 76 | return function(stanza) { 77 | var from, handler, handlers, i, id, len, roomname, x, xmlns, xquery; 78 | from = stanza.getAttribute('from'); 79 | if (!from) { 80 | return true; 81 | } 82 | roomname = from.split("/")[0]; 83 | if (!_this.rooms[roomname]) { 84 | return true; 85 | } 86 | room = _this.rooms[roomname]; 87 | handlers = {}; 88 | if (stanza.nodeName === "message") { 89 | handlers = room._message_handlers; 90 | } else if (stanza.nodeName === "presence") { 91 | xquery = stanza.getElementsByTagName("x"); 92 | if (xquery.length > 0) { 93 | for (i = 0, len = xquery.length; i < len; i++) { 94 | x = xquery[i]; 95 | xmlns = x.getAttribute("xmlns"); 96 | if (xmlns && xmlns.match(strophe_js.Strophe.NS.MUC)) { 97 | handlers = room._presence_handlers; 98 | break; 99 | } 100 | } 101 | } 102 | } 103 | for (id in handlers) { 104 | handler = handlers[id]; 105 | if (!handler(stanza, room)) { 106 | delete handlers[id]; 107 | } 108 | } 109 | return true; 110 | }; 111 | })(this)); 112 | } 113 | if (!this.rooms.hasOwnProperty(room)) { 114 | this.rooms[room] = new XmppRoom(this, room, nick, password); 115 | if (pres_handler_cb) { 116 | this.rooms[room].addHandler('presence', pres_handler_cb); 117 | } 118 | if (msg_handler_cb) { 119 | this.rooms[room].addHandler('message', msg_handler_cb); 120 | } 121 | if (roster_cb) { 122 | this.rooms[room].addHandler('roster', roster_cb); 123 | } 124 | this.roomNames.push(room); 125 | } 126 | return this._connection.send(msg); 127 | }, 128 | 129 | /*Function 130 | Leave a multi-user chat room 131 | Parameters: 132 | (String) room - The multi-user chat room to leave. 133 | (String) nick - The nick name used in the room. 134 | (Function) handler_cb - Optional function to handle the successful leave. 135 | (String) exit_msg - optional exit message. 136 | Returns: 137 | iqid - The unique id for the room leave. 138 | */ 139 | leave: function(room, nick, handler_cb, exit_msg) { 140 | var id, presence, presenceid, room_nick; 141 | id = this.roomNames.indexOf(room); 142 | delete this.rooms[room]; 143 | if (id >= 0) { 144 | this.roomNames.splice(id, 1); 145 | if (this.roomNames.length === 0) { 146 | this._connection.deleteHandler(this._muc_handler); 147 | this._muc_handler = null; 148 | } 149 | } 150 | room_nick = this.test_append_nick(room, nick); 151 | presenceid = this._connection.getUniqueId(); 152 | presence = strophe_js.$pres({ 153 | type: "unavailable", 154 | id: presenceid, 155 | from: this._connection.jid, 156 | to: room_nick 157 | }); 158 | if (exit_msg != null) { 159 | presence.c("status", exit_msg); 160 | } 161 | if (handler_cb != null) { 162 | this._connection.addHandler(handler_cb, null, "presence", null, presenceid); 163 | } 164 | this._connection.send(presence); 165 | return presenceid; 166 | }, 167 | 168 | /*Function 169 | Parameters: 170 | (String) room - The multi-user chat room name. 171 | (String) nick - The nick name used in the chat room. 172 | (String) message - The plaintext message to send to the room. 173 | (String) html_message - The message to send to the room with html markup. 174 | (String) type - "groupchat" for group chat messages o 175 | "chat" for private chat messages 176 | Returns: 177 | msgiq - the unique id used to send the message 178 | */ 179 | message: function(room, nick, message, html_message, type, msgid) { 180 | var msg, parent, room_nick; 181 | room_nick = this.test_append_nick(room, nick); 182 | type = type || (nick != null ? "chat" : "groupchat"); 183 | msgid = msgid || this._connection.getUniqueId(); 184 | msg = strophe_js.$msg({ 185 | to: room_nick, 186 | from: this._connection.jid, 187 | type: type, 188 | id: msgid 189 | }).c("body").t(message); 190 | msg.up(); 191 | if (html_message != null) { 192 | msg.c("html", { 193 | xmlns: strophe_js.Strophe.NS.XHTML_IM 194 | }).c("body", { 195 | xmlns: strophe_js.Strophe.NS.XHTML 196 | }).h(html_message); 197 | if (msg.node.childNodes.length === 0) { 198 | parent = msg.node.parentNode; 199 | msg.up().up(); 200 | msg.node.removeChild(parent); 201 | } else { 202 | msg.up().up(); 203 | } 204 | } 205 | msg.c("x", { 206 | xmlns: "jabber:x:event" 207 | }).c("composing"); 208 | this._connection.send(msg); 209 | return msgid; 210 | }, 211 | 212 | /*Function 213 | Convenience Function to send a Message to all Occupants 214 | Parameters: 215 | (String) room - The multi-user chat room name. 216 | (String) message - The plaintext message to send to the room. 217 | (String) html_message - The message to send to the room with html markup. 218 | (String) msgid - Optional unique ID which will be set as the 'id' attribute of the stanza 219 | Returns: 220 | msgiq - the unique id used to send the message 221 | */ 222 | groupchat: function(room, message, html_message, msgid) { 223 | return this.message(room, null, message, html_message, void 0, msgid); 224 | }, 225 | 226 | /*Function 227 | Send a mediated invitation. 228 | Parameters: 229 | (String) room - The multi-user chat room name. 230 | (String) receiver - The invitation's receiver. 231 | (String) reason - Optional reason for joining the room. 232 | Returns: 233 | msgiq - the unique id used to send the invitation 234 | */ 235 | invite: function(room, receiver, reason) { 236 | var invitation, msgid; 237 | msgid = this._connection.getUniqueId(); 238 | invitation = strophe_js.$msg({ 239 | from: this._connection.jid, 240 | to: room, 241 | id: msgid 242 | }).c('x', { 243 | xmlns: strophe_js.Strophe.NS.MUC_USER 244 | }).c('invite', { 245 | to: receiver 246 | }); 247 | if (reason != null) { 248 | invitation.c('reason', reason); 249 | } 250 | this._connection.send(invitation); 251 | return msgid; 252 | }, 253 | 254 | /*Function 255 | Send a mediated multiple invitation. 256 | Parameters: 257 | (String) room - The multi-user chat room name. 258 | (Array) receivers - The invitation's receivers. 259 | (String) reason - Optional reason for joining the room. 260 | Returns: 261 | msgiq - the unique id used to send the invitation 262 | */ 263 | multipleInvites: function(room, receivers, reason) { 264 | var i, invitation, len, msgid, receiver; 265 | msgid = this._connection.getUniqueId(); 266 | invitation = strophe_js.$msg({ 267 | from: this._connection.jid, 268 | to: room, 269 | id: msgid 270 | }).c('x', { 271 | xmlns: strophe_js.Strophe.NS.MUC_USER 272 | }); 273 | for (i = 0, len = receivers.length; i < len; i++) { 274 | receiver = receivers[i]; 275 | invitation.c('invite', { 276 | to: receiver 277 | }); 278 | if (reason != null) { 279 | invitation.c('reason', reason); 280 | invitation.up(); 281 | } 282 | invitation.up(); 283 | } 284 | this._connection.send(invitation); 285 | return msgid; 286 | }, 287 | 288 | /*Function 289 | Send a direct invitation. 290 | Parameters: 291 | (String) room - The multi-user chat room name. 292 | (String) receiver - The invitation's receiver. 293 | (String) reason - Optional reason for joining the room. 294 | (String) password - Optional password for the room. 295 | Returns: 296 | msgiq - the unique id used to send the invitation 297 | */ 298 | directInvite: function(room, receiver, reason, password) { 299 | var attrs, invitation, msgid; 300 | msgid = this._connection.getUniqueId(); 301 | attrs = { 302 | xmlns: 'jabber:x:conference', 303 | jid: room 304 | }; 305 | if (reason != null) { 306 | attrs.reason = reason; 307 | } 308 | if (password != null) { 309 | attrs.password = password; 310 | } 311 | invitation = strophe_js.$msg({ 312 | from: this._connection.jid, 313 | to: receiver, 314 | id: msgid 315 | }).c('x', attrs); 316 | this._connection.send(invitation); 317 | return msgid; 318 | }, 319 | 320 | /*Function 321 | Queries a room for a list of occupants 322 | (String) room - The multi-user chat room name. 323 | (Function) success_cb - Optional function to handle the info. 324 | (Function) error_cb - Optional function to handle an error. 325 | Returns: 326 | id - the unique id used to send the info request 327 | */ 328 | queryOccupants: function(room, success_cb, error_cb) { 329 | var attrs, info; 330 | attrs = { 331 | xmlns: strophe_js.Strophe.NS.DISCO_ITEMS 332 | }; 333 | info = strophe_js.$iq({ 334 | from: this._connection.jid, 335 | to: room, 336 | type: 'get' 337 | }).c('query', attrs); 338 | return this._connection.sendIQ(info, success_cb, error_cb); 339 | }, 340 | 341 | /*Function 342 | Start a room configuration. 343 | Parameters: 344 | (String) room - The multi-user chat room name. 345 | (Function) handler_cb - Optional function to handle the config form. 346 | Returns: 347 | id - the unique id used to send the configuration request 348 | */ 349 | configure: function(room, handler_cb, error_cb) { 350 | var config, stanza; 351 | config = strophe_js.$iq({ 352 | to: room, 353 | type: "get" 354 | }).c("query", { 355 | xmlns: strophe_js.Strophe.NS.MUC_OWNER 356 | }); 357 | stanza = config.tree(); 358 | return this._connection.sendIQ(stanza, handler_cb, error_cb); 359 | }, 360 | 361 | /*Function 362 | Cancel the room configuration 363 | Parameters: 364 | (String) room - The multi-user chat room name. 365 | Returns: 366 | id - the unique id used to cancel the configuration. 367 | */ 368 | cancelConfigure: function(room) { 369 | var config, stanza; 370 | config = strophe_js.$iq({ 371 | to: room, 372 | type: "set" 373 | }).c("query", { 374 | xmlns: strophe_js.Strophe.NS.MUC_OWNER 375 | }).c("x", { 376 | xmlns: "jabber:x:data", 377 | type: "cancel" 378 | }); 379 | stanza = config.tree(); 380 | return this._connection.sendIQ(stanza); 381 | }, 382 | 383 | /*Function 384 | Save a room configuration. 385 | Parameters: 386 | (String) room - The multi-user chat room name. 387 | (Array) config- Form Object or an array of form elements used to configure the room. 388 | Returns: 389 | id - the unique id used to save the configuration. 390 | */ 391 | saveConfiguration: function(room, config, success_cb, error_cb) { 392 | var conf, i, iq, len, stanza; 393 | iq = strophe_js.$iq({ 394 | to: room, 395 | type: "set" 396 | }).c("query", { 397 | xmlns: strophe_js.Strophe.NS.MUC_OWNER 398 | }); 399 | if (typeof strophe_js.Strophe.x !== "undefined" && typeof strophe_js.Strophe.x.Form !== "undefined" && config instanceof strophe_js.Strophe.x.Form) { 400 | config.type = "submit"; 401 | iq.cnode(config.toXML()); 402 | } else { 403 | iq.c("x", { 404 | xmlns: "jabber:x:data", 405 | type: "submit" 406 | }); 407 | for (i = 0, len = config.length; i < len; i++) { 408 | conf = config[i]; 409 | iq.cnode(conf).up(); 410 | } 411 | } 412 | stanza = iq.tree(); 413 | return this._connection.sendIQ(stanza, success_cb, error_cb); 414 | }, 415 | 416 | /*Function 417 | Parameters: 418 | (String) room - The multi-user chat room name. 419 | Returns: 420 | id - the unique id used to create the chat room. 421 | */ 422 | createInstantRoom: function(room, success_cb, error_cb) { 423 | var roomiq; 424 | roomiq = strophe_js.$iq({ 425 | to: room, 426 | type: "set" 427 | }).c("query", { 428 | xmlns: strophe_js.Strophe.NS.MUC_OWNER 429 | }).c("x", { 430 | xmlns: "jabber:x:data", 431 | type: "submit" 432 | }); 433 | return this._connection.sendIQ(roomiq.tree(), success_cb, error_cb); 434 | }, 435 | 436 | /*Function 437 | Parameters: 438 | (String) room - The multi-user chat room name. 439 | (Object) config - the configuration. ex: {"muc#roomconfig_publicroom": "0", "muc#roomconfig_persistentroom": "1"} 440 | Returns: 441 | id - the unique id used to create the chat room. 442 | */ 443 | createConfiguredRoom: function(room, config, success_cb, error_cb) { 444 | var k, roomiq, v; 445 | roomiq = strophe_js.$iq({ 446 | to: room, 447 | type: "set" 448 | }).c("query", { 449 | xmlns: strophe_js.Strophe.NS.MUC_OWNER 450 | }).c("x", { 451 | xmlns: "jabber:x:data", 452 | type: "submit" 453 | }); 454 | roomiq.c('field', { 455 | 'var': 'FORM_TYPE' 456 | }).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up(); 457 | for (k in config) { 458 | if (!hasProp.call(config, k)) continue; 459 | v = config[k]; 460 | roomiq.c('field', { 461 | 'var': k 462 | }).c('value').t(v).up().up(); 463 | } 464 | return this._connection.sendIQ(roomiq.tree(), success_cb, error_cb); 465 | }, 466 | 467 | /*Function 468 | Set the topic of the chat room. 469 | Parameters: 470 | (String) room - The multi-user chat room name. 471 | (String) topic - Topic message. 472 | */ 473 | setTopic: function(room, topic) { 474 | var msg; 475 | msg = strophe_js.$msg({ 476 | to: room, 477 | from: this._connection.jid, 478 | type: "groupchat" 479 | }).c("subject", { 480 | xmlns: "jabber:client" 481 | }).t(topic); 482 | return this._connection.send(msg.tree()); 483 | }, 484 | 485 | /*Function 486 | Internal Function that Changes the role or affiliation of a member 487 | of a MUC room. This function is used by modifyRole and modifyAffiliation. 488 | The modification can only be done by a room moderator. An error will be 489 | returned if the user doesn't have permission. 490 | Parameters: 491 | (String) room - The multi-user chat room name. 492 | (Object) item - Object with nick and role or jid and affiliation attribute 493 | (String) reason - Optional reason for the change. 494 | (Function) handler_cb - Optional callback for success 495 | (Function) error_cb - Optional callback for error 496 | Returns: 497 | iq - the id of the mode change request. 498 | */ 499 | _modifyPrivilege: function(room, item, reason, handler_cb, error_cb) { 500 | var iq; 501 | iq = strophe_js.$iq({ 502 | to: room, 503 | type: "set" 504 | }).c("query", { 505 | xmlns: strophe_js.Strophe.NS.MUC_ADMIN 506 | }).cnode(item.node); 507 | if (reason != null) { 508 | iq.c("reason", reason); 509 | } 510 | return this._connection.sendIQ(iq.tree(), handler_cb, error_cb); 511 | }, 512 | 513 | /*Function 514 | Changes the role of a member of a MUC room. 515 | The modification can only be done by a room moderator. An error will be 516 | returned if the user doesn't have permission. 517 | Parameters: 518 | (String) room - The multi-user chat room name. 519 | (String) nick - The nick name of the user to modify. 520 | (String) role - The new role of the user. 521 | (String) affiliation - The new affiliation of the user. 522 | (String) reason - Optional reason for the change. 523 | (Function) handler_cb - Optional callback for success 524 | (Function) error_cb - Optional callback for error 525 | Returns: 526 | iq - the id of the mode change request. 527 | */ 528 | modifyRole: function(room, nick, role, reason, handler_cb, error_cb) { 529 | var item; 530 | item = strophe_js.$build("item", { 531 | nick: nick, 532 | role: role 533 | }); 534 | return this._modifyPrivilege(room, item, reason, handler_cb, error_cb); 535 | }, 536 | kick: function(room, nick, reason, handler_cb, error_cb) { 537 | return this.modifyRole(room, nick, 'none', reason, handler_cb, error_cb); 538 | }, 539 | voice: function(room, nick, reason, handler_cb, error_cb) { 540 | return this.modifyRole(room, nick, 'participant', reason, handler_cb, error_cb); 541 | }, 542 | mute: function(room, nick, reason, handler_cb, error_cb) { 543 | return this.modifyRole(room, nick, 'visitor', reason, handler_cb, error_cb); 544 | }, 545 | op: function(room, nick, reason, handler_cb, error_cb) { 546 | return this.modifyRole(room, nick, 'moderator', reason, handler_cb, error_cb); 547 | }, 548 | deop: function(room, nick, reason, handler_cb, error_cb) { 549 | return this.modifyRole(room, nick, 'participant', reason, handler_cb, error_cb); 550 | }, 551 | 552 | /*Function 553 | Changes the affiliation of a member of a MUC room. 554 | The modification can only be done by a room moderator. An error will be 555 | returned if the user doesn't have permission. 556 | Parameters: 557 | (String) room - The multi-user chat room name. 558 | (String) jid - The jid of the user to modify. 559 | (String) affiliation - The new affiliation of the user. 560 | (String) reason - Optional reason for the change. 561 | (Function) handler_cb - Optional callback for success 562 | (Function) error_cb - Optional callback for error 563 | Returns: 564 | iq - the id of the mode change request. 565 | */ 566 | modifyAffiliation: function(room, jid, affiliation, reason, handler_cb, error_cb) { 567 | var item; 568 | item = strophe_js.$build("item", { 569 | jid: jid, 570 | affiliation: affiliation 571 | }); 572 | return this._modifyPrivilege(room, item, reason, handler_cb, error_cb); 573 | }, 574 | ban: function(room, jid, reason, handler_cb, error_cb) { 575 | return this.modifyAffiliation(room, jid, 'outcast', reason, handler_cb, error_cb); 576 | }, 577 | member: function(room, jid, reason, handler_cb, error_cb) { 578 | return this.modifyAffiliation(room, jid, 'member', reason, handler_cb, error_cb); 579 | }, 580 | revoke: function(room, jid, reason, handler_cb, error_cb) { 581 | return this.modifyAffiliation(room, jid, 'none', reason, handler_cb, error_cb); 582 | }, 583 | owner: function(room, jid, reason, handler_cb, error_cb) { 584 | return this.modifyAffiliation(room, jid, 'owner', reason, handler_cb, error_cb); 585 | }, 586 | admin: function(room, jid, reason, handler_cb, error_cb) { 587 | return this.modifyAffiliation(room, jid, 'admin', reason, handler_cb, error_cb); 588 | }, 589 | 590 | /*Function 591 | Change the current users nick name. 592 | Parameters: 593 | (String) room - The multi-user chat room name. 594 | (String) user - The new nick name. 595 | */ 596 | changeNick: function(room, user) { 597 | var presence, room_nick; 598 | room_nick = this.test_append_nick(room, user); 599 | presence = strophe_js.$pres({ 600 | from: this._connection.jid, 601 | to: room_nick, 602 | id: this._connection.getUniqueId() 603 | }); 604 | return this._connection.send(presence.tree()); 605 | }, 606 | 607 | /*Function 608 | Change the current users status. 609 | Parameters: 610 | (String) room - The multi-user chat room name. 611 | (String) user - The current nick. 612 | (String) show - The new show-text. 613 | (String) status - The new status-text. 614 | */ 615 | setStatus: function(room, user, show, status) { 616 | var presence, room_nick; 617 | room_nick = this.test_append_nick(room, user); 618 | presence = strophe_js.$pres({ 619 | from: this._connection.jid, 620 | to: room_nick 621 | }); 622 | if (show != null) { 623 | presence.c('show', show).up(); 624 | } 625 | if (status != null) { 626 | presence.c('status', status); 627 | } 628 | return this._connection.send(presence.tree()); 629 | }, 630 | 631 | /*Function 632 | Registering with a room. 633 | @see http://xmpp.org/extensions/xep-0045.html#register 634 | Parameters: 635 | (String) room - The multi-user chat room name. 636 | (Function) handle_cb - Function to call for room list return. 637 | (Function) error_cb - Function to call on error. 638 | */ 639 | registrationRequest: function(room, handle_cb, error_cb) { 640 | var iq; 641 | iq = strophe_js.$iq({ 642 | to: room, 643 | from: this._connection.jid, 644 | type: "get" 645 | }).c("query", { 646 | xmlns: strophe_js.Strophe.NS.MUC_REGISTER 647 | }); 648 | return this._connection.sendIQ(iq, function(stanza) { 649 | var $field, $fields, field, fields, i, len, length; 650 | $fields = stanza.getElementsByTagName('field'); 651 | length = $fields.length; 652 | fields = { 653 | required: [], 654 | optional: [] 655 | }; 656 | for (i = 0, len = $fields.length; i < len; i++) { 657 | $field = $fields[i]; 658 | field = { 659 | "var": $field.getAttribute('var'), 660 | label: $field.getAttribute('label'), 661 | type: $field.getAttribute('type') 662 | }; 663 | if ($field.getElementsByTagName('required').length > 0) { 664 | fields.required.push(field); 665 | } else { 666 | fields.optional.push(field); 667 | } 668 | } 669 | return handle_cb(fields); 670 | }, error_cb); 671 | }, 672 | 673 | /*Function 674 | Submits registration form. 675 | Parameters: 676 | (String) room - The multi-user chat room name. 677 | (Function) handle_cb - Function to call for room list return. 678 | (Function) error_cb - Function to call on error. 679 | */ 680 | submitRegistrationForm: function(room, fields, handle_cb, error_cb) { 681 | var iq, key, val; 682 | iq = strophe_js.$iq({ 683 | to: room, 684 | type: "set" 685 | }).c("query", { 686 | xmlns: strophe_js.Strophe.NS.MUC_REGISTER 687 | }); 688 | iq.c("x", { 689 | xmlns: "jabber:x:data", 690 | type: "submit" 691 | }); 692 | iq.c('field', { 693 | 'var': 'FORM_TYPE' 694 | }).c('value').t('http://jabber.org/protocol/muc#register').up().up(); 695 | for (key in fields) { 696 | val = fields[key]; 697 | iq.c('field', { 698 | 'var': key 699 | }).c('value').t(val).up().up(); 700 | } 701 | return this._connection.sendIQ(iq, handle_cb, error_cb); 702 | }, 703 | 704 | /*Function 705 | List all chat room available on a server. 706 | Parameters: 707 | (String) server - name of chat server. 708 | (String) handle_cb - Function to call for room list return. 709 | (String) error_cb - Function to call on error. 710 | */ 711 | listRooms: function(server, handle_cb, error_cb) { 712 | var iq; 713 | iq = strophe_js.$iq({ 714 | to: server, 715 | from: this._connection.jid, 716 | type: "get" 717 | }).c("query", { 718 | xmlns: strophe_js.Strophe.NS.DISCO_ITEMS 719 | }); 720 | return this._connection.sendIQ(iq, handle_cb, error_cb); 721 | }, 722 | test_append_nick: function(room, nick) { 723 | var domain, node; 724 | node = strophe_js.Strophe.escapeNode(strophe_js.Strophe.getNodeFromJid(room)); 725 | domain = strophe_js.Strophe.getDomainFromJid(room); 726 | return node + "@" + domain + (nick != null ? "/" + nick : ""); 727 | } 728 | }); 729 | 730 | XmppRoom = (function() { 731 | function XmppRoom(client, name, nick1, password1) { 732 | this.client = client; 733 | this.name = name; 734 | this.nick = nick1; 735 | this.password = password1; 736 | this._roomRosterHandler = bind(this._roomRosterHandler, this); 737 | this._addOccupant = bind(this._addOccupant, this); 738 | this.roster = {}; 739 | this._message_handlers = {}; 740 | this._presence_handlers = {}; 741 | this._roster_handlers = {}; 742 | this._handler_ids = 0; 743 | if (this.client.muc) { 744 | this.client = this.client.muc; 745 | } 746 | this.name = strophe_js.Strophe.getBareJidFromJid(this.name); 747 | this.addHandler('presence', this._roomRosterHandler); 748 | } 749 | 750 | XmppRoom.prototype.join = function(msg_handler_cb, pres_handler_cb, roster_cb) { 751 | return this.client.join(this.name, this.nick, msg_handler_cb, pres_handler_cb, roster_cb, this.password); 752 | }; 753 | 754 | XmppRoom.prototype.leave = function(handler_cb, message) { 755 | this.client.leave(this.name, this.nick, handler_cb, message); 756 | return delete this.client.rooms[this.name]; 757 | }; 758 | 759 | XmppRoom.prototype.message = function(nick, message, html_message, type) { 760 | return this.client.message(this.name, nick, message, html_message, type); 761 | }; 762 | 763 | XmppRoom.prototype.groupchat = function(message, html_message) { 764 | return this.client.groupchat(this.name, message, html_message); 765 | }; 766 | 767 | XmppRoom.prototype.invite = function(receiver, reason) { 768 | return this.client.invite(this.name, receiver, reason); 769 | }; 770 | 771 | XmppRoom.prototype.multipleInvites = function(receivers, reason) { 772 | return this.client.invite(this.name, receivers, reason); 773 | }; 774 | 775 | XmppRoom.prototype.directInvite = function(receiver, reason) { 776 | return this.client.directInvite(this.name, receiver, reason, this.password); 777 | }; 778 | 779 | XmppRoom.prototype.configure = function(handler_cb) { 780 | return this.client.configure(this.name, handler_cb); 781 | }; 782 | 783 | XmppRoom.prototype.cancelConfigure = function() { 784 | return this.client.cancelConfigure(this.name); 785 | }; 786 | 787 | XmppRoom.prototype.saveConfiguration = function(config) { 788 | return this.client.saveConfiguration(this.name, config); 789 | }; 790 | 791 | XmppRoom.prototype.queryOccupants = function(success_cb, error_cb) { 792 | return this.client.queryOccupants(this.name, success_cb, error_cb); 793 | }; 794 | 795 | XmppRoom.prototype.setTopic = function(topic) { 796 | return this.client.setTopic(this.name, topic); 797 | }; 798 | 799 | XmppRoom.prototype.modifyRole = function(nick, role, reason, success_cb, error_cb) { 800 | return this.client.modifyRole(this.name, nick, role, reason, success_cb, error_cb); 801 | }; 802 | 803 | XmppRoom.prototype.kick = function(nick, reason, handler_cb, error_cb) { 804 | return this.client.kick(this.name, nick, reason, handler_cb, error_cb); 805 | }; 806 | 807 | XmppRoom.prototype.voice = function(nick, reason, handler_cb, error_cb) { 808 | return this.client.voice(this.name, nick, reason, handler_cb, error_cb); 809 | }; 810 | 811 | XmppRoom.prototype.mute = function(nick, reason, handler_cb, error_cb) { 812 | return this.client.mute(this.name, nick, reason, handler_cb, error_cb); 813 | }; 814 | 815 | XmppRoom.prototype.op = function(nick, reason, handler_cb, error_cb) { 816 | return this.client.op(this.name, nick, reason, handler_cb, error_cb); 817 | }; 818 | 819 | XmppRoom.prototype.deop = function(nick, reason, handler_cb, error_cb) { 820 | return this.client.deop(this.name, nick, reason, handler_cb, error_cb); 821 | }; 822 | 823 | XmppRoom.prototype.modifyAffiliation = function(jid, affiliation, reason, success_cb, error_cb) { 824 | return this.client.modifyAffiliation(this.name, jid, affiliation, reason, success_cb, error_cb); 825 | }; 826 | 827 | XmppRoom.prototype.ban = function(jid, reason, handler_cb, error_cb) { 828 | return this.client.ban(this.name, jid, reason, handler_cb, error_cb); 829 | }; 830 | 831 | XmppRoom.prototype.member = function(jid, reason, handler_cb, error_cb) { 832 | return this.client.member(this.name, jid, reason, handler_cb, error_cb); 833 | }; 834 | 835 | XmppRoom.prototype.revoke = function(jid, reason, handler_cb, error_cb) { 836 | return this.client.revoke(this.name, jid, reason, handler_cb, error_cb); 837 | }; 838 | 839 | XmppRoom.prototype.owner = function(jid, reason, handler_cb, error_cb) { 840 | return this.client.owner(this.name, jid, reason, handler_cb, error_cb); 841 | }; 842 | 843 | XmppRoom.prototype.admin = function(jid, reason, handler_cb, error_cb) { 844 | return this.client.admin(this.name, jid, reason, handler_cb, error_cb); 845 | }; 846 | 847 | XmppRoom.prototype.changeNick = function(nick1) { 848 | this.nick = nick1; 849 | return this.client.changeNick(this.name, nick); 850 | }; 851 | 852 | XmppRoom.prototype.setStatus = function(show, status) { 853 | return this.client.setStatus(this.name, this.nick, show, status); 854 | }; 855 | 856 | 857 | /*Function 858 | Adds a handler to the MUC room. 859 | Parameters: 860 | (String) handler_type - 'message', 'presence' or 'roster'. 861 | (Function) handler - The handler function. 862 | Returns: 863 | id - the id of handler. 864 | */ 865 | 866 | XmppRoom.prototype.addHandler = function(handler_type, handler) { 867 | var id; 868 | id = this._handler_ids++; 869 | switch (handler_type) { 870 | case 'presence': 871 | this._presence_handlers[id] = handler; 872 | break; 873 | case 'message': 874 | this._message_handlers[id] = handler; 875 | break; 876 | case 'roster': 877 | this._roster_handlers[id] = handler; 878 | break; 879 | default: 880 | this._handler_ids--; 881 | return null; 882 | } 883 | return id; 884 | }; 885 | 886 | 887 | /*Function 888 | Removes a handler from the MUC room. 889 | This function takes ONLY ids returned by the addHandler function 890 | of this room. passing handler ids returned by connection.addHandler 891 | may brake things! 892 | Parameters: 893 | (number) id - the id of the handler 894 | */ 895 | 896 | XmppRoom.prototype.removeHandler = function(id) { 897 | delete this._presence_handlers[id]; 898 | delete this._message_handlers[id]; 899 | return delete this._roster_handlers[id]; 900 | }; 901 | 902 | 903 | /*Function 904 | Creates and adds an Occupant to the Room Roster. 905 | Parameters: 906 | (Object) data - the data the Occupant is filled with 907 | Returns: 908 | occ - the created Occupant. 909 | */ 910 | 911 | XmppRoom.prototype._addOccupant = function(data) { 912 | var occ; 913 | occ = new Occupant(data, this); 914 | this.roster[occ.nick] = occ; 915 | return occ; 916 | }; 917 | 918 | 919 | /*Function 920 | The standard handler that managed the Room Roster. 921 | Parameters: 922 | (Object) pres - the presence stanza containing user information 923 | */ 924 | 925 | XmppRoom.prototype._roomRosterHandler = function(pres) { 926 | var data, handler, id, newnick, nick, ref; 927 | data = XmppRoom._parsePresence(pres); 928 | nick = data.nick; 929 | newnick = data.newnick || null; 930 | switch (data.type) { 931 | case 'error': 932 | return true; 933 | case 'unavailable': 934 | if (newnick) { 935 | data.nick = newnick; 936 | if (this.roster[nick] && this.roster[newnick]) { 937 | this.roster[nick].update(this.roster[newnick]); 938 | this.roster[newnick] = this.roster[nick]; 939 | } 940 | if (this.roster[nick] && !this.roster[newnick]) { 941 | this.roster[newnick] = this.roster[nick].update(data); 942 | } 943 | } 944 | delete this.roster[nick]; 945 | break; 946 | default: 947 | if (this.roster[nick]) { 948 | this.roster[nick].update(data); 949 | } else { 950 | this._addOccupant(data); 951 | } 952 | } 953 | ref = this._roster_handlers; 954 | for (id in ref) { 955 | handler = ref[id]; 956 | if (!handler(this.roster, this)) { 957 | delete this._roster_handlers[id]; 958 | } 959 | } 960 | return true; 961 | }; 962 | 963 | 964 | /*Function 965 | Parses a presence stanza 966 | Parameters: 967 | (Object) data - the data extracted from the presence stanza 968 | */ 969 | 970 | XmppRoom._parsePresence = function(pres) { 971 | var c, c2, data, i, j, len, len1, ref, ref1, ref2; 972 | data = {}; 973 | data.nick = strophe_js.Strophe.getResourceFromJid(pres.getAttribute("from")); 974 | data.type = pres.getAttribute("type"); 975 | data.states = []; 976 | ref = pres.childNodes; 977 | for (i = 0, len = ref.length; i < len; i++) { 978 | c = ref[i]; 979 | switch (c.nodeName) { 980 | case "error": 981 | data.errorcode = c.getAttribute("code"); 982 | data.error = (ref1 = c.childNodes[0]) != null ? ref1.nodeName : void 0; 983 | break; 984 | case "status": 985 | data.status = c.textContent || null; 986 | break; 987 | case "show": 988 | data.show = c.textContent || null; 989 | break; 990 | case "x": 991 | if (c.getAttribute("xmlns") === strophe_js.Strophe.NS.MUC_USER) { 992 | ref2 = c.childNodes; 993 | for (j = 0, len1 = ref2.length; j < len1; j++) { 994 | c2 = ref2[j]; 995 | switch (c2.nodeName) { 996 | case "item": 997 | data.affiliation = c2.getAttribute("affiliation"); 998 | data.role = c2.getAttribute("role"); 999 | data.jid = c2.getAttribute("jid"); 1000 | data.newnick = c2.getAttribute("nick"); 1001 | break; 1002 | case "status": 1003 | if (c2.getAttribute("code")) { 1004 | data.states.push(c2.getAttribute("code")); 1005 | } 1006 | } 1007 | } 1008 | } 1009 | } 1010 | } 1011 | return data; 1012 | }; 1013 | 1014 | return XmppRoom; 1015 | 1016 | })(); 1017 | 1018 | RoomConfig = (function() { 1019 | function RoomConfig(info) { 1020 | this.parse = bind(this.parse, this); 1021 | if (info != null) { 1022 | this.parse(info); 1023 | } 1024 | } 1025 | 1026 | RoomConfig.prototype.parse = function(result) { 1027 | var attr, attrs, child, field, i, identity, j, l, len, len1, len2, query, ref; 1028 | query = result.getElementsByTagName("query")[0].childNodes; 1029 | this.identities = []; 1030 | this.features = []; 1031 | this.x = []; 1032 | for (i = 0, len = query.length; i < len; i++) { 1033 | child = query[i]; 1034 | attrs = child.attributes; 1035 | switch (child.nodeName) { 1036 | case "identity": 1037 | identity = {}; 1038 | for (j = 0, len1 = attrs.length; j < len1; j++) { 1039 | attr = attrs[j]; 1040 | identity[attr.name] = attr.textContent; 1041 | } 1042 | this.identities.push(identity); 1043 | break; 1044 | case "feature": 1045 | this.features.push(child.getAttribute("var")); 1046 | break; 1047 | case "x": 1048 | if ((!child.childNodes[0].getAttribute("var") === 'FORM_TYPE') || (!child.childNodes[0].getAttribute("type") === 'hidden')) { 1049 | break; 1050 | } 1051 | ref = child.childNodes; 1052 | for (l = 0, len2 = ref.length; l < len2; l++) { 1053 | field = ref[l]; 1054 | if (!field.attributes.type) { 1055 | this.x.push({ 1056 | "var": field.getAttribute("var"), 1057 | label: field.getAttribute("label") || "", 1058 | value: field.firstChild.textContent || "" 1059 | }); 1060 | } 1061 | } 1062 | } 1063 | } 1064 | return { 1065 | "identities": this.identities, 1066 | "features": this.features, 1067 | "x": this.x 1068 | }; 1069 | }; 1070 | 1071 | return RoomConfig; 1072 | 1073 | })(); 1074 | 1075 | Occupant = (function() { 1076 | function Occupant(data, room1) { 1077 | this.room = room1; 1078 | this.update = bind(this.update, this); 1079 | this.admin = bind(this.admin, this); 1080 | this.owner = bind(this.owner, this); 1081 | this.revoke = bind(this.revoke, this); 1082 | this.member = bind(this.member, this); 1083 | this.ban = bind(this.ban, this); 1084 | this.modifyAffiliation = bind(this.modifyAffiliation, this); 1085 | this.deop = bind(this.deop, this); 1086 | this.op = bind(this.op, this); 1087 | this.mute = bind(this.mute, this); 1088 | this.voice = bind(this.voice, this); 1089 | this.kick = bind(this.kick, this); 1090 | this.modifyRole = bind(this.modifyRole, this); 1091 | this.update(data); 1092 | } 1093 | 1094 | Occupant.prototype.modifyRole = function(role, reason, success_cb, error_cb) { 1095 | return this.room.modifyRole(this.nick, role, reason, success_cb, error_cb); 1096 | }; 1097 | 1098 | Occupant.prototype.kick = function(reason, handler_cb, error_cb) { 1099 | return this.room.kick(this.nick, reason, handler_cb, error_cb); 1100 | }; 1101 | 1102 | Occupant.prototype.voice = function(reason, handler_cb, error_cb) { 1103 | return this.room.voice(this.nick, reason, handler_cb, error_cb); 1104 | }; 1105 | 1106 | Occupant.prototype.mute = function(reason, handler_cb, error_cb) { 1107 | return this.room.mute(this.nick, reason, handler_cb, error_cb); 1108 | }; 1109 | 1110 | Occupant.prototype.op = function(reason, handler_cb, error_cb) { 1111 | return this.room.op(this.nick, reason, handler_cb, error_cb); 1112 | }; 1113 | 1114 | Occupant.prototype.deop = function(reason, handler_cb, error_cb) { 1115 | return this.room.deop(this.nick, reason, handler_cb, error_cb); 1116 | }; 1117 | 1118 | Occupant.prototype.modifyAffiliation = function(affiliation, reason, success_cb, error_cb) { 1119 | return this.room.modifyAffiliation(this.jid, affiliation, reason, success_cb, error_cb); 1120 | }; 1121 | 1122 | Occupant.prototype.ban = function(reason, handler_cb, error_cb) { 1123 | return this.room.ban(this.jid, reason, handler_cb, error_cb); 1124 | }; 1125 | 1126 | Occupant.prototype.member = function(reason, handler_cb, error_cb) { 1127 | return this.room.member(this.jid, reason, handler_cb, error_cb); 1128 | }; 1129 | 1130 | Occupant.prototype.revoke = function(reason, handler_cb, error_cb) { 1131 | return this.room.revoke(this.jid, reason, handler_cb, error_cb); 1132 | }; 1133 | 1134 | Occupant.prototype.owner = function(reason, handler_cb, error_cb) { 1135 | return this.room.owner(this.jid, reason, handler_cb, error_cb); 1136 | }; 1137 | 1138 | Occupant.prototype.admin = function(reason, handler_cb, error_cb) { 1139 | return this.room.admin(this.jid, reason, handler_cb, error_cb); 1140 | }; 1141 | 1142 | Occupant.prototype.update = function(data) { 1143 | this.nick = data.nick || null; 1144 | this.affiliation = data.affiliation || null; 1145 | this.role = data.role || null; 1146 | this.jid = data.jid || null; 1147 | this.status = data.status || null; 1148 | this.show = data.show || null; 1149 | return this; 1150 | }; 1151 | 1152 | return Occupant; 1153 | 1154 | })(); 1155 | 1156 | }))); 1157 | //# sourceMappingURL=strophe.muc.js.map 1158 | -------------------------------------------------------------------------------- /lib/strophe.muc.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"strophe.muc.js","sources":["../src/strophe.muc.coffee"],"sourcesContent":["###\n *Plugin to implement the MUC extension.\n http://xmpp.org/extensions/xep-0045.html\n *Previous Author:\n Nathan Zorn \n *Complete CoffeeScript rewrite:\n Andreas Guth \n###\n`import { $build, $iq, $msg, $pres, Strophe } from 'strophe.js';`\n\nStrophe.addConnectionPlugin 'muc',\n _connection: null\n rooms: {}\n roomNames: []\n\n ###Function\n Initialize the MUC plugin. Sets the correct connection object and\n extends the namesace.\n ###\n init: (conn) ->\n @_connection = conn\n @_muc_handler = null\n # extend name space\n # NS.MUC - XMPP Multi-user chat namespace from XEP 45.\n Strophe.addNamespace 'MUC_OWNER', Strophe.NS.MUC+\"#owner\"\n Strophe.addNamespace 'MUC_ADMIN', Strophe.NS.MUC+\"#admin\"\n Strophe.addNamespace 'MUC_USER', Strophe.NS.MUC+\"#user\"\n Strophe.addNamespace 'MUC_ROOMCONF', Strophe.NS.MUC+\"#roomconfig\"\n Strophe.addNamespace 'MUC_REGISTER', \"jabber:iq:register\"\n\n ###Function\n Join a multi-user chat room\n Parameters:\n (String) room - The multi-user chat room to join.\n (String) nick - The nickname to use in the chat room. Optional\n (Function) msg_handler_cb - The function call to handle messages from the\n specified chat room.\n (Function) pres_handler_cb - The function call back to handle presence\n in the chat room.\n (Function) roster_cb - The function call to handle roster info in the chat room\n (String) password - The optional password to use. (password protected\n rooms only)\n (Object) history_attrs - Optional attributes for retrieving history\n (XML DOM Element) extended_presence - Optional XML for extending presence\n ###\n join: (room, nick, msg_handler_cb, pres_handler_cb, roster_cb, password, history_attrs, extended_presence) ->\n room_nick = @test_append_nick(room, nick)\n msg = $pres(\n from: @_connection.jid\n to: room_nick )\n .c(\"x\", xmlns: Strophe.NS.MUC)\n\n if history_attrs?\n msg = msg.c(\"history\", history_attrs).up()\n\n if password?\n msg.cnode Strophe.xmlElement(\"password\", [], password)\n\n if extended_presence?\n msg.up().cnode extended_presence\n\n # One handler for all rooms that dispatches to room callbacks\n @_muc_handler ?= @_connection.addHandler (stanza) =>\n from = stanza.getAttribute 'from'\n return true unless from\n roomname = from.split(\"/\")[0]\n\n # Abort if the stanza is not for a known MUC\n return true unless @rooms[roomname]\n room = @rooms[roomname]\n\n handlers = {}\n\n #select the right handlers\n if stanza.nodeName is \"message\"\n handlers = room._message_handlers\n else if stanza.nodeName is \"presence\"\n xquery = stanza.getElementsByTagName \"x\"\n if xquery.length > 0\n # Handle only MUC user protocol\n for x in xquery\n xmlns = x.getAttribute \"xmlns\"\n if xmlns and xmlns.match Strophe.NS.MUC\n handlers = room._presence_handlers\n break\n\n # loop over selected handlers (if any) and remove on false\n for id, handler of handlers\n delete handlers[id] unless handler stanza, room\n\n return true\n\n unless @rooms.hasOwnProperty(room)\n @rooms[room] = new XmppRoom(@, room, nick, password )\n @rooms[room].addHandler 'presence', pres_handler_cb if pres_handler_cb\n @rooms[room].addHandler 'message', msg_handler_cb if msg_handler_cb\n @rooms[room].addHandler 'roster', roster_cb if roster_cb\n @roomNames.push room\n\n @_connection.send msg\n\n ###Function\n Leave a multi-user chat room\n Parameters:\n (String) room - The multi-user chat room to leave.\n (String) nick - The nick name used in the room.\n (Function) handler_cb - Optional function to handle the successful leave.\n (String) exit_msg - optional exit message.\n Returns:\n iqid - The unique id for the room leave.\n ###\n leave: (room, nick, handler_cb, exit_msg) ->\n id = @roomNames.indexOf room\n delete @rooms[room]\n if id >=0\n @roomNames.splice id, 1\n if @roomNames.length is 0\n @_connection.deleteHandler @_muc_handler\n @_muc_handler = null\n room_nick = @test_append_nick room, nick\n presenceid = @_connection.getUniqueId()\n presence = $pres (\n type: \"unavailable\"\n id: presenceid\n from: @_connection.jid\n to: room_nick )\n\n presence.c \"status\", exit_msg if exit_msg?\n\n if handler_cb?\n @_connection.addHandler(\n handler_cb\n null\n \"presence\"\n null\n presenceid )\n\n @_connection.send presence\n return presenceid\n\n ###Function\n Parameters:\n (String) room - The multi-user chat room name.\n (String) nick - The nick name used in the chat room.\n (String) message - The plaintext message to send to the room.\n (String) html_message - The message to send to the room with html markup.\n (String) type - \"groupchat\" for group chat messages o\n \"chat\" for private chat messages\n Returns:\n msgiq - the unique id used to send the message\n ###\n message: (room, nick, message, html_message, type, msgid) ->\n room_nick = @test_append_nick(room, nick)\n type = type or if nick? then \"chat\" else \"groupchat\"\n msgid = msgid or @_connection.getUniqueId()\n msg = $msg(\n to: room_nick\n from: @_connection.jid\n type: type\n id: msgid )\n .c(\"body\")\n .t(message)\n msg.up()\n if html_message?\n msg.c(\"html\", xmlns: Strophe.NS.XHTML_IM)\n .c(\"body\", xmlns: Strophe.NS.XHTML)\n .h(html_message)\n if msg.node.childNodes.length is 0\n # html creation or import failed somewhere; fallback to plaintext\n parent = msg.node.parentNode\n msg.up().up()\n # get rid of the empty html element if we got invalid html\n #so we don't send an empty message\n msg.node.removeChild parent\n else\n msg.up().up()\n msg.c(\"x\", xmlns: \"jabber:x:event\").c(\"composing\")\n @_connection.send msg\n return msgid\n\n ###Function\n Convenience Function to send a Message to all Occupants\n Parameters:\n (String) room - The multi-user chat room name.\n (String) message - The plaintext message to send to the room.\n (String) html_message - The message to send to the room with html markup.\n (String) msgid - Optional unique ID which will be set as the 'id' attribute of the stanza\n Returns:\n msgiq - the unique id used to send the message\n ###\n groupchat: (room, message, html_message, msgid) ->\n @message room, null, message, html_message, undefined, msgid\n\n ###Function\n Send a mediated invitation.\n Parameters:\n (String) room - The multi-user chat room name.\n (String) receiver - The invitation's receiver.\n (String) reason - Optional reason for joining the room.\n Returns:\n msgiq - the unique id used to send the invitation\n ###\n invite: (room, receiver, reason) ->\n msgid = @_connection.getUniqueId()\n invitation = $msg(\n from: @_connection.jid\n to: room\n id: msgid )\n .c('x', xmlns: Strophe.NS.MUC_USER)\n .c('invite', to: receiver)\n invitation.c 'reason', reason if reason?\n @_connection.send invitation\n return msgid\n\n ###Function\n Send a mediated multiple invitation.\n Parameters:\n (String) room - The multi-user chat room name.\n (Array) receivers - The invitation's receivers.\n (String) reason - Optional reason for joining the room.\n Returns:\n msgiq - the unique id used to send the invitation\n ###\n multipleInvites: (room, receivers, reason) ->\n msgid = @_connection.getUniqueId()\n invitation = $msg(\n from: @_connection.jid\n to: room\n id: msgid )\n .c('x', xmlns: Strophe.NS.MUC_USER)\n\n for receiver in receivers\n invitation.c 'invite', to: receiver\n if reason?\n invitation.c 'reason', reason\n invitation.up()\n invitation.up()\n\n @_connection.send invitation\n return msgid\n\n ###Function\n Send a direct invitation.\n Parameters:\n (String) room - The multi-user chat room name.\n (String) receiver - The invitation's receiver.\n (String) reason - Optional reason for joining the room.\n (String) password - Optional password for the room.\n Returns:\n msgiq - the unique id used to send the invitation\n ###\n directInvite: (room, receiver, reason, password) ->\n msgid = @_connection.getUniqueId()\n attrs =\n xmlns: 'jabber:x:conference'\n jid: room\n attrs.reason = reason if reason?\n attrs.password = password if password?\n invitation = $msg(\n from: @_connection.jid\n to: receiver\n id: msgid )\n .c('x', attrs)\n @_connection.send invitation\n return msgid\n\n ###Function\n Queries a room for a list of occupants\n (String) room - The multi-user chat room name.\n (Function) success_cb - Optional function to handle the info.\n (Function) error_cb - Optional function to handle an error.\n Returns:\n id - the unique id used to send the info request\n ###\n queryOccupants: (room, success_cb, error_cb) ->\n attrs = xmlns: Strophe.NS.DISCO_ITEMS\n info = $iq(\n from:this._connection.jid\n to:room\n type:'get' )\n .c('query', attrs)\n @_connection.sendIQ info, success_cb, error_cb\n\n ###Function\n Start a room configuration.\n Parameters:\n (String) room - The multi-user chat room name.\n (Function) handler_cb - Optional function to handle the config form.\n Returns:\n id - the unique id used to send the configuration request\n ###\n configure: (room, handler_cb, error_cb) ->\n # send iq to start room configuration\n config = $iq(\n to:room\n type: \"get\" )\n .c(\"query\", xmlns: Strophe.NS.MUC_OWNER)\n stanza = config.tree()\n @_connection.sendIQ stanza, handler_cb, error_cb\n\n ###Function\n Cancel the room configuration\n Parameters:\n (String) room - The multi-user chat room name.\n Returns:\n id - the unique id used to cancel the configuration.\n ###\n cancelConfigure: (room) ->\n #send iq to start room configuration\n config = $iq(\n to: room\n type: \"set\" )\n .c(\"query\", xmlns: Strophe.NS.MUC_OWNER)\n .c(\"x\", xmlns: \"jabber:x:data\", type: \"cancel\")\n stanza = config.tree()\n @_connection.sendIQ stanza\n\n ###Function\n Save a room configuration.\n Parameters:\n (String) room - The multi-user chat room name.\n (Array) config- Form Object or an array of form elements used to configure the room.\n Returns:\n id - the unique id used to save the configuration.\n ###\n saveConfiguration: (room, config, success_cb, error_cb) ->\n iq = $iq(\n to: room\n type: \"set\" )\n .c(\"query\", xmlns: Strophe.NS.MUC_OWNER)\n if typeof Strophe.x isnt \"undefined\" and typeof Strophe.x.Form isnt \"undefined\" and config instanceof Strophe.x.Form\n config.type = \"submit\"\n iq.cnode config.toXML()\n else\n iq.c(\"x\", xmlns: \"jabber:x:data\", type: \"submit\")\n iq.cnode(conf).up() for conf in config\n stanza = iq.tree()\n @_connection.sendIQ stanza, success_cb, error_cb\n\n ###Function\n Parameters:\n (String) room - The multi-user chat room name.\n Returns:\n id - the unique id used to create the chat room.\n ###\n createInstantRoom: (room, success_cb, error_cb) ->\n roomiq = $iq(\n to: room\n type: \"set\" )\n .c(\"query\", xmlns: Strophe.NS.MUC_OWNER)\n .c(\"x\", xmlns: \"jabber:x:data\", type: \"submit\")\n @_connection.sendIQ roomiq.tree(), success_cb, error_cb\n\n ###Function\n Parameters:\n (String) room - The multi-user chat room name.\n (Object) config - the configuration. ex: {\"muc#roomconfig_publicroom\": \"0\", \"muc#roomconfig_persistentroom\": \"1\"}\n Returns:\n id - the unique id used to create the chat room.\n ###\n createConfiguredRoom: (room, config, success_cb, error_cb) ->\n roomiq = $iq(\n to: room\n type: \"set\" )\n .c(\"query\", xmlns: Strophe.NS.MUC_OWNER)\n .c(\"x\", xmlns: \"jabber:x:data\", type: \"submit\")\n\n # Owner submits configuration form\n roomiq.c('field', { 'var': 'FORM_TYPE' }).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();\n\n roomiq.c('field', { 'var': k}).c('value').t(v).up().up() for own k, v of config\n\n @_connection.sendIQ roomiq.tree(), success_cb, error_cb\n\n ###Function\n Set the topic of the chat room.\n Parameters:\n (String) room - The multi-user chat room name.\n (String) topic - Topic message.\n ###\n setTopic: (room, topic) ->\n msg = $msg(\n to: room\n from: @_connection.jid\n type: \"groupchat\" )\n .c(\"subject\", xmlns: \"jabber:client\")\n .t(topic)\n @_connection.send msg.tree()\n\n ###Function\n Internal Function that Changes the role or affiliation of a member\n of a MUC room. This function is used by modifyRole and modifyAffiliation.\n The modification can only be done by a room moderator. An error will be\n returned if the user doesn't have permission.\n Parameters:\n (String) room - The multi-user chat room name.\n (Object) item - Object with nick and role or jid and affiliation attribute\n (String) reason - Optional reason for the change.\n (Function) handler_cb - Optional callback for success\n (Function) error_cb - Optional callback for error\n Returns:\n iq - the id of the mode change request.\n ###\n _modifyPrivilege: (room, item, reason, handler_cb, error_cb) ->\n iq = $iq(\n to: room\n type: \"set\" )\n .c(\"query\", xmlns: Strophe.NS.MUC_ADMIN)\n .cnode(item.node)\n\n iq.c(\"reason\", reason) if reason?\n\n @_connection.sendIQ iq.tree(), handler_cb, error_cb\n\n ###Function\n Changes the role of a member of a MUC room.\n The modification can only be done by a room moderator. An error will be\n returned if the user doesn't have permission.\n Parameters:\n (String) room - The multi-user chat room name.\n (String) nick - The nick name of the user to modify.\n (String) role - The new role of the user.\n (String) affiliation - The new affiliation of the user.\n (String) reason - Optional reason for the change.\n (Function) handler_cb - Optional callback for success\n (Function) error_cb - Optional callback for error\n Returns:\n iq - the id of the mode change request.\n ###\n modifyRole: (room, nick, role, reason, handler_cb, error_cb) ->\n item = $build(\"item\"\n nick: nick\n role: role )\n\n @_modifyPrivilege room, item, reason, handler_cb, error_cb\n\n kick: (room, nick, reason, handler_cb, error_cb) ->\n @modifyRole room, nick, 'none', reason, handler_cb, error_cb\n\n voice: (room, nick, reason, handler_cb, error_cb) ->\n @modifyRole room, nick, 'participant', reason, handler_cb, error_cb\n\n mute: (room, nick, reason, handler_cb, error_cb) ->\n @modifyRole room, nick, 'visitor', reason, handler_cb, error_cb\n\n op: (room, nick, reason, handler_cb, error_cb) ->\n @modifyRole room, nick, 'moderator', reason, handler_cb, error_cb\n\n deop: (room, nick, reason, handler_cb, error_cb) ->\n @modifyRole room, nick, 'participant', reason, handler_cb, error_cb\n\n ###Function\n Changes the affiliation of a member of a MUC room.\n The modification can only be done by a room moderator. An error will be\n returned if the user doesn't have permission.\n Parameters:\n (String) room - The multi-user chat room name.\n (String) jid - The jid of the user to modify.\n (String) affiliation - The new affiliation of the user.\n (String) reason - Optional reason for the change.\n (Function) handler_cb - Optional callback for success\n (Function) error_cb - Optional callback for error\n Returns:\n iq - the id of the mode change request.\n ###\n modifyAffiliation: (room, jid, affiliation, reason, handler_cb, error_cb) ->\n item = $build(\"item\"\n jid: jid\n affiliation: affiliation )\n\n @_modifyPrivilege room, item, reason, handler_cb, error_cb\n\n ban: (room, jid, reason, handler_cb, error_cb) ->\n @modifyAffiliation room, jid, 'outcast', reason, handler_cb, error_cb\n\n member: (room, jid, reason, handler_cb, error_cb) ->\n @modifyAffiliation room, jid, 'member', reason, handler_cb, error_cb\n\n revoke: (room, jid, reason, handler_cb, error_cb) ->\n @modifyAffiliation room, jid, 'none', reason, handler_cb, error_cb\n\n owner: (room, jid, reason, handler_cb, error_cb) ->\n @modifyAffiliation room, jid, 'owner', reason, handler_cb, error_cb\n\n admin: (room, jid, reason, handler_cb, error_cb) ->\n @modifyAffiliation room, jid, 'admin', reason, handler_cb, error_cb\n\n ###Function\n Change the current users nick name.\n Parameters:\n (String) room - The multi-user chat room name.\n (String) user - The new nick name.\n ###\n changeNick: (room, user) ->\n room_nick = @test_append_nick room, user\n presence = $pres(\n from: @_connection.jid\n to: room_nick\n id: @_connection.getUniqueId() )\n @_connection.send presence.tree()\n\n ###Function\n Change the current users status.\n Parameters:\n (String) room - The multi-user chat room name.\n (String) user - The current nick.\n (String) show - The new show-text.\n (String) status - The new status-text.\n ###\n setStatus: (room, user, show, status) ->\n room_nick = @test_append_nick room, user\n presence = $pres(\n from: @_connection.jid\n to: room_nick )\n presence.c('show', show).up() if show?\n presence.c('status', status) if status?\n @_connection.send presence.tree()\n\n ###Function\n Registering with a room.\n @see http://xmpp.org/extensions/xep-0045.html#register\n Parameters:\n (String) room - The multi-user chat room name.\n (Function) handle_cb - Function to call for room list return.\n (Function) error_cb - Function to call on error.\n ###\n registrationRequest: (room, handle_cb, error_cb) ->\n iq = $iq(\n to: room,\n from: @_connection.jid,\n type: \"get\"\n )\n .c(\"query\", xmlns: Strophe.NS.MUC_REGISTER)\n\n @_connection.sendIQ iq, (stanza) ->\n $fields = stanza.getElementsByTagName 'field'\n length = $fields.length\n fields =\n required: []\n optional: []\n\n for $field in $fields\n field =\n var: $field.getAttribute 'var'\n label: $field.getAttribute 'label'\n type: $field.getAttribute 'type'\n\n if $field.getElementsByTagName('required').length > 0\n fields.required.push field\n else\n fields.optional.push field\n\n handle_cb fields\n , error_cb\n\n ###Function\n Submits registration form.\n Parameters:\n (String) room - The multi-user chat room name.\n (Function) handle_cb - Function to call for room list return.\n (Function) error_cb - Function to call on error.\n ###\n submitRegistrationForm: (room, fields, handle_cb, error_cb) ->\n iq = $iq({\n to: room,\n type: \"set\"\n }).c(\"query\", xmlns: Strophe.NS.MUC_REGISTER);\n iq.c(\"x\",\n xmlns: \"jabber:x:data\",\n type: \"submit\"\n );\n iq.c('field', 'var': 'FORM_TYPE')\n .c('value')\n .t('http://jabber.org/protocol/muc#register')\n .up().up()\n\n for key, val of fields\n iq.c('field', 'var': key)\n .c('value')\n .t(val).up().up()\n\n @._connection.sendIQ iq, handle_cb, error_cb\n\n ###Function\n List all chat room available on a server.\n Parameters:\n (String) server - name of chat server.\n (String) handle_cb - Function to call for room list return.\n (String) error_cb - Function to call on error.\n ###\n listRooms: (server, handle_cb, error_cb) ->\n iq = $iq(\n to: server\n from: @_connection.jid\n type: \"get\" )\n .c(\"query\", xmlns: Strophe.NS.DISCO_ITEMS)\n @_connection.sendIQ iq, handle_cb, error_cb\n\n test_append_nick: (room, nick) ->\n node = Strophe.escapeNode(Strophe.getNodeFromJid(room))\n domain = Strophe.getDomainFromJid(room)\n node + \"@\" + domain + if nick? then \"/#{nick}\" else \"\"\n\nclass XmppRoom\n\n\n constructor: (@client, @name, @nick, @password) ->\n @roster = {}\n @_message_handlers = {}\n @_presence_handlers = {}\n @_roster_handlers = {}\n @_handler_ids = 0\n @client = @client.muc if @client.muc\n @name = Strophe.getBareJidFromJid @name\n @addHandler 'presence', @_roomRosterHandler\n\n join: (msg_handler_cb, pres_handler_cb, roster_cb) ->\n @client.join(@name, @nick, msg_handler_cb, pres_handler_cb, roster_cb, @password)\n\n leave: (handler_cb, message) ->\n @client.leave @name, @nick, handler_cb, message\n delete @client.rooms[@name]\n\n message: (nick, message, html_message, type) ->\n @client.message @name, nick, message, html_message, type\n\n groupchat: (message, html_message) ->\n @client.groupchat @name, message, html_message\n\n invite: (receiver, reason) ->\n @client.invite @name, receiver, reason\n\n multipleInvites: (receivers, reason) ->\n @client.invite @name, receivers, reason\n\n directInvite: (receiver, reason) ->\n @client.directInvite @name, receiver, reason, @password\n\n configure: (handler_cb) ->\n @client.configure @name, handler_cb\n\n cancelConfigure: ->\n @client.cancelConfigure @name\n\n saveConfiguration: (config) ->\n @client.saveConfiguration @name, config\n\n queryOccupants: (success_cb, error_cb) ->\n @client.queryOccupants @name, success_cb, error_cb\n\n setTopic: (topic) ->\n @client.setTopic @name, topic\n\n modifyRole: (nick, role, reason, success_cb, error_cb) ->\n @client.modifyRole @name, nick, role, reason, success_cb, error_cb\n\n kick: (nick, reason, handler_cb, error_cb) ->\n @client.kick @name, nick, reason, handler_cb, error_cb\n\n voice: (nick, reason, handler_cb, error_cb) ->\n @client.voice @name, nick, reason, handler_cb, error_cb\n\n mute: (nick, reason, handler_cb, error_cb) ->\n @client.mute @name, nick, reason, handler_cb, error_cb\n\n op: (nick, reason, handler_cb, error_cb) ->\n @client.op @name, nick, reason, handler_cb, error_cb\n\n deop: (nick, reason, handler_cb, error_cb) ->\n @client.deop @name, nick, reason, handler_cb, error_cb\n\n modifyAffiliation: (jid, affiliation, reason, success_cb, error_cb) ->\n @client.modifyAffiliation @name,\n jid, affiliation, reason,\n success_cb, error_cb\n\n ban: (jid, reason, handler_cb, error_cb) ->\n @client.ban @name, jid, reason, handler_cb, error_cb\n\n member: (jid, reason, handler_cb, error_cb) ->\n @client.member @name, jid, reason, handler_cb, error_cb\n\n revoke: (jid, reason, handler_cb, error_cb) ->\n @client.revoke @name, jid, reason, handler_cb, error_cb\n\n owner: (jid, reason, handler_cb, error_cb) ->\n @client.owner @name, jid, reason, handler_cb, error_cb\n\n admin: (jid, reason, handler_cb, error_cb) ->\n @client.admin @name, jid, reason, handler_cb, error_cb\n\n changeNick: (@nick) ->\n @client.changeNick @name, nick\n\n setStatus: (show, status) ->\n @client.setStatus @name, @nick, show, status\n\n ###Function\n Adds a handler to the MUC room.\n Parameters:\n (String) handler_type - 'message', 'presence' or 'roster'.\n (Function) handler - The handler function.\n Returns:\n id - the id of handler.\n ###\n addHandler: (handler_type, handler) ->\n id = @_handler_ids++\n switch handler_type\n when 'presence'\n @_presence_handlers[id] = handler\n when 'message'\n @_message_handlers[id] = handler\n when 'roster'\n @_roster_handlers[id] = handler\n else\n @_handler_ids--\n return null\n id\n\n ###Function\n Removes a handler from the MUC room.\n This function takes ONLY ids returned by the addHandler function\n of this room. passing handler ids returned by connection.addHandler\n may brake things!\n Parameters:\n (number) id - the id of the handler\n ###\n removeHandler: (id) ->\n delete @_presence_handlers[id]\n delete @_message_handlers[id]\n delete @_roster_handlers[id]\n\n ###Function\n Creates and adds an Occupant to the Room Roster.\n Parameters:\n (Object) data - the data the Occupant is filled with\n Returns:\n occ - the created Occupant.\n ###\n _addOccupant: (data) =>\n occ = new Occupant data, @\n @roster[occ.nick] = occ\n occ\n\n ###Function\n The standard handler that managed the Room Roster.\n Parameters:\n (Object) pres - the presence stanza containing user information\n ###\n _roomRosterHandler: (pres) =>\n data = XmppRoom._parsePresence pres\n nick = data.nick\n newnick = data.newnick or null\n switch data.type\n when 'error' then return true\n when 'unavailable'\n if newnick\n data.nick = newnick\n # If both Occupant Instances exist, switch the new one\n # with the old renamed one\n if @roster[nick] and @roster[newnick]\n @roster[nick].update @roster[newnick]\n @roster[newnick] = @roster[nick]\n # If the renamed Occupant doesn't exist yet but the old one does,\n # let the new one be the Same instance\n if @roster[nick] and not @roster[newnick]\n @roster[newnick] = @roster[nick].update data\n # If the old Occupant is already deleted, do nothing\n # unless @roster[newnick]\n # tmp_occ = @roster[newnick]\n # @roster[newnick].update(data).update(tmp_occ)\n delete @roster[nick]\n else\n if @roster[nick]\n @roster[nick].update data\n else\n @_addOccupant data\n for id, handler of @_roster_handlers\n delete @_roster_handlers[id] unless handler @roster, @\n true\n\n ###Function\n Parses a presence stanza\n Parameters:\n (Object) data - the data extracted from the presence stanza\n ###\n @_parsePresence: (pres) ->\n data = {}\n data.nick = Strophe.getResourceFromJid pres.getAttribute(\"from\")\n data.type = pres.getAttribute(\"type\")\n data.states = []\n for c in pres.childNodes\n switch c.nodeName\n when \"error\"\n data.errorcode = c.getAttribute(\"code\")\n data.error = c.childNodes[0]?.nodeName\n when \"status\"\n data.status = c.textContent or null\n when \"show\"\n data.show = c.textContent or null\n when \"x\"\n if c.getAttribute(\"xmlns\") is Strophe.NS.MUC_USER\n for c2 in c.childNodes\n switch c2.nodeName\n when \"item\"\n data.affiliation = c2.getAttribute(\"affiliation\")\n data.role = c2.getAttribute(\"role\")\n data.jid = c2.getAttribute(\"jid\")\n data.newnick = c2.getAttribute(\"nick\")\n when \"status\"\n if c2.getAttribute(\"code\")\n data.states.push c2.getAttribute(\"code\")\n data\n\nclass RoomConfig\n\n constructor: (info) ->\n @parse info if info?\n\n parse: (result) =>\n query = result.getElementsByTagName(\"query\")[0].childNodes\n @identities = []\n @features = []\n @x = []\n for child in query\n attrs = child.attributes\n switch child.nodeName\n when \"identity\"\n identity = {}\n identity[attr.name] = attr.textContent for attr in attrs\n @identities.push identity\n when \"feature\"\n @features.push child.getAttribute(\"var\")\n when \"x\"\n break if (\n (not child.childNodes[0].getAttribute(\"var\") is 'FORM_TYPE') or\n (not child.childNodes[0].getAttribute(\"type\") is 'hidden') )\n for field in child.childNodes when not field.attributes.type\n @x.push (\n var: field.getAttribute(\"var\")\n label: field.getAttribute(\"label\") or \"\"\n value: field.firstChild.textContent or \"\" )\n\n \"identities\": @identities, \"features\": @features, \"x\": @x\n\nclass Occupant\n constructor: (data, @room) ->\n @update data\n\n modifyRole: (role, reason, success_cb, error_cb) =>\n @room.modifyRole @nick, role, reason, success_cb, error_cb\n\n kick: (reason, handler_cb, error_cb) =>\n @room.kick @nick, reason, handler_cb, error_cb\n\n voice: (reason, handler_cb, error_cb) =>\n @room.voice @nick, reason, handler_cb, error_cb\n\n mute: (reason, handler_cb, error_cb) =>\n @room.mute @nick, reason, handler_cb, error_cb\n\n op: (reason, handler_cb, error_cb) =>\n @room.op @nick, reason, handler_cb, error_cb\n\n deop: (reason, handler_cb, error_cb) =>\n @room.deop @nick, reason, handler_cb, error_cb\n\n modifyAffiliation: (affiliation, reason, success_cb, error_cb) =>\n @room.modifyAffiliation @jid, affiliation, reason, success_cb, error_cb\n\n ban: (reason, handler_cb, error_cb) =>\n @room.ban @jid, reason, handler_cb, error_cb\n\n member: (reason, handler_cb, error_cb) =>\n @room.member @jid, reason, handler_cb, error_cb\n\n revoke: (reason, handler_cb, error_cb) =>\n @room.revoke @jid, reason, handler_cb, error_cb\n\n owner: (reason, handler_cb, error_cb) =>\n @room.owner @jid, reason, handler_cb, error_cb\n\n admin: (reason, handler_cb, error_cb) =>\n @room.admin @jid, reason, handler_cb, error_cb\n\n update: (data) =>\n @nick = data.nick or null\n @affiliation = data.affiliation or null\n @role = data.role or null\n @jid = data.jid or null\n @status = data.status or null\n @show = data.show or null\n @\n\n"],"names":["Strophe","$pres","$msg","$iq","$build"],"mappings":"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"} -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strophejs-plugin-muc", 3 | "version": "1.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "balanced-match": { 8 | "version": "1.0.0", 9 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 10 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 11 | "dev": true 12 | }, 13 | "brace-expansion": { 14 | "version": "1.1.8", 15 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 16 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 17 | "dev": true, 18 | "requires": { 19 | "balanced-match": "1.0.0", 20 | "concat-map": "0.0.1" 21 | } 22 | }, 23 | "coffee-script": { 24 | "version": "1.12.6", 25 | "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.6.tgz", 26 | "integrity": "sha1-KFo/cRVokGUGTWv570Vy22ZpXL8=", 27 | "dev": true 28 | }, 29 | "concat-map": { 30 | "version": "0.0.1", 31 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 32 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 33 | "dev": true 34 | }, 35 | "estree-walker": { 36 | "version": "0.2.1", 37 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.2.1.tgz", 38 | "integrity": "sha1-va/oCVOD2EFNXcLs9MkXO225QS4=", 39 | "dev": true 40 | }, 41 | "minimatch": { 42 | "version": "3.0.4", 43 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 44 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 45 | "dev": true, 46 | "requires": { 47 | "brace-expansion": "1.1.8" 48 | } 49 | }, 50 | "object-assign": { 51 | "version": "4.1.1", 52 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 53 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 54 | "dev": true 55 | }, 56 | "rollup": { 57 | "version": "0.45.2", 58 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.45.2.tgz", 59 | "integrity": "sha512-2+bq5GQSrocdhr+M92mOQRmF1evtLRzv9NdmEC2wo7BILvTG8irHCtD0q+zg8ikNu63iJicdN5IzyxAXRTFKOQ==", 60 | "dev": true, 61 | "requires": { 62 | "source-map-support": "0.4.15" 63 | } 64 | }, 65 | "rollup-plugin-coffee-script": { 66 | "version": "1.1.0", 67 | "resolved": "https://registry.npmjs.org/rollup-plugin-coffee-script/-/rollup-plugin-coffee-script-1.1.0.tgz", 68 | "integrity": "sha1-BIteIw0ZOCNiZWW5kuaEG7F8VUs=", 69 | "dev": true, 70 | "requires": { 71 | "object-assign": "4.1.1", 72 | "rollup-pluginutils": "1.5.2" 73 | } 74 | }, 75 | "rollup-pluginutils": { 76 | "version": "1.5.2", 77 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz", 78 | "integrity": "sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg=", 79 | "dev": true, 80 | "requires": { 81 | "estree-walker": "0.2.1", 82 | "minimatch": "3.0.4" 83 | } 84 | }, 85 | "source-map": { 86 | "version": "0.5.6", 87 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", 88 | "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", 89 | "dev": true 90 | }, 91 | "source-map-support": { 92 | "version": "0.4.15", 93 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", 94 | "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=", 95 | "dev": true, 96 | "requires": { 97 | "source-map": "0.5.6" 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strophejs-plugin-muc", 3 | "version": "1.1.0", 4 | "description": "A strophe.js plugin for Multi-User Chat (XEP-0045)", 5 | "main": "lib/strophe.muc.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "build": "rollup -c", 11 | "prepublish": "npm run --silent build", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/strophe/strophejs-plugin-muc.git" 17 | }, 18 | "author": "", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/strophe/strophejs-plugin-muc/issues" 22 | }, 23 | "homepage": "https://github.com/strophe/strophejs-plugin-muc#readme", 24 | "devDependencies": { 25 | "coffee-script": "^1.12.6", 26 | "rollup": "^0.45.2", 27 | "rollup-plugin-coffee-script": "^1.1.0" 28 | }, 29 | "peerDependencies": { 30 | "strophe.js": ">=1.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import coffeescript from 'rollup-plugin-coffee-script'; 2 | 3 | export default { 4 | entry: 'src/strophe.muc.coffee', 5 | dest: 'lib/strophe.muc.js', 6 | format: 'umd', 7 | globals: { 8 | 'strophe.js': 'window', 9 | }, 10 | external: ['strophe.js'], 11 | plugins: [ 12 | coffeescript() 13 | ], 14 | sourceMap: true 15 | } 16 | -------------------------------------------------------------------------------- /src/strophe.muc.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | *Plugin to implement the MUC extension. 3 | http://xmpp.org/extensions/xep-0045.html 4 | *Previous Author: 5 | Nathan Zorn 6 | *Complete CoffeeScript rewrite: 7 | Andreas Guth 8 | ### 9 | `import { $build, $iq, $msg, $pres, Strophe } from 'strophe.js';` 10 | 11 | Strophe.addConnectionPlugin 'muc', 12 | _connection: null 13 | rooms: {} 14 | roomNames: [] 15 | 16 | ###Function 17 | Initialize the MUC plugin. Sets the correct connection object and 18 | extends the namesace. 19 | ### 20 | init: (conn) -> 21 | @_connection = conn 22 | @_muc_handler = null 23 | # extend name space 24 | # NS.MUC - XMPP Multi-user chat namespace from XEP 45. 25 | Strophe.addNamespace 'MUC_OWNER', Strophe.NS.MUC+"#owner" 26 | Strophe.addNamespace 'MUC_ADMIN', Strophe.NS.MUC+"#admin" 27 | Strophe.addNamespace 'MUC_USER', Strophe.NS.MUC+"#user" 28 | Strophe.addNamespace 'MUC_ROOMCONF', Strophe.NS.MUC+"#roomconfig" 29 | Strophe.addNamespace 'MUC_REGISTER', "jabber:iq:register" 30 | 31 | ###Function 32 | Join a multi-user chat room 33 | Parameters: 34 | (String) room - The multi-user chat room to join. 35 | (String) nick - The nickname to use in the chat room. Optional 36 | (Function) msg_handler_cb - The function call to handle messages from the 37 | specified chat room. 38 | (Function) pres_handler_cb - The function call back to handle presence 39 | in the chat room. 40 | (Function) roster_cb - The function call to handle roster info in the chat room 41 | (String) password - The optional password to use. (password protected 42 | rooms only) 43 | (Object) history_attrs - Optional attributes for retrieving history 44 | (XML DOM Element) extended_presence - Optional XML for extending presence 45 | ### 46 | join: (room, nick, msg_handler_cb, pres_handler_cb, roster_cb, password, history_attrs, extended_presence) -> 47 | room_nick = @test_append_nick(room, nick) 48 | msg = $pres( 49 | from: @_connection.jid 50 | to: room_nick ) 51 | .c("x", xmlns: Strophe.NS.MUC) 52 | 53 | if history_attrs? 54 | msg = msg.c("history", history_attrs).up() 55 | 56 | if password? 57 | msg.cnode Strophe.xmlElement("password", [], password) 58 | 59 | if extended_presence? 60 | msg.up().cnode extended_presence 61 | 62 | # One handler for all rooms that dispatches to room callbacks 63 | @_muc_handler ?= @_connection.addHandler (stanza) => 64 | from = stanza.getAttribute 'from' 65 | return true unless from 66 | roomname = from.split("/")[0] 67 | 68 | # Abort if the stanza is not for a known MUC 69 | return true unless @rooms[roomname] 70 | room = @rooms[roomname] 71 | 72 | handlers = {} 73 | 74 | #select the right handlers 75 | if stanza.nodeName is "message" 76 | handlers = room._message_handlers 77 | else if stanza.nodeName is "presence" 78 | xquery = stanza.getElementsByTagName "x" 79 | if xquery.length > 0 80 | # Handle only MUC user protocol 81 | for x in xquery 82 | xmlns = x.getAttribute "xmlns" 83 | if xmlns and xmlns.match Strophe.NS.MUC 84 | handlers = room._presence_handlers 85 | break 86 | 87 | # loop over selected handlers (if any) and remove on false 88 | for id, handler of handlers 89 | delete handlers[id] unless handler stanza, room 90 | 91 | return true 92 | 93 | unless @rooms.hasOwnProperty(room) 94 | @rooms[room] = new XmppRoom(@, room, nick, password ) 95 | @rooms[room].addHandler 'presence', pres_handler_cb if pres_handler_cb 96 | @rooms[room].addHandler 'message', msg_handler_cb if msg_handler_cb 97 | @rooms[room].addHandler 'roster', roster_cb if roster_cb 98 | @roomNames.push room 99 | 100 | @_connection.send msg 101 | 102 | ###Function 103 | Leave a multi-user chat room 104 | Parameters: 105 | (String) room - The multi-user chat room to leave. 106 | (String) nick - The nick name used in the room. 107 | (Function) handler_cb - Optional function to handle the successful leave. 108 | (String) exit_msg - optional exit message. 109 | Returns: 110 | iqid - The unique id for the room leave. 111 | ### 112 | leave: (room, nick, handler_cb, exit_msg) -> 113 | id = @roomNames.indexOf room 114 | delete @rooms[room] 115 | if id >=0 116 | @roomNames.splice id, 1 117 | if @roomNames.length is 0 118 | @_connection.deleteHandler @_muc_handler 119 | @_muc_handler = null 120 | room_nick = @test_append_nick room, nick 121 | presenceid = @_connection.getUniqueId() 122 | presence = $pres ( 123 | type: "unavailable" 124 | id: presenceid 125 | from: @_connection.jid 126 | to: room_nick ) 127 | 128 | presence.c "status", exit_msg if exit_msg? 129 | 130 | if handler_cb? 131 | @_connection.addHandler( 132 | handler_cb 133 | null 134 | "presence" 135 | null 136 | presenceid ) 137 | 138 | @_connection.send presence 139 | return presenceid 140 | 141 | ###Function 142 | Parameters: 143 | (String) room - The multi-user chat room name. 144 | (String) nick - The nick name used in the chat room. 145 | (String) message - The plaintext message to send to the room. 146 | (String) html_message - The message to send to the room with html markup. 147 | (String) type - "groupchat" for group chat messages o 148 | "chat" for private chat messages 149 | Returns: 150 | msgiq - the unique id used to send the message 151 | ### 152 | message: (room, nick, message, html_message, type, msgid) -> 153 | room_nick = @test_append_nick(room, nick) 154 | type = type or if nick? then "chat" else "groupchat" 155 | msgid = msgid or @_connection.getUniqueId() 156 | msg = $msg( 157 | to: room_nick 158 | from: @_connection.jid 159 | type: type 160 | id: msgid ) 161 | .c("body") 162 | .t(message) 163 | msg.up() 164 | if html_message? 165 | msg.c("html", xmlns: Strophe.NS.XHTML_IM) 166 | .c("body", xmlns: Strophe.NS.XHTML) 167 | .h(html_message) 168 | if msg.node.childNodes.length is 0 169 | # html creation or import failed somewhere; fallback to plaintext 170 | parent = msg.node.parentNode 171 | msg.up().up() 172 | # get rid of the empty html element if we got invalid html 173 | #so we don't send an empty message 174 | msg.node.removeChild parent 175 | else 176 | msg.up().up() 177 | msg.c("x", xmlns: "jabber:x:event").c("composing") 178 | @_connection.send msg 179 | return msgid 180 | 181 | ###Function 182 | Convenience Function to send a Message to all Occupants 183 | Parameters: 184 | (String) room - The multi-user chat room name. 185 | (String) message - The plaintext message to send to the room. 186 | (String) html_message - The message to send to the room with html markup. 187 | (String) msgid - Optional unique ID which will be set as the 'id' attribute of the stanza 188 | Returns: 189 | msgiq - the unique id used to send the message 190 | ### 191 | groupchat: (room, message, html_message, msgid) -> 192 | @message room, null, message, html_message, undefined, msgid 193 | 194 | ###Function 195 | Send a mediated invitation. 196 | Parameters: 197 | (String) room - The multi-user chat room name. 198 | (String) receiver - The invitation's receiver. 199 | (String) reason - Optional reason for joining the room. 200 | Returns: 201 | msgiq - the unique id used to send the invitation 202 | ### 203 | invite: (room, receiver, reason) -> 204 | msgid = @_connection.getUniqueId() 205 | invitation = $msg( 206 | from: @_connection.jid 207 | to: room 208 | id: msgid ) 209 | .c('x', xmlns: Strophe.NS.MUC_USER) 210 | .c('invite', to: receiver) 211 | invitation.c 'reason', reason if reason? 212 | @_connection.send invitation 213 | return msgid 214 | 215 | ###Function 216 | Send a mediated multiple invitation. 217 | Parameters: 218 | (String) room - The multi-user chat room name. 219 | (Array) receivers - The invitation's receivers. 220 | (String) reason - Optional reason for joining the room. 221 | Returns: 222 | msgiq - the unique id used to send the invitation 223 | ### 224 | multipleInvites: (room, receivers, reason) -> 225 | msgid = @_connection.getUniqueId() 226 | invitation = $msg( 227 | from: @_connection.jid 228 | to: room 229 | id: msgid ) 230 | .c('x', xmlns: Strophe.NS.MUC_USER) 231 | 232 | for receiver in receivers 233 | invitation.c 'invite', to: receiver 234 | if reason? 235 | invitation.c 'reason', reason 236 | invitation.up() 237 | invitation.up() 238 | 239 | @_connection.send invitation 240 | return msgid 241 | 242 | ###Function 243 | Send a direct invitation. 244 | Parameters: 245 | (String) room - The multi-user chat room name. 246 | (String) receiver - The invitation's receiver. 247 | (String) reason - Optional reason for joining the room. 248 | (String) password - Optional password for the room. 249 | Returns: 250 | msgiq - the unique id used to send the invitation 251 | ### 252 | directInvite: (room, receiver, reason, password) -> 253 | msgid = @_connection.getUniqueId() 254 | attrs = 255 | xmlns: 'jabber:x:conference' 256 | jid: room 257 | attrs.reason = reason if reason? 258 | attrs.password = password if password? 259 | invitation = $msg( 260 | from: @_connection.jid 261 | to: receiver 262 | id: msgid ) 263 | .c('x', attrs) 264 | @_connection.send invitation 265 | return msgid 266 | 267 | ###Function 268 | Queries a room for a list of occupants 269 | (String) room - The multi-user chat room name. 270 | (Function) success_cb - Optional function to handle the info. 271 | (Function) error_cb - Optional function to handle an error. 272 | Returns: 273 | id - the unique id used to send the info request 274 | ### 275 | queryOccupants: (room, success_cb, error_cb) -> 276 | attrs = xmlns: Strophe.NS.DISCO_ITEMS 277 | info = $iq( 278 | from:this._connection.jid 279 | to:room 280 | type:'get' ) 281 | .c('query', attrs) 282 | @_connection.sendIQ info, success_cb, error_cb 283 | 284 | ###Function 285 | Start a room configuration. 286 | Parameters: 287 | (String) room - The multi-user chat room name. 288 | (Function) handler_cb - Optional function to handle the config form. 289 | Returns: 290 | id - the unique id used to send the configuration request 291 | ### 292 | configure: (room, handler_cb, error_cb) -> 293 | # send iq to start room configuration 294 | config = $iq( 295 | to:room 296 | type: "get" ) 297 | .c("query", xmlns: Strophe.NS.MUC_OWNER) 298 | stanza = config.tree() 299 | @_connection.sendIQ stanza, handler_cb, error_cb 300 | 301 | ###Function 302 | Cancel the room configuration 303 | Parameters: 304 | (String) room - The multi-user chat room name. 305 | Returns: 306 | id - the unique id used to cancel the configuration. 307 | ### 308 | cancelConfigure: (room) -> 309 | #send iq to start room configuration 310 | config = $iq( 311 | to: room 312 | type: "set" ) 313 | .c("query", xmlns: Strophe.NS.MUC_OWNER) 314 | .c("x", xmlns: "jabber:x:data", type: "cancel") 315 | stanza = config.tree() 316 | @_connection.sendIQ stanza 317 | 318 | ###Function 319 | Save a room configuration. 320 | Parameters: 321 | (String) room - The multi-user chat room name. 322 | (Array) config- Form Object or an array of form elements used to configure the room. 323 | Returns: 324 | id - the unique id used to save the configuration. 325 | ### 326 | saveConfiguration: (room, config, success_cb, error_cb) -> 327 | iq = $iq( 328 | to: room 329 | type: "set" ) 330 | .c("query", xmlns: Strophe.NS.MUC_OWNER) 331 | if typeof Strophe.x isnt "undefined" and typeof Strophe.x.Form isnt "undefined" and config instanceof Strophe.x.Form 332 | config.type = "submit" 333 | iq.cnode config.toXML() 334 | else 335 | iq.c("x", xmlns: "jabber:x:data", type: "submit") 336 | iq.cnode(conf).up() for conf in config 337 | stanza = iq.tree() 338 | @_connection.sendIQ stanza, success_cb, error_cb 339 | 340 | ###Function 341 | Parameters: 342 | (String) room - The multi-user chat room name. 343 | Returns: 344 | id - the unique id used to create the chat room. 345 | ### 346 | createInstantRoom: (room, success_cb, error_cb) -> 347 | roomiq = $iq( 348 | to: room 349 | type: "set" ) 350 | .c("query", xmlns: Strophe.NS.MUC_OWNER) 351 | .c("x", xmlns: "jabber:x:data", type: "submit") 352 | @_connection.sendIQ roomiq.tree(), success_cb, error_cb 353 | 354 | ###Function 355 | Parameters: 356 | (String) room - The multi-user chat room name. 357 | (Object) config - the configuration. ex: {"muc#roomconfig_publicroom": "0", "muc#roomconfig_persistentroom": "1"} 358 | Returns: 359 | id - the unique id used to create the chat room. 360 | ### 361 | createConfiguredRoom: (room, config, success_cb, error_cb) -> 362 | roomiq = $iq( 363 | to: room 364 | type: "set" ) 365 | .c("query", xmlns: Strophe.NS.MUC_OWNER) 366 | .c("x", xmlns: "jabber:x:data", type: "submit") 367 | 368 | # Owner submits configuration form 369 | roomiq.c('field', { 'var': 'FORM_TYPE' }).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up(); 370 | 371 | roomiq.c('field', { 'var': k}).c('value').t(v).up().up() for own k, v of config 372 | 373 | @_connection.sendIQ roomiq.tree(), success_cb, error_cb 374 | 375 | ###Function 376 | Set the topic of the chat room. 377 | Parameters: 378 | (String) room - The multi-user chat room name. 379 | (String) topic - Topic message. 380 | ### 381 | setTopic: (room, topic) -> 382 | msg = $msg( 383 | to: room 384 | from: @_connection.jid 385 | type: "groupchat" ) 386 | .c("subject", xmlns: "jabber:client") 387 | .t(topic) 388 | @_connection.send msg.tree() 389 | 390 | ###Function 391 | Internal Function that Changes the role or affiliation of a member 392 | of a MUC room. This function is used by modifyRole and modifyAffiliation. 393 | The modification can only be done by a room moderator. An error will be 394 | returned if the user doesn't have permission. 395 | Parameters: 396 | (String) room - The multi-user chat room name. 397 | (Object) item - Object with nick and role or jid and affiliation attribute 398 | (String) reason - Optional reason for the change. 399 | (Function) handler_cb - Optional callback for success 400 | (Function) error_cb - Optional callback for error 401 | Returns: 402 | iq - the id of the mode change request. 403 | ### 404 | _modifyPrivilege: (room, item, reason, handler_cb, error_cb) -> 405 | iq = $iq( 406 | to: room 407 | type: "set" ) 408 | .c("query", xmlns: Strophe.NS.MUC_ADMIN) 409 | .cnode(item.node) 410 | 411 | iq.c("reason", reason) if reason? 412 | 413 | @_connection.sendIQ iq.tree(), handler_cb, error_cb 414 | 415 | ###Function 416 | Changes the role of a member of a MUC room. 417 | The modification can only be done by a room moderator. An error will be 418 | returned if the user doesn't have permission. 419 | Parameters: 420 | (String) room - The multi-user chat room name. 421 | (String) nick - The nick name of the user to modify. 422 | (String) role - The new role of the user. 423 | (String) affiliation - The new affiliation of the user. 424 | (String) reason - Optional reason for the change. 425 | (Function) handler_cb - Optional callback for success 426 | (Function) error_cb - Optional callback for error 427 | Returns: 428 | iq - the id of the mode change request. 429 | ### 430 | modifyRole: (room, nick, role, reason, handler_cb, error_cb) -> 431 | item = $build("item" 432 | nick: nick 433 | role: role ) 434 | 435 | @_modifyPrivilege room, item, reason, handler_cb, error_cb 436 | 437 | kick: (room, nick, reason, handler_cb, error_cb) -> 438 | @modifyRole room, nick, 'none', reason, handler_cb, error_cb 439 | 440 | voice: (room, nick, reason, handler_cb, error_cb) -> 441 | @modifyRole room, nick, 'participant', reason, handler_cb, error_cb 442 | 443 | mute: (room, nick, reason, handler_cb, error_cb) -> 444 | @modifyRole room, nick, 'visitor', reason, handler_cb, error_cb 445 | 446 | op: (room, nick, reason, handler_cb, error_cb) -> 447 | @modifyRole room, nick, 'moderator', reason, handler_cb, error_cb 448 | 449 | deop: (room, nick, reason, handler_cb, error_cb) -> 450 | @modifyRole room, nick, 'participant', reason, handler_cb, error_cb 451 | 452 | ###Function 453 | Changes the affiliation of a member of a MUC room. 454 | The modification can only be done by a room moderator. An error will be 455 | returned if the user doesn't have permission. 456 | Parameters: 457 | (String) room - The multi-user chat room name. 458 | (String) jid - The jid of the user to modify. 459 | (String) affiliation - The new affiliation of the user. 460 | (String) reason - Optional reason for the change. 461 | (Function) handler_cb - Optional callback for success 462 | (Function) error_cb - Optional callback for error 463 | Returns: 464 | iq - the id of the mode change request. 465 | ### 466 | modifyAffiliation: (room, jid, affiliation, reason, handler_cb, error_cb) -> 467 | item = $build("item" 468 | jid: jid 469 | affiliation: affiliation ) 470 | 471 | @_modifyPrivilege room, item, reason, handler_cb, error_cb 472 | 473 | ban: (room, jid, reason, handler_cb, error_cb) -> 474 | @modifyAffiliation room, jid, 'outcast', reason, handler_cb, error_cb 475 | 476 | member: (room, jid, reason, handler_cb, error_cb) -> 477 | @modifyAffiliation room, jid, 'member', reason, handler_cb, error_cb 478 | 479 | revoke: (room, jid, reason, handler_cb, error_cb) -> 480 | @modifyAffiliation room, jid, 'none', reason, handler_cb, error_cb 481 | 482 | owner: (room, jid, reason, handler_cb, error_cb) -> 483 | @modifyAffiliation room, jid, 'owner', reason, handler_cb, error_cb 484 | 485 | admin: (room, jid, reason, handler_cb, error_cb) -> 486 | @modifyAffiliation room, jid, 'admin', reason, handler_cb, error_cb 487 | 488 | ###Function 489 | Change the current users nick name. 490 | Parameters: 491 | (String) room - The multi-user chat room name. 492 | (String) user - The new nick name. 493 | ### 494 | changeNick: (room, user) -> 495 | room_nick = @test_append_nick room, user 496 | presence = $pres( 497 | from: @_connection.jid 498 | to: room_nick 499 | id: @_connection.getUniqueId() ) 500 | @_connection.send presence.tree() 501 | 502 | ###Function 503 | Change the current users status. 504 | Parameters: 505 | (String) room - The multi-user chat room name. 506 | (String) user - The current nick. 507 | (String) show - The new show-text. 508 | (String) status - The new status-text. 509 | ### 510 | setStatus: (room, user, show, status) -> 511 | room_nick = @test_append_nick room, user 512 | presence = $pres( 513 | from: @_connection.jid 514 | to: room_nick ) 515 | presence.c('show', show).up() if show? 516 | presence.c('status', status) if status? 517 | @_connection.send presence.tree() 518 | 519 | ###Function 520 | Registering with a room. 521 | @see http://xmpp.org/extensions/xep-0045.html#register 522 | Parameters: 523 | (String) room - The multi-user chat room name. 524 | (Function) handle_cb - Function to call for room list return. 525 | (Function) error_cb - Function to call on error. 526 | ### 527 | registrationRequest: (room, handle_cb, error_cb) -> 528 | iq = $iq( 529 | to: room, 530 | from: @_connection.jid, 531 | type: "get" 532 | ) 533 | .c("query", xmlns: Strophe.NS.MUC_REGISTER) 534 | 535 | @_connection.sendIQ iq, (stanza) -> 536 | $fields = stanza.getElementsByTagName 'field' 537 | length = $fields.length 538 | fields = 539 | required: [] 540 | optional: [] 541 | 542 | for $field in $fields 543 | field = 544 | var: $field.getAttribute 'var' 545 | label: $field.getAttribute 'label' 546 | type: $field.getAttribute 'type' 547 | 548 | if $field.getElementsByTagName('required').length > 0 549 | fields.required.push field 550 | else 551 | fields.optional.push field 552 | 553 | handle_cb fields 554 | , error_cb 555 | 556 | ###Function 557 | Submits registration form. 558 | Parameters: 559 | (String) room - The multi-user chat room name. 560 | (Function) handle_cb - Function to call for room list return. 561 | (Function) error_cb - Function to call on error. 562 | ### 563 | submitRegistrationForm: (room, fields, handle_cb, error_cb) -> 564 | iq = $iq({ 565 | to: room, 566 | type: "set" 567 | }).c("query", xmlns: Strophe.NS.MUC_REGISTER); 568 | iq.c("x", 569 | xmlns: "jabber:x:data", 570 | type: "submit" 571 | ); 572 | iq.c('field', 'var': 'FORM_TYPE') 573 | .c('value') 574 | .t('http://jabber.org/protocol/muc#register') 575 | .up().up() 576 | 577 | for key, val of fields 578 | iq.c('field', 'var': key) 579 | .c('value') 580 | .t(val).up().up() 581 | 582 | @._connection.sendIQ iq, handle_cb, error_cb 583 | 584 | ###Function 585 | List all chat room available on a server. 586 | Parameters: 587 | (String) server - name of chat server. 588 | (String) handle_cb - Function to call for room list return. 589 | (String) error_cb - Function to call on error. 590 | ### 591 | listRooms: (server, handle_cb, error_cb) -> 592 | iq = $iq( 593 | to: server 594 | from: @_connection.jid 595 | type: "get" ) 596 | .c("query", xmlns: Strophe.NS.DISCO_ITEMS) 597 | @_connection.sendIQ iq, handle_cb, error_cb 598 | 599 | test_append_nick: (room, nick) -> 600 | node = Strophe.escapeNode(Strophe.getNodeFromJid(room)) 601 | domain = Strophe.getDomainFromJid(room) 602 | node + "@" + domain + if nick? then "/#{nick}" else "" 603 | 604 | class XmppRoom 605 | 606 | 607 | constructor: (@client, @name, @nick, @password) -> 608 | @roster = {} 609 | @_message_handlers = {} 610 | @_presence_handlers = {} 611 | @_roster_handlers = {} 612 | @_handler_ids = 0 613 | @client = @client.muc if @client.muc 614 | @name = Strophe.getBareJidFromJid @name 615 | @addHandler 'presence', @_roomRosterHandler 616 | 617 | join: (msg_handler_cb, pres_handler_cb, roster_cb) -> 618 | @client.join(@name, @nick, msg_handler_cb, pres_handler_cb, roster_cb, @password) 619 | 620 | leave: (handler_cb, message) -> 621 | @client.leave @name, @nick, handler_cb, message 622 | delete @client.rooms[@name] 623 | 624 | message: (nick, message, html_message, type) -> 625 | @client.message @name, nick, message, html_message, type 626 | 627 | groupchat: (message, html_message) -> 628 | @client.groupchat @name, message, html_message 629 | 630 | invite: (receiver, reason) -> 631 | @client.invite @name, receiver, reason 632 | 633 | multipleInvites: (receivers, reason) -> 634 | @client.invite @name, receivers, reason 635 | 636 | directInvite: (receiver, reason) -> 637 | @client.directInvite @name, receiver, reason, @password 638 | 639 | configure: (handler_cb) -> 640 | @client.configure @name, handler_cb 641 | 642 | cancelConfigure: -> 643 | @client.cancelConfigure @name 644 | 645 | saveConfiguration: (config) -> 646 | @client.saveConfiguration @name, config 647 | 648 | queryOccupants: (success_cb, error_cb) -> 649 | @client.queryOccupants @name, success_cb, error_cb 650 | 651 | setTopic: (topic) -> 652 | @client.setTopic @name, topic 653 | 654 | modifyRole: (nick, role, reason, success_cb, error_cb) -> 655 | @client.modifyRole @name, nick, role, reason, success_cb, error_cb 656 | 657 | kick: (nick, reason, handler_cb, error_cb) -> 658 | @client.kick @name, nick, reason, handler_cb, error_cb 659 | 660 | voice: (nick, reason, handler_cb, error_cb) -> 661 | @client.voice @name, nick, reason, handler_cb, error_cb 662 | 663 | mute: (nick, reason, handler_cb, error_cb) -> 664 | @client.mute @name, nick, reason, handler_cb, error_cb 665 | 666 | op: (nick, reason, handler_cb, error_cb) -> 667 | @client.op @name, nick, reason, handler_cb, error_cb 668 | 669 | deop: (nick, reason, handler_cb, error_cb) -> 670 | @client.deop @name, nick, reason, handler_cb, error_cb 671 | 672 | modifyAffiliation: (jid, affiliation, reason, success_cb, error_cb) -> 673 | @client.modifyAffiliation @name, 674 | jid, affiliation, reason, 675 | success_cb, error_cb 676 | 677 | ban: (jid, reason, handler_cb, error_cb) -> 678 | @client.ban @name, jid, reason, handler_cb, error_cb 679 | 680 | member: (jid, reason, handler_cb, error_cb) -> 681 | @client.member @name, jid, reason, handler_cb, error_cb 682 | 683 | revoke: (jid, reason, handler_cb, error_cb) -> 684 | @client.revoke @name, jid, reason, handler_cb, error_cb 685 | 686 | owner: (jid, reason, handler_cb, error_cb) -> 687 | @client.owner @name, jid, reason, handler_cb, error_cb 688 | 689 | admin: (jid, reason, handler_cb, error_cb) -> 690 | @client.admin @name, jid, reason, handler_cb, error_cb 691 | 692 | changeNick: (@nick) -> 693 | @client.changeNick @name, nick 694 | 695 | setStatus: (show, status) -> 696 | @client.setStatus @name, @nick, show, status 697 | 698 | ###Function 699 | Adds a handler to the MUC room. 700 | Parameters: 701 | (String) handler_type - 'message', 'presence' or 'roster'. 702 | (Function) handler - The handler function. 703 | Returns: 704 | id - the id of handler. 705 | ### 706 | addHandler: (handler_type, handler) -> 707 | id = @_handler_ids++ 708 | switch handler_type 709 | when 'presence' 710 | @_presence_handlers[id] = handler 711 | when 'message' 712 | @_message_handlers[id] = handler 713 | when 'roster' 714 | @_roster_handlers[id] = handler 715 | else 716 | @_handler_ids-- 717 | return null 718 | id 719 | 720 | ###Function 721 | Removes a handler from the MUC room. 722 | This function takes ONLY ids returned by the addHandler function 723 | of this room. passing handler ids returned by connection.addHandler 724 | may brake things! 725 | Parameters: 726 | (number) id - the id of the handler 727 | ### 728 | removeHandler: (id) -> 729 | delete @_presence_handlers[id] 730 | delete @_message_handlers[id] 731 | delete @_roster_handlers[id] 732 | 733 | ###Function 734 | Creates and adds an Occupant to the Room Roster. 735 | Parameters: 736 | (Object) data - the data the Occupant is filled with 737 | Returns: 738 | occ - the created Occupant. 739 | ### 740 | _addOccupant: (data) => 741 | occ = new Occupant data, @ 742 | @roster[occ.nick] = occ 743 | occ 744 | 745 | ###Function 746 | The standard handler that managed the Room Roster. 747 | Parameters: 748 | (Object) pres - the presence stanza containing user information 749 | ### 750 | _roomRosterHandler: (pres) => 751 | data = XmppRoom._parsePresence pres 752 | nick = data.nick 753 | newnick = data.newnick or null 754 | switch data.type 755 | when 'error' then return true 756 | when 'unavailable' 757 | if newnick 758 | data.nick = newnick 759 | # If both Occupant Instances exist, switch the new one 760 | # with the old renamed one 761 | if @roster[nick] and @roster[newnick] 762 | @roster[nick].update @roster[newnick] 763 | @roster[newnick] = @roster[nick] 764 | # If the renamed Occupant doesn't exist yet but the old one does, 765 | # let the new one be the Same instance 766 | if @roster[nick] and not @roster[newnick] 767 | @roster[newnick] = @roster[nick].update data 768 | # If the old Occupant is already deleted, do nothing 769 | # unless @roster[newnick] 770 | # tmp_occ = @roster[newnick] 771 | # @roster[newnick].update(data).update(tmp_occ) 772 | delete @roster[nick] 773 | else 774 | if @roster[nick] 775 | @roster[nick].update data 776 | else 777 | @_addOccupant data 778 | for id, handler of @_roster_handlers 779 | delete @_roster_handlers[id] unless handler @roster, @ 780 | true 781 | 782 | ###Function 783 | Parses a presence stanza 784 | Parameters: 785 | (Object) data - the data extracted from the presence stanza 786 | ### 787 | @_parsePresence: (pres) -> 788 | data = {} 789 | data.nick = Strophe.getResourceFromJid pres.getAttribute("from") 790 | data.type = pres.getAttribute("type") 791 | data.states = [] 792 | for c in pres.childNodes 793 | switch c.nodeName 794 | when "error" 795 | data.errorcode = c.getAttribute("code") 796 | data.error = c.childNodes[0]?.nodeName 797 | when "status" 798 | data.status = c.textContent or null 799 | when "show" 800 | data.show = c.textContent or null 801 | when "x" 802 | if c.getAttribute("xmlns") is Strophe.NS.MUC_USER 803 | for c2 in c.childNodes 804 | switch c2.nodeName 805 | when "item" 806 | data.affiliation = c2.getAttribute("affiliation") 807 | data.role = c2.getAttribute("role") 808 | data.jid = c2.getAttribute("jid") 809 | data.newnick = c2.getAttribute("nick") 810 | when "status" 811 | if c2.getAttribute("code") 812 | data.states.push c2.getAttribute("code") 813 | data 814 | 815 | class RoomConfig 816 | 817 | constructor: (info) -> 818 | @parse info if info? 819 | 820 | parse: (result) => 821 | query = result.getElementsByTagName("query")[0].childNodes 822 | @identities = [] 823 | @features = [] 824 | @x = [] 825 | for child in query 826 | attrs = child.attributes 827 | switch child.nodeName 828 | when "identity" 829 | identity = {} 830 | identity[attr.name] = attr.textContent for attr in attrs 831 | @identities.push identity 832 | when "feature" 833 | @features.push child.getAttribute("var") 834 | when "x" 835 | break if ( 836 | (not child.childNodes[0].getAttribute("var") is 'FORM_TYPE') or 837 | (not child.childNodes[0].getAttribute("type") is 'hidden') ) 838 | for field in child.childNodes when not field.attributes.type 839 | @x.push ( 840 | var: field.getAttribute("var") 841 | label: field.getAttribute("label") or "" 842 | value: field.firstChild.textContent or "" ) 843 | 844 | "identities": @identities, "features": @features, "x": @x 845 | 846 | class Occupant 847 | constructor: (data, @room) -> 848 | @update data 849 | 850 | modifyRole: (role, reason, success_cb, error_cb) => 851 | @room.modifyRole @nick, role, reason, success_cb, error_cb 852 | 853 | kick: (reason, handler_cb, error_cb) => 854 | @room.kick @nick, reason, handler_cb, error_cb 855 | 856 | voice: (reason, handler_cb, error_cb) => 857 | @room.voice @nick, reason, handler_cb, error_cb 858 | 859 | mute: (reason, handler_cb, error_cb) => 860 | @room.mute @nick, reason, handler_cb, error_cb 861 | 862 | op: (reason, handler_cb, error_cb) => 863 | @room.op @nick, reason, handler_cb, error_cb 864 | 865 | deop: (reason, handler_cb, error_cb) => 866 | @room.deop @nick, reason, handler_cb, error_cb 867 | 868 | modifyAffiliation: (affiliation, reason, success_cb, error_cb) => 869 | @room.modifyAffiliation @jid, affiliation, reason, success_cb, error_cb 870 | 871 | ban: (reason, handler_cb, error_cb) => 872 | @room.ban @jid, reason, handler_cb, error_cb 873 | 874 | member: (reason, handler_cb, error_cb) => 875 | @room.member @jid, reason, handler_cb, error_cb 876 | 877 | revoke: (reason, handler_cb, error_cb) => 878 | @room.revoke @jid, reason, handler_cb, error_cb 879 | 880 | owner: (reason, handler_cb, error_cb) => 881 | @room.owner @jid, reason, handler_cb, error_cb 882 | 883 | admin: (reason, handler_cb, error_cb) => 884 | @room.admin @jid, reason, handler_cb, error_cb 885 | 886 | update: (data) => 887 | @nick = data.nick or null 888 | @affiliation = data.affiliation or null 889 | @role = data.role or null 890 | @jid = data.jid or null 891 | @status = data.status or null 892 | @show = data.show or null 893 | @ 894 | 895 | -------------------------------------------------------------------------------- /test/buster.js: -------------------------------------------------------------------------------- 1 | var config = module.exports; 2 | 3 | config["MUC tests"] = { 4 | env: "browser", 5 | rootPath: "../../", 6 | libs: [ 7 | "test-helpers/strophe.min.js", 8 | "test-helpers/strophe.sentinel.js" 9 | ], 10 | extensions: [ 11 | require("when") 12 | ], 13 | sources: [ 14 | "muc/strophe.muc.js" 15 | ], 16 | tests: [ 17 | "muc/test/*-test.js" 18 | ], 19 | testHelpers: [ 20 | "test/helpers.js" 21 | ] 22 | }; 23 | -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | var assert = buster.assert; 2 | var refute = buster.refute; 3 | var expect = buster.expect; 4 | var when = buster.when; 5 | -------------------------------------------------------------------------------- /test/room-test.js: -------------------------------------------------------------------------------- 1 | //var buster = require('buster'); 2 | 3 | var id = 0; 4 | 5 | (function() { 6 | const gate = "https://conversejs.org/http-bind"; 7 | const user = "dima@tigase.im"; 8 | const password = "master"; 9 | 10 | function RoomClient(name) { 11 | var messages = []; 12 | var presences = []; 13 | var wrongHandler = false; 14 | 15 | this.message = when.defer(); 16 | this.presence = when.defer(); 17 | this.fail = when.defer(); 18 | 19 | this.messages = function() { return messages.slice(); } 20 | this.presences = function() { return presences.slice(); } 21 | this.wrongHandler = function() { return wrongHandler; } 22 | this.name = function() { return name; } 23 | 24 | this.onMessage = function(stanza, room) { 25 | if(room.name != name) { 26 | wrongHandler = true; 27 | this.fail.resolver.resolve(); 28 | this.fail = when.defer(); 29 | return false; 30 | } 31 | 32 | messages.push(stanza); 33 | this.message.resolver.resolve(); 34 | this.message = when.defer(); 35 | return true; 36 | }.bind(this); 37 | 38 | this.onPresence = function(stanza, room) { 39 | if(room.name != name) { 40 | wrongHandler = true; 41 | this.fail.resolver.resolve(); 42 | this.fail = when.defer(); 43 | return false; 44 | } 45 | 46 | presences.push(stanza); 47 | this.presence.resolver.resolve(); 48 | this.presence = when.defer(); 49 | return true; 50 | }.bind(this); 51 | }; 52 | 53 | buster.testCase("Check room handlers", { 54 | setUp: function() { 55 | this.timeout = 20000; 56 | 57 | this.connection = new ConnectionSentinel(); 58 | var pr = this.connection.connect(gate, user, password); 59 | this.plugin = this.connection._connection.muc; 60 | 61 | return pr; 62 | }, 63 | 64 | tearDown: function() { 65 | if(!this.connection._connected) return; 66 | return this.connection.disconnect(); 67 | }, 68 | 69 | "Rooms should have separate callbacks": function() { 70 | var sentinel = when.defer(); 71 | 72 | var rooms = [new RoomClient("room-1@muc.tigase.im"), 73 | new RoomClient("room-2@muc.tigase.im")]; 74 | 75 | var gotPresence = false; 76 | var gotMessage = false; 77 | this.plugin.join(rooms[0].name(), "dima", rooms[0].onMessage, rooms[0].onPresence); 78 | rooms[0].presence.promise.then(function() { 79 | gotPresence = true; 80 | this.plugin.groupchat(rooms[0].name(), "Hello, world!"); 81 | return true; 82 | }.bind(this)); 83 | rooms[0].message.promise.then(function() { 84 | gotMessage = true; 85 | return true; 86 | }); 87 | this.plugin.join(rooms[1].name(), "dima", rooms[1].onMessage, rooms[1].onPresence); 88 | 89 | setTimeout(function() { 90 | assert(gotPresence); 91 | assert(gotMessage); 92 | refute(rooms[0].wrongHandler()); 93 | refute(rooms[1].wrongHandler()); 94 | 95 | sentinel.resolver.resolve(); 96 | }, 2000); 97 | 98 | return sentinel.promise; 99 | }, 100 | 101 | "Callback should not be removed when we leave one room": function() { 102 | var sentinel = when.defer(); 103 | 104 | var rooms = [new RoomClient("room-6@muc.tigase.im"), 105 | new RoomClient("room-7@muc.tigase.im")]; 106 | 107 | var gotMessage = false; 108 | var messageWasSent = false; 109 | this.plugin.join(rooms[0].name(), "dima", rooms[0].onMessage, rooms[0].onPresence); 110 | rooms[0].presence.promise.then(function() { 111 | this.plugin.join(rooms[1].name(), "dima", rooms[1].onMessage, rooms[1].onPresence); 112 | rooms[1].presence.promise.then(function() { 113 | this.plugin.leave(rooms[0].name(), "dima"); 114 | setTimeout(function() { 115 | messageWasSent = true; 116 | this.plugin.groupchat(rooms[1].name(), "Hello, world!"); 117 | rooms[1].message.promise.then(function() { 118 | 119 | gotMessage = true; 120 | return true; 121 | }.bind(this)); 122 | }.bind(this), 1000); 123 | return true; 124 | }.bind(this)); 125 | return true; 126 | }.bind(this)); 127 | 128 | setTimeout(function() { 129 | assert(messageWasSent, "Message has to be sent, i.e. all the room join/leave worked"); 130 | assert(gotMessage, "Message has to be received."); 131 | sentinel.resolver.resolve(); 132 | }.bind(this), 6000); 133 | return sentinel.promise; 134 | } 135 | }); 136 | })(); 137 | --------------------------------------------------------------------------------