strophe.muc.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  1. /*
  2. *Plugin to implement the MUC extension.
  3. http://xmpp.org/extensions/xep-0045.html
  4. *Previous Author:
  5. Nathan Zorn <nathan.zorn@gmail.com>
  6. *Complete CoffeeScript rewrite:
  7. Andreas Guth <guth@dbis.rwth-aachen.de>
  8. *Cleanup, AMD/global registrations and bugfixes:
  9. JC Brand <jc@opkode.com>
  10. */
  11. // AMD/global registrations
  12. (function (root, factory) {
  13. if (typeof console === undefined || typeof console.log === undefined) {
  14. console = { log: function () {}, error: function () {} };
  15. }
  16. if (typeof define === 'function' && define.amd) {
  17. define([
  18. "strophe"
  19. ], function () {
  20. if (console===undefined || console.log===undefined) {
  21. console = { log: function () {}, error: function () {} };
  22. }
  23. return factory(jQuery, console);
  24. }
  25. );
  26. } else {
  27. return factory(jQuery, console);
  28. }
  29. }(this, function ($, console) {
  30. (function() {
  31. var Occupant, RoomConfig, XmppRoom,
  32. __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
  33. Strophe.addConnectionPlugin('muc', {
  34. _connection: null,
  35. rooms: [],
  36. init: function(conn) {
  37. /* Initialize the MUC plugin. Sets the correct connection object and
  38. * extends the namesace.
  39. */
  40. this._connection = conn;
  41. this._muc_handler = null;
  42. Strophe.addNamespace('MUC_OWNER', Strophe.NS.MUC + "#owner");
  43. Strophe.addNamespace('MUC_ADMIN', Strophe.NS.MUC + "#admin");
  44. Strophe.addNamespace('MUC_USER', Strophe.NS.MUC + "#user");
  45. return Strophe.addNamespace('MUC_ROOMCONF', Strophe.NS.MUC + "#roomconfig");
  46. },
  47. join: function(room, nick, msg_handler_cb, pres_handler_cb, roster_cb, password) {
  48. /* Join a multi-user chat room
  49. *
  50. * Parameters:
  51. * (String) room - The multi-user chat room to join.
  52. * (String) nick - Optional nickname to use in the chat room.
  53. * (Function) msg_handler_cb - The function call to handle messages from the specified chat room.
  54. * (Function) pres_handler_cb - The function call back to handle presence in the chat room.
  55. * (String) password - The optional password to use. (password protected rooms only)
  56. */
  57. var msg, room_nick, _this = this;
  58. room_nick = this.test_append_nick(room, nick);
  59. msg = $pres({
  60. from: this._connection.jid,
  61. to: room_nick
  62. }).c("x", {
  63. xmlns: Strophe.NS.MUC
  64. });
  65. if (password !== null) {
  66. msg.cnode(Strophe.xmlElement("password", [], password));
  67. }
  68. if (this._muc_handler === null) {
  69. this._muc_handler = this._connection.addHandler(function(stanza) {
  70. var from, handler, handlers, id, roomname, x, xmlns, xquery, _i, _len;
  71. from = stanza.getAttribute('from');
  72. roomname = from.split("/")[0];
  73. if (!_this.rooms[roomname]) { return true; }
  74. room = _this.rooms[roomname];
  75. handlers = {};
  76. if (stanza.nodeName === "message") {
  77. handlers = room._message_handlers;
  78. } else if (stanza.nodeName === "presence") {
  79. xquery = stanza.getElementsByTagName("x");
  80. if (xquery.length > 0) {
  81. for (_i = 0, _len = xquery.length; _i < _len; _i++) {
  82. x = xquery[_i];
  83. xmlns = x.getAttribute("xmlns");
  84. if (xmlns && xmlns.match(Strophe.NS.MUC)) {
  85. handlers = room._presence_handlers;
  86. break;
  87. }
  88. }
  89. }
  90. }
  91. _.each(handlers, function (handler, id, handlers) {
  92. if (!handler(stanza, room)) { delete handlers[id]; }
  93. });
  94. return true;
  95. });
  96. }
  97. if (!_.has(this.rooms, room)) {
  98. this.rooms[room] = new XmppRoom(this, room, nick, password);
  99. }
  100. if (pres_handler_cb) {
  101. this.rooms[room].addHandler('presence', pres_handler_cb);
  102. }
  103. if (msg_handler_cb) {
  104. this.rooms[room].addHandler('message', msg_handler_cb);
  105. }
  106. if (roster_cb) {
  107. this.rooms[room].addHandler('roster', roster_cb);
  108. }
  109. return this._connection.send(msg);
  110. },
  111. leave: function(room, nick, handler_cb, exit_msg) {
  112. /* Leave a multi-user chat room
  113. *
  114. * Parameters:
  115. * (String) room - The multi-user chat room to leave.
  116. * (String) nick - The nick name used in the room.
  117. * (Function) handler_cb - Optional function to handle the successful leave.
  118. * (String) exit_msg - optional exit message.
  119. * Returns:
  120. * iqid - The unique id for the room leave.
  121. */
  122. var presence, presenceid, room_nick;
  123. delete this.rooms[room];
  124. if (this.rooms.length === 0) {
  125. this._connection.deleteHandler(this._muc_handler);
  126. this._muc_handler = null;
  127. }
  128. room_nick = this.test_append_nick(room, nick);
  129. presenceid = this._connection.getUniqueId();
  130. presence = $pres({
  131. type: "unavailable",
  132. id: presenceid,
  133. from: this._connection.jid,
  134. to: room_nick
  135. });
  136. if (exit_msg !== null) {
  137. presence.c("status", exit_msg);
  138. }
  139. if (handler_cb !== null) {
  140. this._connection.addHandler(handler_cb, null, "presence", null, presenceid);
  141. }
  142. this._connection.send(presence);
  143. return presenceid;
  144. },
  145. message: function(room, nick, message, html_message, type) {
  146. /* Parameters:
  147. * (String) room - The multi-user chat room name.
  148. * (String) nick - The nick name used in the chat room.
  149. * (String) message - The plaintext message to send to the room.
  150. * (String) html_message - The message to send to the room with html markup.
  151. * (String) type - "groupchat" for group chat messages o
  152. * "chat" for private chat messages
  153. * Returns:
  154. * msgiq - the unique id used to send the message
  155. */
  156. var msg, msgid, parent, room_nick;
  157. room_nick = this.test_append_nick(room, nick);
  158. type = type || (nick !== null ? "chat" : "groupchat");
  159. msgid = this._connection.getUniqueId();
  160. msg = $msg({
  161. to: room_nick,
  162. from: this._connection.jid,
  163. type: type,
  164. id: msgid
  165. }).c("body", {
  166. xmlns: Strophe.NS.CLIENT
  167. }).t(message);
  168. msg.up();
  169. if (html_message !== null) {
  170. msg.c("html", {xmlns: Strophe.NS.XHTML_IM}).c("body", {xmlns: Strophe.NS.XHTML}).h(html_message);
  171. if (msg.node.childNodes.length === 0) {
  172. parent = msg.node.parentNode;
  173. msg.up().up();
  174. msg.node.removeChild(parent);
  175. } else {
  176. msg.up().up();
  177. }
  178. }
  179. msg.c("x", {
  180. xmlns: "jabber:x:event"
  181. }).c("composing");
  182. this._connection.send(msg);
  183. return msgid;
  184. },
  185. groupchat: function(room, message, html_message) {
  186. /* Convenience Function to send a Message to all Occupants
  187. * Parameters:
  188. * (String) room - The multi-user chat room name.
  189. * (String) message - The plaintext message to send to the room.
  190. * (String) html_message - The message to send to the room with html markup.
  191. * Returns:
  192. * msgiq - the unique id used to send the message
  193. */
  194. return this.message(room, null, message, html_message);
  195. },
  196. invite: function(room, receiver, reason) {
  197. /* Send a mediated invitation.
  198. * Parameters:
  199. * (String) room - The multi-user chat room name.
  200. * (String) receiver - The invitation's receiver.
  201. * (String) reason - Optional reason for joining the room.
  202. * Returns:
  203. * msgiq - the unique id used to send the invitation
  204. */
  205. var invitation, msgid;
  206. msgid = this._connection.getUniqueId();
  207. invitation = $msg({
  208. from: this._connection.jid,
  209. to: room,
  210. id: msgid
  211. }).c('x', {
  212. xmlns: Strophe.NS.MUC_USER
  213. }).c('invite', {
  214. to: receiver
  215. });
  216. if (reason !== null) {
  217. invitation.c('reason', reason);
  218. }
  219. this._connection.send(invitation);
  220. return msgid;
  221. },
  222. directInvite: function(room, receiver, reason, password) {
  223. /* Send a direct invitation.
  224. * Parameters:
  225. * (String) room - The multi-user chat room name.
  226. * (String) receiver - The invitation's receiver.
  227. * (String) reason - Optional reason for joining the room.
  228. * (String) password - Optional password for the room.
  229. * Returns:
  230. * msgiq - the unique id used to send the invitation
  231. */
  232. var attrs, invitation, msgid;
  233. msgid = this._connection.getUniqueId();
  234. attrs = {
  235. xmlns: 'jabber:x:conference',
  236. jid: room
  237. };
  238. if (reason !== null) { attrs.reason = reason; }
  239. if (password !== null) { attrs.password = password; }
  240. invitation = $msg({
  241. from: this._connection.jid,
  242. to: receiver,
  243. id: msgid
  244. }).c('x', attrs);
  245. this._connection.send(invitation);
  246. return msgid;
  247. },
  248. queryOccupants: function(room, success_cb, error_cb) {
  249. /* Queries a room for a list of occupants
  250. * (String) room - The multi-user chat room name.
  251. * (Function) success_cb - Optional function to handle the info.
  252. * (Function) error_cb - Optional function to handle an error.
  253. * Returns:
  254. * id - the unique id used to send the info request
  255. */
  256. var attrs, info;
  257. attrs = {
  258. xmlns: Strophe.NS.DISCO_ITEMS
  259. };
  260. info = $iq({
  261. from: this._connection.jid,
  262. to: room,
  263. type: 'get'
  264. }).c('query', attrs);
  265. return this._connection.sendIQ(info, success_cb, error_cb);
  266. },
  267. configure: function(room, handler_cb) {
  268. /* Start a room configuration.
  269. * Parameters:
  270. * (String) room - The multi-user chat room name.
  271. * (Function) handler_cb - Optional function to handle the config form.
  272. * Returns:
  273. * id - the unique id used to send the configuration request
  274. */
  275. var config, id, stanza;
  276. config = $iq({
  277. to: room,
  278. type: "get"
  279. }).c("query", {
  280. xmlns: Strophe.NS.MUC_OWNER
  281. });
  282. stanza = config.tree();
  283. id = this._connection.sendIQ(stanza);
  284. if (handler_cb !== null) {
  285. this._connection.addHandler(function(stanza) { handler_cb(stanza);
  286. return false;
  287. }, Strophe.NS.MUC_OWNER, "iq", null, id);
  288. }
  289. return id;
  290. },
  291. cancelConfigure: function(room) {
  292. /* Cancel the room configuration
  293. * Parameters:
  294. * (String) room - The multi-user chat room name.
  295. * Returns:
  296. * id - the unique id used to cancel the configuration.
  297. */
  298. var config, stanza;
  299. config = $iq({
  300. to: room,
  301. type: "set"
  302. }).c("query", {
  303. xmlns: Strophe.NS.MUC_OWNER
  304. }).c("x", {
  305. xmlns: "jabber:x:data",
  306. type: "cancel"
  307. });
  308. stanza = config.tree();
  309. return this._connection.sendIQ(stanza);
  310. },
  311. saveConfiguration: function(room, configarray) {
  312. /* Save a room configuration.
  313. * Parameters:
  314. * (String) room - The multi-user chat room name.
  315. * (Array) configarray - an array of form elements used to configure the room.
  316. * Returns:
  317. * id - the unique id used to save the configuration.
  318. */
  319. var conf, config, stanza, _i, _len;
  320. config = $iq({
  321. to: room,
  322. type: "set"
  323. }).c("query", {
  324. xmlns: Strophe.NS.MUC_OWNER
  325. }).c("x", {
  326. xmlns: "jabber:x:data",
  327. type: "submit"
  328. });
  329. for (_i = 0, _len = configarray.length; _i < _len; _i++) {
  330. conf = configarray[_i];
  331. config.cnode(conf).up();
  332. }
  333. stanza = config.tree();
  334. return this._connection.sendIQ(stanza);
  335. },
  336. createInstantRoom: function(room) {
  337. /* Parameters:
  338. * (String) room - The multi-user chat room name.
  339. * Returns:
  340. * id - the unique id used to create the chat room.
  341. */
  342. var roomiq;
  343. roomiq = $iq({
  344. to: room,
  345. type: "set"
  346. }).c("query", {
  347. xmlns: Strophe.NS.MUC_OWNER
  348. }).c("x", {
  349. xmlns: "jabber:x:data",
  350. type: "submit"
  351. });
  352. return this._connection.sendIQ(roomiq.tree());
  353. },
  354. setTopic: function(room, topic) {
  355. /* Set the topic of the chat room.
  356. * Parameters:
  357. * (String) room - The multi-user chat room name.
  358. * (String) topic - Topic message.
  359. */
  360. var msg;
  361. msg = $msg({
  362. to: room,
  363. from: this._connection.jid,
  364. type: "groupchat"
  365. }).c("subject", {
  366. xmlns: "jabber:client"
  367. }).t(topic);
  368. return this._connection.send(msg.tree());
  369. },
  370. _modifyPrivilege: function(room, item, reason, handler_cb, error_cb) {
  371. /* Internal Function that Changes the role or affiliation of a member
  372. * of a MUC room. This function is used by modifyRole and modifyAffiliation.
  373. * The modification can only be done by a room moderator. An error will be
  374. * returned if the user doesn't have permission.
  375. * Parameters:
  376. * (String) room - The multi-user chat room name.
  377. * (Object) item - Object with nick and role or jid and affiliation attribute
  378. * (String) reason - Optional reason for the change.
  379. * (Function) handler_cb - Optional callback for success
  380. * (Function) errer_cb - Optional callback for error
  381. * Returns:
  382. * iq - the id of the mode change request.
  383. */
  384. var iq;
  385. iq = $iq({
  386. to: room,
  387. type: "set"
  388. }).c("query", {
  389. xmlns: Strophe.NS.MUC_ADMIN
  390. }).cnode(item.node);
  391. if (reason !== null) { iq.c("reason", reason); }
  392. return this._connection.sendIQ(iq.tree(), handler_cb, error_cb);
  393. },
  394. modifyRole: function(room, nick, role, reason, handler_cb, error_cb) {
  395. /* Changes the role of a member of a MUC room.
  396. * The modification can only be done by a room moderator. An error will be
  397. * returned if the user doesn't have permission.
  398. * Parameters:
  399. * (String) room - The multi-user chat room name.
  400. * (String) nick - The nick name of the user to modify.
  401. * (String) role - The new role of the user.
  402. * (String) affiliation - The new affiliation of the user.
  403. * (String) reason - Optional reason for the change.
  404. * (Function) handler_cb - Optional callback for success
  405. * (Function) errer_cb - Optional callback for error
  406. * Returns:
  407. * iq - the id of the mode change request.
  408. */
  409. var item;
  410. item = $build("item", {
  411. nick: nick,
  412. role: role
  413. });
  414. return this._modifyPrivilege(room, item, reason, handler_cb, error_cb);
  415. },
  416. kick: function(room, nick, reason, handler_cb, error_cb) {
  417. return this.modifyRole(room, nick, 'none', reason, handler_cb, error_cb);
  418. },
  419. voice: function(room, nick, reason, handler_cb, error_cb) {
  420. return this.modifyRole(room, nick, 'participant', reason, handler_cb, error_cb);
  421. },
  422. mute: function(room, nick, reason, handler_cb, error_cb) {
  423. return this.modifyRole(room, nick, 'visitor', reason, handler_cb, error_cb);
  424. },
  425. op: function(room, nick, reason, handler_cb, error_cb) {
  426. return this.modifyRole(room, nick, 'moderator', reason, handler_cb, error_cb);
  427. },
  428. deop: function(room, nick, reason, handler_cb, error_cb) {
  429. return this.modifyRole(room, nick, 'participant', reason, handler_cb, error_cb);
  430. },
  431. modifyAffiliation: function(room, jid, affiliation, reason, handler_cb, error_cb) {
  432. /* Changes the affiliation of a member of a MUC room.
  433. * The modification can only be done by a room moderator. An error will be
  434. * returned if the user doesn't have permission.
  435. * Parameters:
  436. * (String) room - The multi-user chat room name.
  437. * (String) jid - The jid of the user to modify.
  438. * (String) affiliation - The new affiliation of the user.
  439. * (String) reason - Optional reason for the change.
  440. * (Function) handler_cb - Optional callback for success
  441. * (Function) errer_cb - Optional callback for error
  442. * Returns:
  443. * iq - the id of the mode change request.
  444. */
  445. var item;
  446. item = $build("item", {
  447. jid: jid,
  448. affiliation: affiliation
  449. });
  450. return this._modifyPrivilege(room, item, reason, handler_cb, error_cb);
  451. },
  452. ban: function(room, jid, reason, handler_cb, error_cb) {
  453. return this.modifyAffiliation(room, jid, 'outcast', reason, handler_cb, error_cb);
  454. },
  455. member: function(room, jid, reason, handler_cb, error_cb) {
  456. return this.modifyAffiliation(room, jid, 'member', reason, handler_cb, error_cb);
  457. },
  458. revoke: function(room, jid, reason, handler_cb, error_cb) {
  459. return this.modifyAffiliation(room, jid, 'none', reason, handler_cb, error_cb);
  460. },
  461. owner: function(room, jid, reason, handler_cb, error_cb) {
  462. return this.modifyAffiliation(room, jid, 'owner', reason, handler_cb, error_cb);
  463. },
  464. admin: function(room, jid, reason, handler_cb, error_cb) {
  465. return this.modifyAffiliation(room, jid, 'admin', reason, handler_cb, error_cb);
  466. },
  467. changeNick: function(room, user) {
  468. /* Change the current users nick name.
  469. * Parameters:
  470. * (String) room - The multi-user chat room name.
  471. * (String) user - The new nick name.
  472. */
  473. var presence, room_nick;
  474. room_nick = this.test_append_nick(room, user);
  475. presence = $pres({
  476. from: this._connection.jid,
  477. to: room_nick,
  478. id: this._connection.getUniqueId()
  479. });
  480. return this._connection.send(presence.tree());
  481. },
  482. setStatus: function(room, user, show, status) {
  483. /* Change the current users status.
  484. * Parameters:
  485. * (String) room - The multi-user chat room name.
  486. * (String) user - The current nick.
  487. * (String) show - The new show-text.
  488. * (String) status - The new status-text.
  489. */
  490. var presence, room_nick;
  491. room_nick = this.test_append_nick(room, user);
  492. presence = $pres({
  493. from: this._connection.jid,
  494. to: room_nick
  495. });
  496. if (show !== null) { presence.c('show', show).up(); }
  497. if (status !== null) { presence.c('status', status); }
  498. return this._connection.send(presence.tree());
  499. },
  500. listRooms: function(server, handle_cb) {
  501. /* List all chat room available on a server.
  502. * Parameters:
  503. * (String) server - name of chat server.
  504. * (String) handle_cb - Function to call for room list return.
  505. */
  506. var iq;
  507. iq = $iq({
  508. to: server,
  509. from: this._connection.jid,
  510. type: "get"
  511. }).c("query", {
  512. xmlns: Strophe.NS.DISCO_ITEMS
  513. });
  514. return this._connection.sendIQ(iq, handle_cb);
  515. },
  516. test_append_nick: function(room, nick) {
  517. return room + (nick !== null ? "/" + (Strophe.escapeNode(nick)) : "");
  518. }
  519. });
  520. XmppRoom = (function() {
  521. function XmppRoom(client, name, nick, password) {
  522. this.roster = {};
  523. this._message_handlers = {};
  524. this._presence_handlers = {};
  525. this._roster_handlers = {};
  526. this._handler_ids = 0;
  527. this.client = client;
  528. this.name = name;
  529. this.nick = nick;
  530. this.password = password;
  531. this._roomRosterHandler = __bind(this._roomRosterHandler, this);
  532. this._addOccupant = __bind(this._addOccupant, this);
  533. if (client.muc) { this.client = client.muc; }
  534. this.name = Strophe.getBareJidFromJid(name);
  535. this.client.rooms[this.name] = this;
  536. this.addHandler('presence', this._roomRosterHandler);
  537. }
  538. XmppRoom.prototype.join = function(msg_handler_cb, pres_handler_cb) {
  539. if (!this.client.rooms[this.name]) {
  540. return this.client.join(this.name, this.nick, msg_handler_cb, pres_handler_cb, this.password);
  541. }
  542. };
  543. XmppRoom.prototype.leave = function(handler_cb, message) {
  544. this.client.leave(this.name, this.nick, handler_cb, message);
  545. return delete this.client.rooms[this.name];
  546. };
  547. XmppRoom.prototype.message = function(nick, message, html_message, type) {
  548. return this.client.message(this.name, nick, message, html_message, type);
  549. };
  550. XmppRoom.prototype.groupchat = function(message, html_message) {
  551. return this.client.groupchat(this.name, message, html_message);
  552. };
  553. XmppRoom.prototype.invite = function(receiver, reason) {
  554. return this.client.invite(this.name, receiver, reason);
  555. };
  556. XmppRoom.prototype.directInvite = function(receiver, reason) {
  557. return this.client.directInvite(this.name, receiver, reason, this.password);
  558. };
  559. XmppRoom.prototype.configure = function(handler_cb) {
  560. return this.client.configure(this.name, handler_cb);
  561. };
  562. XmppRoom.prototype.cancelConfigure = function() {
  563. return this.client.cancelConfigure(this.name);
  564. };
  565. XmppRoom.prototype.saveConfiguration = function(configarray) {
  566. return this.client.saveConfiguration(this.name, configarray);
  567. };
  568. XmppRoom.prototype.queryOccupants = function(success_cb, error_cb) {
  569. return this.client.queryOccupants(this.name, success_cb, error_cb);
  570. };
  571. XmppRoom.prototype.setTopic = function(topic) {
  572. return this.client.setTopic(this.name, topic);
  573. };
  574. XmppRoom.prototype.modifyRole = function(nick, role, reason, success_cb, error_cb) {
  575. return this.client.modifyRole(this.name, nick, role, reason, success_cb, error_cb);
  576. };
  577. XmppRoom.prototype.kick = function(nick, reason, handler_cb, error_cb) {
  578. return this.client.kick(this.name, nick, reason, handler_cb, error_cb);
  579. };
  580. XmppRoom.prototype.voice = function(nick, reason, handler_cb, error_cb) {
  581. return this.client.voice(this.name, nick, reason, handler_cb, error_cb);
  582. };
  583. XmppRoom.prototype.mute = function(nick, reason, handler_cb, error_cb) {
  584. return this.client.mute(this.name, nick, reason, handler_cb, error_cb);
  585. };
  586. XmppRoom.prototype.op = function(nick, reason, handler_cb, error_cb) {
  587. return this.client.op(this.name, nick, reason, handler_cb, error_cb);
  588. };
  589. XmppRoom.prototype.deop = function(nick, reason, handler_cb, error_cb) {
  590. return this.client.deop(this.name, nick, reason, handler_cb, error_cb);
  591. };
  592. XmppRoom.prototype.modifyAffiliation = function(jid, affiliation, reason, success_cb, error_cb) {
  593. return this.client.modifyAffiliation(this.name, jid, affiliation, reason, success_cb, error_cb);
  594. };
  595. XmppRoom.prototype.ban = function(jid, reason, handler_cb, error_cb) {
  596. return this.client.ban(this.name, jid, reason, handler_cb, error_cb);
  597. };
  598. XmppRoom.prototype.member = function(jid, reason, handler_cb, error_cb) {
  599. return this.client.member(this.name, jid, reason, handler_cb, error_cb);
  600. };
  601. XmppRoom.prototype.revoke = function(jid, reason, handler_cb, error_cb) {
  602. return this.client.revoke(this.name, jid, reason, handler_cb, error_cb);
  603. };
  604. XmppRoom.prototype.owner = function(jid, reason, handler_cb, error_cb) {
  605. return this.client.owner(this.name, jid, reason, handler_cb, error_cb);
  606. };
  607. XmppRoom.prototype.admin = function(jid, reason, handler_cb, error_cb) {
  608. return this.client.admin(this.name, jid, reason, handler_cb, error_cb);
  609. };
  610. XmppRoom.prototype.changeNick = function(nick) {
  611. this.nick = nick;
  612. return this.client.changeNick(this.name, nick);
  613. };
  614. XmppRoom.prototype.setStatus = function(show, status) {
  615. return this.client.setStatus(this.name, this.nick, show, status);
  616. };
  617. XmppRoom.prototype.addHandler = function(handler_type, handler) {
  618. /* Adds a handler to the MUC room.
  619. * Parameters:
  620. * (String) handler_type - 'message', 'presence' or 'roster'.
  621. * (Function) handler - The handler function.
  622. * Returns:
  623. * id - the id of handler.
  624. */
  625. var id;
  626. id = this._handler_ids++;
  627. switch (handler_type) {
  628. case 'presence':
  629. this._presence_handlers[id] = handler;
  630. break;
  631. case 'message':
  632. this._message_handlers[id] = handler;
  633. break;
  634. case 'roster':
  635. this._roster_handlers[id] = handler;
  636. break;
  637. default:
  638. this._handler_ids--;
  639. return null;
  640. }
  641. return id;
  642. };
  643. XmppRoom.prototype.removeHandler = function(id) {
  644. /* Removes a handler from the MUC room.
  645. * This function takes ONLY ids returned by the addHandler function
  646. * of this room. passing handler ids returned by connection.addHandler
  647. * may brake things!
  648. * Parameters:
  649. * (number) id - the id of the handler
  650. */
  651. delete this._presence_handlers[id];
  652. delete this._message_handlers[id];
  653. return delete this._roster_handlers[id];
  654. };
  655. XmppRoom.prototype._addOccupant = function(data) {
  656. /* Creates and adds an Occupant to the Room Roster.
  657. * Parameters:
  658. * (Object) data - the data the Occupant is filled with
  659. * Returns:
  660. * occ - the created Occupant.
  661. */
  662. var occ;
  663. occ = new Occupant(data, this);
  664. this.roster[occ.nick] = occ;
  665. return occ;
  666. };
  667. XmppRoom.prototype._roomRosterHandler = function(pres) {
  668. /* The standard handler that managed the Room Roster.
  669. * Parameters:
  670. * (Object) pres - the presence stanza containing user information
  671. */
  672. var data, handler, id, newnick, nick, _ref;
  673. data = XmppRoom._parsePresence(pres);
  674. nick = data.nick;
  675. newnick = data.newnick || null;
  676. switch (data.type) {
  677. case 'error':
  678. return;
  679. case 'unavailable':
  680. if (newnick) {
  681. data.nick = newnick;
  682. if (this.roster[nick] && this.roster[newnick]) {
  683. this.roster[nick].update(this.roster[newnick]);
  684. this.roster[newnick] = this.roster[nick];
  685. }
  686. if (this.roster[nick] && !this.roster[newnick]) {
  687. this.roster[newnick] = this.roster[nick].update(data);
  688. }
  689. }
  690. delete this.roster[nick];
  691. break;
  692. default:
  693. if (this.roster[nick]) {
  694. this.roster[nick].update(data);
  695. } else {
  696. this._addOccupant(data);
  697. }
  698. }
  699. _ref = this._roster_handlers;
  700. for (id in _ref) {
  701. handler = _ref[id];
  702. if (!handler(this.roster, this)) { delete this._roster_handlers[id]; }
  703. }
  704. return true;
  705. };
  706. XmppRoom._parsePresence = function(pres) {
  707. /* Parses a presence stanza into a map
  708. * Parameters:
  709. * (Object) pres - the presence stanza
  710. * Returns:
  711. * (Object) data - the data extracted from the presence stanza
  712. */
  713. var $pres, data, i, j, children, item;
  714. $pres = $(pres);
  715. data = {};
  716. data.nick = Strophe.getResourceFromJid($pres.attr('from'));
  717. data.type = $pres.attr('type');
  718. data.states = [];
  719. for (i=0; i < $pres.children().length; i++) {
  720. child = $pres.children()[0];
  721. switch (child.nodeName) {
  722. case 'status':
  723. data.status = child.textContent || null;
  724. break;
  725. case 'show':
  726. data.show = child.textContent || null;
  727. break;
  728. case 'x':
  729. if ($(child).attr('xmlns') === Strophe.NS.MUC_USER) {
  730. children = $(child).children();
  731. for (j=0; j < children.length; j++) {
  732. item = children[0];
  733. switch (item.nodeName) {
  734. case "item":
  735. a = item.attributes;
  736. data.affiliation = $(item).attr('affiliation') || null;
  737. data.role = $(item).attr('role') || null;
  738. data.jid = $(item).attr('jid') || null;
  739. data.newnick = $(item).attr('nick') || null;
  740. break;
  741. case "status":
  742. if ($(item).attr('code')) {
  743. data.states.push($(item).attr('code'));
  744. }
  745. break;
  746. }
  747. }
  748. }
  749. break;
  750. }
  751. }
  752. return data;
  753. };
  754. return XmppRoom;
  755. })();
  756. RoomConfig = (function() {
  757. function RoomConfig(info) {
  758. this.parse = __bind(this.parse, this);
  759. if (info !== null) { this.parse(info); }
  760. }
  761. RoomConfig.prototype.parse = function(result) {
  762. var attr, attrs, child, field, identity, query, _i, _j, _k, _len, _len2, _len3, _ref;
  763. query = result.getElementsByTagName("query")[0].childNodes;
  764. this.identities = [];
  765. this.features = [];
  766. this.x = [];
  767. for (_i = 0, _len = query.length; _i < _len; _i++) {
  768. child = query[_i];
  769. attrs = child.attributes;
  770. switch (child.nodeName) {
  771. case "identity":
  772. identity = {};
  773. for (_j = 0, _len2 = attrs.length; _j < _len2; _j++) {
  774. attr = attrs[_j];
  775. identity[attr.name] = attr.textContent;
  776. }
  777. this.identities.push(identity);
  778. break;
  779. case "feature":
  780. this.features.push(attrs["var"].textContent);
  781. break;
  782. case "x":
  783. attrs = child.childNodes[0].attributes;
  784. if ((!attrs["var"].textContent === 'FORM_TYPE') || (!attrs.type.textContent === 'hidden')) {
  785. break;
  786. }
  787. _ref = child.childNodes;
  788. for (_k = 0, _len3 = _ref.length; _k < _len3; _k++) {
  789. field = _ref[_k];
  790. if (!(!field.attributes.type)) continue;
  791. attrs = field.attributes;
  792. this.x.push({
  793. "var": attrs["var"].textContent,
  794. label: attrs.label.textContent || "",
  795. value: field.firstChild.textContent || ""
  796. });
  797. }
  798. }
  799. }
  800. return {
  801. "identities": this.identities,
  802. "features": this.features,
  803. "x": this.x
  804. };
  805. };
  806. return RoomConfig;
  807. })();
  808. Occupant = (function() {
  809. function Occupant(data, room) {
  810. this.room = room;
  811. this.update = __bind(this.update, this);
  812. this.admin = __bind(this.admin, this);
  813. this.owner = __bind(this.owner, this);
  814. this.revoke = __bind(this.revoke, this);
  815. this.member = __bind(this.member, this);
  816. this.ban = __bind(this.ban, this);
  817. this.modifyAffiliation = __bind(this.modifyAffiliation, this);
  818. this.deop = __bind(this.deop, this);
  819. this.op = __bind(this.op, this);
  820. this.mute = __bind(this.mute, this);
  821. this.voice = __bind(this.voice, this);
  822. this.kick = __bind(this.kick, this);
  823. this.modifyRole = __bind(this.modifyRole, this);
  824. this.update(data);
  825. }
  826. Occupant.prototype.modifyRole = function(role, reason, success_cb, error_cb) {
  827. return this.room.modifyRole(this.nick, role, reason, success_cb, error_cb);
  828. };
  829. Occupant.prototype.kick = function(reason, handler_cb, error_cb) {
  830. return this.room.kick(this.nick, reason, handler_cb, error_cb);
  831. };
  832. Occupant.prototype.voice = function(reason, handler_cb, error_cb) {
  833. return this.room.voice(this.nick, reason, handler_cb, error_cb);
  834. };
  835. Occupant.prototype.mute = function(reason, handler_cb, error_cb) {
  836. return this.room.mute(this.nick, reason, handler_cb, error_cb);
  837. };
  838. Occupant.prototype.op = function(reason, handler_cb, error_cb) {
  839. return this.room.op(this.nick, reason, handler_cb, error_cb);
  840. };
  841. Occupant.prototype.deop = function(reason, handler_cb, error_cb) {
  842. return this.room.deop(this.nick, reason, handler_cb, error_cb);
  843. };
  844. Occupant.prototype.modifyAffiliation = function(affiliation, reason, success_cb, error_cb) {
  845. return this.room.modifyAffiliation(this.jid, affiliation, reason, success_cb, error_cb);
  846. };
  847. Occupant.prototype.ban = function(reason, handler_cb, error_cb) {
  848. return this.room.ban(this.jid, reason, handler_cb, error_cb);
  849. };
  850. Occupant.prototype.member = function(reason, handler_cb, error_cb) {
  851. return this.room.member(this.jid, reason, handler_cb, error_cb);
  852. };
  853. Occupant.prototype.revoke = function(reason, handler_cb, error_cb) {
  854. return this.room.revoke(this.jid, reason, handler_cb, error_cb);
  855. };
  856. Occupant.prototype.owner = function(reason, handler_cb, error_cb) {
  857. return this.room.owner(this.jid, reason, handler_cb, error_cb);
  858. };
  859. Occupant.prototype.admin = function(reason, handler_cb, error_cb) {
  860. return this.room.admin(this.jid, reason, handler_cb, error_cb);
  861. };
  862. Occupant.prototype.update = function(data) {
  863. this.nick = data.nick || null;
  864. this.affiliation = data.affiliation || null;
  865. this.role = data.role || null;
  866. this.jid = data.jid || null;
  867. this.status = data.status || null;
  868. this.show = data.show || null;
  869. return this;
  870. };
  871. return Occupant;
  872. })();
  873. }).call(this);
  874. }));