converse-controlbox.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. // Converse.js (A browser based XMPP chat client)
  2. // http://conversejs.org
  3. //
  4. // Copyright (c) 2012-2016, Jan-Carel Brand <jc@opkode.com>
  5. // Licensed under the Mozilla Public License (MPLv2)
  6. //
  7. /*global define, Backbone */
  8. (function (root, factory) {
  9. define("converse-controlbox", [
  10. "converse-core",
  11. "converse-api",
  12. // TODO: remove the next two dependencies
  13. "converse-rosterview",
  14. "converse-chatview"
  15. ], factory);
  16. }(this, function (converse, converse_api) {
  17. "use strict";
  18. // Strophe methods for building stanzas
  19. var Strophe = converse_api.env.Strophe,
  20. b64_sha1 = converse_api.env.b64_sha1,
  21. utils = converse_api.env.utils;
  22. // Other necessary globals
  23. var $ = converse_api.env.jQuery,
  24. _ = converse_api.env._,
  25. __ = utils.__.bind(converse),
  26. moment = converse_api.env.moment;
  27. converse_api.plugins.add('controlbox', {
  28. overrides: {
  29. // Overrides mentioned here will be picked up by converse.js's
  30. // plugin architecture they will replace existing methods on the
  31. // relevant objects or classes.
  32. //
  33. // New functions which don't exist yet can also be added.
  34. initSession: function () {
  35. this.controlboxtoggle = new this.ControlBoxToggle();
  36. this._super.initSession.apply(this, arguments);
  37. },
  38. initConnection: function () {
  39. this._super.initConnection.apply(this, arguments);
  40. if (this.connection) {
  41. this.addControlBox();
  42. }
  43. },
  44. onDisconnected: function () {
  45. var result = this._super.onDisconnected.apply(this, arguments);
  46. if (result === 'disconnected') {
  47. converse._tearDown();
  48. var view = converse.chatboxviews.get('controlbox');
  49. view.model.set({connected:false});
  50. view.$('#controlbox-tabs').empty();
  51. view.renderLoginPanel();
  52. }
  53. return result;
  54. },
  55. _tearDown: function () {
  56. this._super._tearDown.apply(this, arguments);
  57. if (this.rosterview) {
  58. this.rosterview.unregisterHandlers();
  59. // Removes roster groups
  60. this.rosterview.model.off().reset();
  61. this.rosterview.undelegateEvents().remove();
  62. }
  63. },
  64. clearSession: function () {
  65. this._super.clearSession.apply(this, arguments);
  66. if (typeof this.connection !== 'undefined' && this.connection.connected) {
  67. this.chatboxes.get('controlbox').save({'connected': false});
  68. }
  69. },
  70. ChatBoxes: {
  71. chatBoxMayBeShown: function (chatbox) {
  72. return this._super.chatBoxMayBeShown.apply(this, arguments) &&
  73. chatbox.get('id') !== 'controlbox';
  74. },
  75. onChatBoxesFetched: function (collection, resp) {
  76. this._super.onChatBoxesFetched.apply(this, arguments);
  77. if (!_.include(_.pluck(resp, 'id'), 'controlbox')) {
  78. this.add({
  79. id: 'controlbox',
  80. box_id: 'controlbox'
  81. });
  82. }
  83. this.get('controlbox').save({connected:true});
  84. },
  85. },
  86. ChatBoxViews: {
  87. onChatBoxAdded: function (item) {
  88. if (item.get('box_id') === 'controlbox') {
  89. var view = this.get(item.get('id'));
  90. if (view) {
  91. view.model = item;
  92. view.initialize();
  93. return view;
  94. } else {
  95. view = new converse.ControlBoxView({model: item});
  96. return this.add(item.get('id'), view);
  97. }
  98. } else {
  99. return this._super.onChatBoxAdded.apply(this, arguments);
  100. }
  101. },
  102. closeAllChatBoxes: function () {
  103. this.each(function (view) {
  104. if (view.model.get('id') !== 'controlbox') {
  105. view.close();
  106. }
  107. });
  108. return this;
  109. },
  110. getChatBoxWidth: function (view) {
  111. var controlbox = this.get('controlbox');
  112. if (view.model.get('id') === 'controlbox') {
  113. /* We return the width of the controlbox or its toggle,
  114. * depending on which is visible.
  115. */
  116. if (!controlbox || !controlbox.$el.is(':visible')) {
  117. return converse.controlboxtoggle.$el.outerWidth(true);
  118. } else {
  119. return controlbox.$el.outerWidth(true);
  120. }
  121. } else {
  122. return this._super.getChatBoxWidth.apply(this, arguments);
  123. }
  124. }
  125. },
  126. ChatBox: {
  127. initialize: function () {
  128. if (this.get('id') === 'controlbox') {
  129. this.set({
  130. 'time_opened': moment(0).valueOf(),
  131. 'num_unread': 0
  132. });
  133. } else {
  134. this._super.initialize.apply(this, arguments);
  135. }
  136. },
  137. },
  138. ChatBoxView: {
  139. insertIntoPage: function () {
  140. this.$el.insertAfter(converse.chatboxviews.get("controlbox").$el);
  141. return this;
  142. }
  143. }
  144. },
  145. initialize: function () {
  146. /* The initialize function gets called as soon as the plugin is
  147. * loaded by converse.js's plugin machinery.
  148. */
  149. var converse = this.converse;
  150. this.updateSettings({
  151. allow_logout: true,
  152. default_domain: undefined,
  153. show_controlbox_by_default: false,
  154. sticky_controlbox: false,
  155. xhr_user_search: false,
  156. xhr_user_search_url: ''
  157. });
  158. var LABEL_CONTACTS = __('Contacts');
  159. converse.addControlBox = function () {
  160. return converse.chatboxes.add({
  161. id: 'controlbox',
  162. box_id: 'controlbox',
  163. closed: !converse.show_controlbox_by_default
  164. });
  165. };
  166. converse.ControlBoxView = converse.ChatBoxView.extend({
  167. tagName: 'div',
  168. className: 'chatbox',
  169. id: 'controlbox',
  170. events: {
  171. 'click a.close-chatbox-button': 'close',
  172. 'click ul#controlbox-tabs li a': 'switchTab',
  173. },
  174. initialize: function () {
  175. this.$el.insertAfter(converse.controlboxtoggle.$el);
  176. this.model.on('change:connected', this.onConnected, this);
  177. this.model.on('destroy', this.hide, this);
  178. this.model.on('hide', this.hide, this);
  179. this.model.on('show', this.show, this);
  180. this.model.on('change:closed', this.ensureClosedState, this);
  181. this.render();
  182. if (this.model.get('connected')) {
  183. this.initRoster();
  184. }
  185. if (typeof this.model.get('closed')==='undefined') {
  186. this.model.set('closed', !converse.show_controlbox_by_default);
  187. }
  188. if (!this.model.get('closed')) {
  189. this.show();
  190. } else {
  191. this.hide();
  192. }
  193. },
  194. render: function () {
  195. this.$el.html(converse.templates.controlbox(
  196. _.extend(this.model.toJSON(), {
  197. sticky_controlbox: converse.sticky_controlbox
  198. }))
  199. );
  200. if (!converse.connection.connected || !converse.connection.authenticated || converse.connection.disconnecting) {
  201. this.renderLoginPanel();
  202. } else if (!this.contactspanel || !this.contactspanel.$el.is(':visible')) {
  203. this.renderContactsPanel();
  204. }
  205. return this;
  206. },
  207. giveFeedback: function (message, klass) {
  208. var $el = this.$('.conn-feedback');
  209. $el.addClass('conn-feedback').text(message);
  210. if (klass) {
  211. $el.addClass(klass);
  212. }
  213. },
  214. onConnected: function () {
  215. if (this.model.get('connected')) {
  216. this.render().initRoster();
  217. }
  218. },
  219. initRoster: function () {
  220. /* We initialize the roster, which will appear inside the
  221. * Contacts Panel.
  222. */
  223. var rostergroups = new converse.RosterGroups();
  224. rostergroups.browserStorage = new Backbone.BrowserStorage[converse.storage](
  225. b64_sha1('converse.roster.groups'+converse.bare_jid));
  226. converse.rosterview = new converse.RosterView({model: rostergroups});
  227. this.contactspanel.$el.append(converse.rosterview.$el);
  228. converse.rosterview.render().fetch().update();
  229. return this;
  230. },
  231. renderLoginPanel: function () {
  232. var $feedback = this.$('.conn-feedback'); // we want to still show any existing feedback.
  233. this.loginpanel = new converse.LoginPanel({
  234. '$parent': this.$el.find('.controlbox-panes'),
  235. 'model': this
  236. });
  237. this.loginpanel.render();
  238. if ($feedback.length && $feedback.text() !== __('Connecting')) {
  239. this.$('.conn-feedback').replaceWith($feedback);
  240. }
  241. return this;
  242. },
  243. renderContactsPanel: function () {
  244. this.contactspanel = new converse.ContactsPanel({
  245. '$parent': this.$el.find('.controlbox-panes')
  246. });
  247. this.contactspanel.render();
  248. converse.xmppstatusview = new converse.XMPPStatusView({
  249. 'model': converse.xmppstatus
  250. });
  251. converse.xmppstatusview.render();
  252. },
  253. close: function (ev) {
  254. if (ev && ev.preventDefault) { ev.preventDefault(); }
  255. if (converse.connection.connected) {
  256. this.model.save({'closed': true});
  257. } else {
  258. this.model.trigger('hide');
  259. }
  260. converse.emit('controlBoxClosed', this);
  261. return this;
  262. },
  263. ensureClosedState: function () {
  264. if (this.model.get('closed')) {
  265. this.hide();
  266. } else {
  267. this.show();
  268. }
  269. },
  270. hide: function (callback) {
  271. this.$el.hide('fast', function () {
  272. utils.refreshWebkit();
  273. converse.emit('chatBoxClosed', this);
  274. converse.controlboxtoggle.show(function () {
  275. if (typeof callback === "function") {
  276. callback();
  277. }
  278. });
  279. });
  280. return this;
  281. },
  282. onControlBoxToggleHidden: function () {
  283. this.$el.show('fast', function () {
  284. converse.controlboxtoggle.updateOnlineCount();
  285. utils.refreshWebkit();
  286. converse.emit('controlBoxOpened', this);
  287. }.bind(this));
  288. },
  289. show: function () {
  290. converse.controlboxtoggle.hide(
  291. this.onControlBoxToggleHidden.bind(this)
  292. );
  293. return this;
  294. },
  295. switchTab: function (ev) {
  296. // TODO: automatically focus the relevant input
  297. if (ev && ev.preventDefault) { ev.preventDefault(); }
  298. var $tab = $(ev.target),
  299. $sibling = $tab.parent().siblings('li').children('a'),
  300. $tab_panel = $($tab.attr('href'));
  301. $($sibling.attr('href')).hide();
  302. $sibling.removeClass('current');
  303. $tab.addClass('current');
  304. $tab_panel.show();
  305. return this;
  306. },
  307. showHelpMessages: function (msgs) {
  308. // Override showHelpMessages in ChatBoxView, for now do nothing.
  309. return;
  310. }
  311. });
  312. converse.LoginPanel = Backbone.View.extend({
  313. tagName: 'div',
  314. id: "login-dialog",
  315. className: 'controlbox-pane',
  316. events: {
  317. 'submit form#converse-login': 'authenticate'
  318. },
  319. initialize: function (cfg) {
  320. cfg.$parent.html(this.$el.html(
  321. converse.templates.login_panel({
  322. 'LOGIN': converse.LOGIN,
  323. 'ANONYMOUS': converse.ANONYMOUS,
  324. 'PREBIND': converse.PREBIND,
  325. 'auto_login': converse.auto_login,
  326. 'authentication': converse.authentication,
  327. 'label_username': __('XMPP Username:'),
  328. 'label_password': __('Password:'),
  329. 'label_anon_login': __('Click here to log in anonymously'),
  330. 'label_login': __('Log In'),
  331. 'placeholder_username': (converse.locked_domain || converse.default_domain) && __('Username') || __('user@server'),
  332. 'placeholder_password': __('password')
  333. })
  334. ));
  335. this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
  336. },
  337. render: function () {
  338. this.$tabs.append(converse.templates.login_tab({label_sign_in: __('Sign in')}));
  339. this.$el.find('input#jid').focus();
  340. if (!this.$el.is(':visible')) {
  341. this.$el.show();
  342. }
  343. return this;
  344. },
  345. authenticate: function (ev) {
  346. if (ev && ev.preventDefault) { ev.preventDefault(); }
  347. var $form = $(ev.target);
  348. if (converse.authentication === converse.ANONYMOUS) {
  349. this.connect($form, converse.jid, null);
  350. return;
  351. }
  352. var $jid_input = $form.find('input[name=jid]'),
  353. jid = $jid_input.val(),
  354. $pw_input = $form.find('input[name=password]'),
  355. password = $pw_input.val(),
  356. errors = false;
  357. if (! jid) {
  358. errors = true;
  359. $jid_input.addClass('error');
  360. }
  361. if (! password) {
  362. errors = true;
  363. $pw_input.addClass('error');
  364. }
  365. if (errors) { return; }
  366. if (converse.locked_domain) {
  367. jid = Strophe.escapeNode(jid) + '@' + converse.locked_domain;
  368. } else if (converse.default_domain && jid.indexOf('@') === -1) {
  369. jid = jid + '@' + converse.default_domain;
  370. }
  371. this.connect($form, jid, password);
  372. return false;
  373. },
  374. connect: function ($form, jid, password) {
  375. var resource;
  376. if ($form) {
  377. $form.find('input[type=submit]').hide().after('<span class="spinner login-submit"/>');
  378. }
  379. if (jid) {
  380. resource = Strophe.getResourceFromJid(jid);
  381. if (!resource) {
  382. jid = jid.toLowerCase() + converse.generateResource();
  383. } else {
  384. jid = Strophe.getBareJidFromJid(jid).toLowerCase()+'/'+resource;
  385. }
  386. }
  387. converse.connection.connect(jid, password, converse.onConnectStatusChanged);
  388. },
  389. remove: function () {
  390. this.$tabs.empty();
  391. this.$el.parent().empty();
  392. }
  393. });
  394. converse.XMPPStatusView = Backbone.View.extend({
  395. el: "span#xmpp-status-holder",
  396. events: {
  397. "click a.choose-xmpp-status": "toggleOptions",
  398. "click #fancy-xmpp-status-select a.change-xmpp-status-message": "renderStatusChangeForm",
  399. "submit #set-custom-xmpp-status": "setStatusMessage",
  400. "click .dropdown dd ul li a": "setStatus"
  401. },
  402. initialize: function () {
  403. this.model.on("change:status", this.updateStatusUI, this);
  404. this.model.on("change:status_message", this.updateStatusUI, this);
  405. this.model.on("update-status-ui", this.updateStatusUI, this);
  406. },
  407. render: function () {
  408. // Replace the default dropdown with something nicer
  409. var $select = this.$el.find('select#select-xmpp-status'),
  410. chat_status = this.model.get('status') || 'offline',
  411. options = $('option', $select),
  412. $options_target,
  413. options_list = [];
  414. this.$el.html(converse.templates.choose_status());
  415. this.$el.find('#fancy-xmpp-status-select')
  416. .html(converse.templates.chat_status({
  417. 'status_message': this.model.get('status_message') || __("I am %1$s", this.getPrettyStatus(chat_status)),
  418. 'chat_status': chat_status,
  419. 'desc_custom_status': __('Click here to write a custom status message'),
  420. 'desc_change_status': __('Click to change your chat status')
  421. }));
  422. // iterate through all the <option> elements and add option values
  423. options.each(function () {
  424. options_list.push(converse.templates.status_option({
  425. 'value': $(this).val(),
  426. 'text': this.text
  427. }));
  428. });
  429. $options_target = this.$el.find("#target dd ul").hide();
  430. $options_target.append(options_list.join(''));
  431. $select.remove();
  432. return this;
  433. },
  434. toggleOptions: function (ev) {
  435. ev.preventDefault();
  436. $(ev.target).parent().parent().siblings('dd').find('ul').toggle('fast');
  437. },
  438. renderStatusChangeForm: function (ev) {
  439. ev.preventDefault();
  440. var status_message = this.model.get('status') || 'offline';
  441. var input = converse.templates.change_status_message({
  442. 'status_message': status_message,
  443. 'label_custom_status': __('Custom status'),
  444. 'label_save': __('Save')
  445. });
  446. var $xmppstatus = this.$el.find('.xmpp-status');
  447. $xmppstatus.parent().addClass('no-border');
  448. $xmppstatus.replaceWith(input);
  449. this.$el.find('.custom-xmpp-status').focus().focus();
  450. },
  451. setStatusMessage: function (ev) {
  452. ev.preventDefault();
  453. this.model.setStatusMessage($(ev.target).find('input').val());
  454. },
  455. setStatus: function (ev) {
  456. ev.preventDefault();
  457. var $el = $(ev.currentTarget),
  458. value = $el.attr('data-value');
  459. if (value === 'logout') {
  460. this.$el.find(".dropdown dd ul").hide();
  461. converse.logOut();
  462. } else {
  463. this.model.setStatus(value);
  464. this.$el.find(".dropdown dd ul").hide();
  465. }
  466. },
  467. getPrettyStatus: function (stat) {
  468. if (stat === 'chat') {
  469. return __('online');
  470. } else if (stat === 'dnd') {
  471. return __('busy');
  472. } else if (stat === 'xa') {
  473. return __('away for long');
  474. } else if (stat === 'away') {
  475. return __('away');
  476. } else if (stat === 'offline') {
  477. return __('offline');
  478. } else {
  479. return __(stat) || __('online');
  480. }
  481. },
  482. updateStatusUI: function (model) {
  483. var stat = model.get('status');
  484. // For translators: the %1$s part gets replaced with the status
  485. // Example, I am online
  486. var status_message = model.get('status_message') || __("I am %1$s", this.getPrettyStatus(stat));
  487. this.$el.find('#fancy-xmpp-status-select').removeClass('no-border').html(
  488. converse.templates.chat_status({
  489. 'chat_status': stat,
  490. 'status_message': status_message,
  491. 'desc_custom_status': __('Click here to write a custom status message'),
  492. 'desc_change_status': __('Click to change your chat status')
  493. }));
  494. }
  495. });
  496. converse.ContactsPanel = Backbone.View.extend({
  497. tagName: 'div',
  498. className: 'controlbox-pane',
  499. id: 'users',
  500. events: {
  501. 'click a.toggle-xmpp-contact-form': 'toggleContactForm',
  502. 'submit form.add-xmpp-contact': 'addContactFromForm',
  503. 'submit form.search-xmpp-contact': 'searchContacts',
  504. 'click a.subscribe-to-user': 'addContactFromList'
  505. },
  506. initialize: function (cfg) {
  507. cfg.$parent.append(this.$el);
  508. this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
  509. },
  510. render: function () {
  511. var markup;
  512. var widgets = converse.templates.contacts_panel({
  513. label_online: __('Online'),
  514. label_busy: __('Busy'),
  515. label_away: __('Away'),
  516. label_offline: __('Offline'),
  517. label_logout: __('Log out'),
  518. include_offline_state: converse.include_offline_state,
  519. allow_logout: converse.allow_logout
  520. });
  521. this.$tabs.append(converse.templates.contacts_tab({label_contacts: LABEL_CONTACTS}));
  522. if (converse.xhr_user_search) {
  523. markup = converse.templates.search_contact({
  524. label_contact_name: __('Contact name'),
  525. label_search: __('Search')
  526. });
  527. } else {
  528. markup = converse.templates.add_contact_form({
  529. label_contact_username: __('e.g. user@example.org'),
  530. label_add: __('Add')
  531. });
  532. }
  533. if (converse.allow_contact_requests) {
  534. widgets += converse.templates.add_contact_dropdown({
  535. label_click_to_chat: __('Click to add new chat contacts'),
  536. label_add_contact: __('Add a contact')
  537. });
  538. }
  539. this.$el.html(widgets);
  540. this.$el.find('.search-xmpp ul').append(markup);
  541. return this;
  542. },
  543. toggleContactForm: function (ev) {
  544. ev.preventDefault();
  545. this.$el.find('.search-xmpp').toggle('fast', function () {
  546. if ($(this).is(':visible')) {
  547. $(this).find('input.username').focus();
  548. }
  549. });
  550. },
  551. searchContacts: function (ev) {
  552. ev.preventDefault();
  553. $.getJSON(converse.xhr_user_search_url+ "?q=" + $(ev.target).find('input.username').val(), function (data) {
  554. var $ul= $('.search-xmpp ul');
  555. $ul.find('li.found-user').remove();
  556. $ul.find('li.chat-info').remove();
  557. if (!data.length) {
  558. $ul.append('<li class="chat-info">'+__('No users found')+'</li>');
  559. }
  560. $(data).each(function (idx, obj) {
  561. $ul.append(
  562. $('<li class="found-user"></li>')
  563. .append(
  564. $('<a class="subscribe-to-user" href="#" title="'+__('Click to add as a chat contact')+'"></a>')
  565. .attr('data-recipient', Strophe.getNodeFromJid(obj.id)+"@"+Strophe.getDomainFromJid(obj.id))
  566. .text(obj.fullname)
  567. )
  568. );
  569. });
  570. });
  571. },
  572. addContactFromForm: function (ev) {
  573. ev.preventDefault();
  574. var $input = $(ev.target).find('input');
  575. var jid = $input.val();
  576. if (! jid) {
  577. // this is not a valid JID
  578. $input.addClass('error');
  579. return;
  580. }
  581. converse.roster.addAndSubscribe(jid);
  582. $('.search-xmpp').hide();
  583. },
  584. addContactFromList: function (ev) {
  585. ev.preventDefault();
  586. var $target = $(ev.target),
  587. jid = $target.attr('data-recipient'),
  588. name = $target.text();
  589. converse.roster.addAndSubscribe(jid, name);
  590. $target.parent().remove();
  591. $('.search-xmpp').hide();
  592. }
  593. });
  594. converse.ControlBoxToggle = Backbone.View.extend({
  595. tagName: 'a',
  596. className: 'toggle-controlbox',
  597. id: 'toggle-controlbox',
  598. events: {
  599. 'click': 'onClick'
  600. },
  601. attributes: {
  602. 'href': "#"
  603. },
  604. initialize: function () {
  605. this.render();
  606. converse.on('initialized', function () {
  607. converse.roster.on("add", this.updateOnlineCount, this);
  608. converse.roster.on('change', this.updateOnlineCount, this);
  609. converse.roster.on("destroy", this.updateOnlineCount, this);
  610. converse.roster.on("remove", this.updateOnlineCount, this);
  611. }.bind(this));
  612. },
  613. render: function () {
  614. $('#conversejs').prepend(this.$el.html(
  615. converse.templates.controlbox_toggle({
  616. 'label_toggle': __('Toggle chat')
  617. })
  618. ));
  619. // We let the render method of ControlBoxView decide whether
  620. // the ControlBox or the Toggle must be shown. This prevents
  621. // artifacts (i.e. on page load the toggle is shown only to then
  622. // seconds later be hidden in favor of the control box).
  623. this.$el.hide();
  624. return this;
  625. },
  626. updateOnlineCount: _.debounce(function () {
  627. if (typeof converse.roster === 'undefined') {
  628. return;
  629. }
  630. var $count = this.$('#online-count');
  631. $count.text('('+converse.roster.getNumOnlineContacts()+')');
  632. if (!$count.is(':visible')) {
  633. $count.show();
  634. }
  635. }, converse.animate ? 100 : 0),
  636. hide: function (callback) {
  637. this.$el.fadeOut('fast', callback);
  638. },
  639. show: function (callback) {
  640. this.$el.show('fast', callback);
  641. },
  642. showControlBox: function () {
  643. var controlbox = converse.chatboxes.get('controlbox');
  644. if (!controlbox) {
  645. controlbox = converse.addControlBox();
  646. }
  647. if (converse.connection.connected) {
  648. controlbox.save({closed: false});
  649. } else {
  650. controlbox.trigger('show');
  651. }
  652. },
  653. onClick: function (e) {
  654. e.preventDefault();
  655. if ($("div#controlbox").is(':visible')) {
  656. var controlbox = converse.chatboxes.get('controlbox');
  657. if (converse.connection.connected) {
  658. controlbox.save({closed: true});
  659. } else {
  660. controlbox.trigger('hide');
  661. }
  662. } else {
  663. this.showControlBox();
  664. }
  665. }
  666. });
  667. }
  668. });
  669. }));