You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Toolbar.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. /* global APP, $, config, interfaceConfig, JitsiMeetJS */
  2. /* jshint -W101 */
  3. import UIUtil from '../util/UIUtil';
  4. import UIEvents from '../../../service/UI/UIEvents';
  5. let roomUrl = null;
  6. let emitter = null;
  7. /**
  8. * Opens the invite link dialog.
  9. */
  10. function openLinkDialog () {
  11. let inviteAttributes;
  12. if (roomUrl === null) {
  13. inviteAttributes = 'data-i18n="[value]roomUrlDefaultMsg" value="' +
  14. APP.translation.translateString("roomUrlDefaultMsg") + '"';
  15. } else {
  16. inviteAttributes = "value=\"" + encodeURI(roomUrl) + "\"";
  17. }
  18. APP.UI.messageHandler.openTwoButtonDialog(
  19. "dialog.shareLink", null, null,
  20. `<input id="inviteLinkRef" type="text" ${inviteAttributes} onclick="this.select();" readonly>`,
  21. false, "dialog.Invite",
  22. function (e, v) {
  23. if (v && roomUrl) {
  24. JitsiMeetJS.analytics.sendEvent('toolbar.invite.button');
  25. emitter.emit(UIEvents.USER_INVITED, roomUrl);
  26. }
  27. else {
  28. JitsiMeetJS.analytics.sendEvent('toolbar.invite.cancel');
  29. }
  30. },
  31. function (event) {
  32. if (roomUrl) {
  33. document.getElementById('inviteLinkRef').select();
  34. } else {
  35. if (event && event.target) {
  36. $(event.target).find('button[value=true]').prop('disabled', true);
  37. }
  38. }
  39. },
  40. function (e, v, m, f) {
  41. if(!v && !m && !f)
  42. JitsiMeetJS.analytics.sendEvent('toolbar.invite.close');
  43. }
  44. );
  45. }
  46. const buttonHandlers = {
  47. "toolbar_button_mute": function () {
  48. let sharedVideoManager = APP.UI.getSharedVideoManager();
  49. if (APP.conference.audioMuted) {
  50. // If there's a shared video with the volume "on" and we aren't
  51. // the video owner, we warn the user
  52. // that currently it's not possible to unmute.
  53. if (sharedVideoManager
  54. && sharedVideoManager.isSharedVideoVolumeOn()
  55. && !sharedVideoManager.isSharedVideoOwner()) {
  56. UIUtil.animateShowElement(
  57. $("#unableToUnmutePopup"), true, 5000);
  58. }
  59. else {
  60. JitsiMeetJS.analytics.sendEvent('toolbar.audio.unmuted');
  61. emitter.emit(UIEvents.AUDIO_MUTED, false, true);
  62. }
  63. } else {
  64. JitsiMeetJS.analytics.sendEvent('toolbar.audio.muted');
  65. emitter.emit(UIEvents.AUDIO_MUTED, true, true);
  66. }
  67. },
  68. "toolbar_button_camera": function () {
  69. if (APP.conference.videoMuted) {
  70. JitsiMeetJS.analytics.sendEvent('toolbar.video.enabled');
  71. emitter.emit(UIEvents.VIDEO_MUTED, false);
  72. } else {
  73. JitsiMeetJS.analytics.sendEvent('toolbar.video.disabled');
  74. emitter.emit(UIEvents.VIDEO_MUTED, true);
  75. }
  76. },
  77. "toolbar_button_security": function () {
  78. JitsiMeetJS.analytics.sendEvent('toolbar.lock.clicked');
  79. emitter.emit(UIEvents.ROOM_LOCK_CLICKED);
  80. },
  81. "toolbar_button_link": function () {
  82. JitsiMeetJS.analytics.sendEvent('toolbar.invite.clicked');
  83. openLinkDialog();
  84. },
  85. "toolbar_button_chat": function () {
  86. JitsiMeetJS.analytics.sendEvent('toolbar.chat.toggled');
  87. emitter.emit(UIEvents.TOGGLE_CHAT);
  88. },
  89. "toolbar_button_etherpad": function () {
  90. JitsiMeetJS.analytics.sendEvent('toolbar.etherpad.clicked');
  91. emitter.emit(UIEvents.ETHERPAD_CLICKED);
  92. },
  93. "toolbar_button_sharedvideo": function () {
  94. JitsiMeetJS.analytics.sendEvent('toolbar.sharedvideo.clicked');
  95. emitter.emit(UIEvents.SHARED_VIDEO_CLICKED);
  96. },
  97. "toolbar_button_desktopsharing": function () {
  98. if (APP.conference.isSharingScreen) {
  99. JitsiMeetJS.analytics.sendEvent('toolbar.screen.disabled');
  100. } else {
  101. JitsiMeetJS.analytics.sendEvent('toolbar.screen.enabled');
  102. }
  103. emitter.emit(UIEvents.TOGGLE_SCREENSHARING);
  104. },
  105. "toolbar_button_fullScreen": function() {
  106. JitsiMeetJS.analytics.sendEvent('toolbar.fullscreen.enabled');
  107. UIUtil.buttonClick("#toolbar_button_fullScreen",
  108. "icon-full-screen icon-exit-full-screen");
  109. emitter.emit(UIEvents.FULLSCREEN_TOGGLE);
  110. },
  111. "toolbar_button_sip": function () {
  112. JitsiMeetJS.analytics.sendEvent('toolbar.sip.clicked');
  113. showSipNumberInput();
  114. },
  115. "toolbar_button_dialpad": function () {
  116. JitsiMeetJS.analytics.sendEvent('toolbar.sip.dialpad.clicked');
  117. dialpadButtonClicked();
  118. },
  119. "toolbar_button_settings": function () {
  120. JitsiMeetJS.analytics.sendEvent('toolbar.settings.toggled');
  121. emitter.emit(UIEvents.TOGGLE_SETTINGS);
  122. },
  123. "toolbar_button_hangup": function () {
  124. JitsiMeetJS.analytics.sendEvent('toolbar.hangup');
  125. emitter.emit(UIEvents.HANGUP);
  126. },
  127. "toolbar_button_login": function () {
  128. JitsiMeetJS.analytics.sendEvent('toolbar.authenticate.login.clicked');
  129. emitter.emit(UIEvents.AUTH_CLICKED);
  130. },
  131. "toolbar_button_logout": function () {
  132. JitsiMeetJS.analytics.sendEvent('toolbar.authenticate.logout.clicked');
  133. // Ask for confirmation
  134. APP.UI.messageHandler.openTwoButtonDialog(
  135. "dialog.logoutTitle",
  136. null,
  137. "dialog.logoutQuestion",
  138. null,
  139. false,
  140. "dialog.Yes",
  141. function (evt, yes) {
  142. if (yes) {
  143. emitter.emit(UIEvents.LOGOUT);
  144. }
  145. }
  146. );
  147. }
  148. };
  149. const defaultToolbarButtons = {
  150. 'microphone': {
  151. id: '#toolbar_button_mute',
  152. shortcut: 'M',
  153. shortcutAttr: 'mutePopover',
  154. shortcutFunc: function() {
  155. JitsiMeetJS.analytics.sendEvent('shortcut.audiomute.toggled');
  156. APP.conference.toggleAudioMuted();
  157. },
  158. shortcutDescription: "keyboardShortcuts.mute"
  159. },
  160. 'camera': {
  161. id: '#toolbar_button_camera',
  162. shortcut: 'V',
  163. shortcutAttr: 'toggleVideoPopover',
  164. shortcutFunc: function() {
  165. JitsiMeetJS.analytics.sendEvent('shortcut.videomute.toggled');
  166. APP.conference.toggleVideoMuted();
  167. },
  168. shortcutDescription: "keyboardShortcuts.videoMute"
  169. },
  170. 'desktop': {
  171. id: '#toolbar_button_desktopsharing',
  172. shortcut: 'D',
  173. shortcutAttr: 'toggleDesktopSharingPopover',
  174. shortcutFunc: function() {
  175. JitsiMeetJS.analytics.sendEvent('shortcut.screen.toggled');
  176. APP.conference.toggleScreenSharing();
  177. },
  178. shortcutDescription: "keyboardShortcuts.toggleScreensharing"
  179. },
  180. 'security': {
  181. id: '#toolbar_button_security'
  182. },
  183. 'invite': {
  184. id: '#toolbar_button_link'
  185. },
  186. 'chat': {
  187. id: '#toolbar_button_chat',
  188. shortcut: 'C',
  189. shortcutAttr: 'toggleChatPopover',
  190. shortcutFunc: function() {
  191. JitsiMeetJS.analytics.sendEvent('shortcut.chat.toggled');
  192. APP.UI.toggleChat();
  193. },
  194. shortcutDescription: "keyboardShortcuts.toggleChat"
  195. },
  196. 'etherpad': {
  197. id: '#toolbar_button_etherpad'
  198. },
  199. 'fullscreen': {
  200. id: '#toolbar_button_fullScreen'
  201. },
  202. 'settings': {
  203. id: '#toolbar_button_settings'
  204. },
  205. 'hangup': {
  206. id: '#toolbar_button_hangup'
  207. }
  208. };
  209. function dialpadButtonClicked() {
  210. //TODO show the dialpad box
  211. }
  212. function showSipNumberInput () {
  213. let defaultNumber = config.defaultSipNumber
  214. ? config.defaultSipNumber
  215. : '';
  216. let sipMsg = APP.translation.generateTranslationHTML("dialog.sipMsg");
  217. APP.UI.messageHandler.openTwoButtonDialog(
  218. null, null, null,
  219. `<h2>${sipMsg}</h2>
  220. <input name="sipNumber" type="text" value="${defaultNumber}" autofocus>`,
  221. false, "dialog.Dial",
  222. function (e, v, m, f) {
  223. if (v && f.sipNumber) {
  224. emitter.emit(UIEvents.SIP_DIAL, f.sipNumber);
  225. }
  226. },
  227. null, null, ':input:first'
  228. );
  229. }
  230. const Toolbar = {
  231. init (eventEmitter) {
  232. emitter = eventEmitter;
  233. // The toolbar is enabled by default.
  234. this.enabled = true;
  235. this.toolbarSelector = $("#header");
  236. UIUtil.hideDisabledButtons(defaultToolbarButtons);
  237. Object.keys(defaultToolbarButtons).forEach(
  238. id => {
  239. if (UIUtil.isButtonEnabled(id)) {
  240. var button = defaultToolbarButtons[id];
  241. if (button.shortcut)
  242. APP.keyboardshortcut.registerShortcut(
  243. button.shortcut,
  244. button.shortcutAttr,
  245. button.shortcutFunc,
  246. button.shortcutDescription
  247. );
  248. }
  249. }
  250. );
  251. Object.keys(buttonHandlers).forEach(
  252. buttonId => $(`#${buttonId}`).click(function(event) {
  253. !$(this).prop('disabled') && buttonHandlers[buttonId](event);
  254. })
  255. );
  256. },
  257. /**
  258. * Enables / disables the toolbar.
  259. * @param {e} set to {true} to enable the toolbar or {false}
  260. * to disable it
  261. */
  262. enable (e) {
  263. this.enabled = e;
  264. if (!e && this.isVisible())
  265. this.hide(false);
  266. },
  267. /**
  268. * Indicates if the bottom toolbar is currently enabled.
  269. * @return {this.enabled}
  270. */
  271. isEnabled() {
  272. return this.enabled;
  273. },
  274. /**
  275. * Updates the room invite url.
  276. */
  277. updateRoomUrl (newRoomUrl) {
  278. roomUrl = newRoomUrl;
  279. // If the invite dialog has been already opened we update the
  280. // information.
  281. let inviteLink = document.getElementById('inviteLinkRef');
  282. if (inviteLink) {
  283. inviteLink.value = roomUrl;
  284. inviteLink.select();
  285. $('#inviteLinkRef').parent()
  286. .find('button[value=true]').prop('disabled', false);
  287. }
  288. },
  289. /**
  290. * Unlocks the lock button state.
  291. */
  292. unlockLockButton () {
  293. if ($("#toolbar_button_security").hasClass("icon-security-locked"))
  294. UIUtil.buttonClick("#toolbar_button_security",
  295. "icon-security icon-security-locked");
  296. },
  297. /**
  298. * Updates the lock button state to locked.
  299. */
  300. lockLockButton () {
  301. if ($("#toolbar_button_security").hasClass("icon-security"))
  302. UIUtil.buttonClick("#toolbar_button_security",
  303. "icon-security icon-security-locked");
  304. },
  305. /**
  306. * Shows or hides authentication button
  307. * @param show <tt>true</tt> to show or <tt>false</tt> to hide
  308. */
  309. showAuthenticateButton (show) {
  310. if (UIUtil.isButtonEnabled('authentication') && show) {
  311. $('#authentication').css({display: "inline"});
  312. } else {
  313. $('#authentication').css({display: "none"});
  314. }
  315. },
  316. showEtherpadButton () {
  317. if (!$('#toolbar_button_etherpad').is(":visible")) {
  318. $('#toolbar_button_etherpad').css({display: 'inline-block'});
  319. }
  320. },
  321. // Shows or hides the 'shared video' button.
  322. showSharedVideoButton () {
  323. if (UIUtil.isButtonEnabled('sharedvideo')
  324. && config.disableThirdPartyRequests !== true) {
  325. $('#toolbar_button_sharedvideo').css({display: "inline-block"});
  326. } else {
  327. $('#toolbar_button_sharedvideo').css({display: "none"});
  328. }
  329. },
  330. // checks whether desktop sharing is enabled and whether
  331. // we have params to start automatically sharing
  332. checkAutoEnableDesktopSharing () {
  333. if (UIUtil.isButtonEnabled('desktop')
  334. && config.autoEnableDesktopSharing) {
  335. emitter.emit(UIEvents.TOGGLE_SCREENSHARING);
  336. }
  337. },
  338. // Shows or hides SIP calls button
  339. showSipCallButton (show) {
  340. if (APP.conference.sipGatewayEnabled()
  341. && UIUtil.isButtonEnabled('sip') && show) {
  342. $('#toolbar_button_sip').css({display: "inline-block"});
  343. } else {
  344. $('#toolbar_button_sip').css({display: "none"});
  345. }
  346. },
  347. // Shows or hides the dialpad button
  348. showDialPadButton (show) {
  349. if (UIUtil.isButtonEnabled('dialpad') && show) {
  350. $('#toolbar_button_dialpad').css({display: "inline-block"});
  351. } else {
  352. $('#toolbar_button_dialpad').css({display: "none"});
  353. }
  354. },
  355. /**
  356. * Displays user authenticated identity name(login).
  357. * @param authIdentity identity name to be displayed.
  358. */
  359. setAuthenticatedIdentity (authIdentity) {
  360. if (authIdentity) {
  361. let selector = $('#toolbar_auth_identity');
  362. selector.css({display: "list-item"});
  363. selector.text(authIdentity);
  364. } else {
  365. $('#toolbar_auth_identity').css({display: "none"});
  366. }
  367. },
  368. /**
  369. * Shows/hides login button.
  370. * @param show <tt>true</tt> to show
  371. */
  372. showLoginButton (show) {
  373. if (UIUtil.isButtonEnabled('authentication') && show) {
  374. $('#toolbar_button_login').css({display: "list-item"});
  375. } else {
  376. $('#toolbar_button_login').css({display: "none"});
  377. }
  378. },
  379. /**
  380. * Shows/hides logout button.
  381. * @param show <tt>true</tt> to show
  382. */
  383. showLogoutButton (show) {
  384. if (UIUtil.isButtonEnabled('authentication') && show) {
  385. $('#toolbar_button_logout').css({display: "list-item"});
  386. } else {
  387. $('#toolbar_button_logout').css({display: "none"});
  388. }
  389. },
  390. /**
  391. * Update the state of the button. The button has blue glow if desktop
  392. * streaming is active.
  393. */
  394. updateDesktopSharingButtonState () {
  395. let button = $("#toolbar_button_desktopsharing");
  396. if (APP.conference.isSharingScreen) {
  397. button.addClass("glow");
  398. } else {
  399. button.removeClass("glow");
  400. }
  401. },
  402. /**
  403. * Marks video icon as muted or not.
  404. * @param {boolean} muted if icon should look like muted or not
  405. */
  406. markVideoIconAsMuted (muted) {
  407. $('#toolbar_button_camera').toggleClass("icon-camera-disabled", muted);
  408. },
  409. /**
  410. * Marks video icon as disabled or not.
  411. * @param {boolean} disabled if icon should look like disabled or not
  412. */
  413. markVideoIconAsDisabled (disabled) {
  414. var $btn = $('#toolbar_button_camera');
  415. $btn
  416. .prop("disabled", disabled)
  417. .attr("data-i18n", disabled
  418. ? "[content]toolbar.cameraDisabled"
  419. : "[content]toolbar.videomute")
  420. .attr("shortcut", disabled ? "" : "toggleVideoPopover");
  421. disabled
  422. ? $btn.attr("disabled", "disabled")
  423. : $btn.removeAttr("disabled");
  424. APP.translation.translateElement($btn);
  425. disabled && this.markVideoIconAsMuted(disabled);
  426. },
  427. /**
  428. * Marks audio icon as muted or not.
  429. * @param {boolean} muted if icon should look like muted or not
  430. */
  431. markAudioIconAsMuted (muted) {
  432. $('#toolbar_button_mute').toggleClass("icon-microphone",
  433. !muted).toggleClass("icon-mic-disabled", muted);
  434. },
  435. /**
  436. * Marks audio icon as disabled or not.
  437. * @param {boolean} disabled if icon should look like disabled or not
  438. */
  439. markAudioIconAsDisabled (disabled) {
  440. var $btn = $('#toolbar_button_mute');
  441. $btn
  442. .prop("disabled", disabled)
  443. .attr("data-i18n", disabled
  444. ? "[content]toolbar.micDisabled"
  445. : "[content]toolbar.mute")
  446. .attr("shortcut", disabled ? "" : "mutePopover");
  447. disabled
  448. ? $btn.attr("disabled", "disabled")
  449. : $btn.removeAttr("disabled");
  450. APP.translation.translateElement($btn);
  451. disabled && this.markAudioIconAsMuted(disabled);
  452. },
  453. /**
  454. * Indicates if the toolbar is currently hovered.
  455. * @return {boolean} true if the toolbar is currently hovered,
  456. * false otherwise
  457. */
  458. isHovered() {
  459. var hovered = false;
  460. this.toolbarSelector.find('*').each(function () {
  461. let id = $(this).attr('id');
  462. if ($(`#${id}:hover`).length > 0) {
  463. hovered = true;
  464. // break each
  465. return false;
  466. }
  467. });
  468. if (hovered)
  469. return true;
  470. if ($("#bottomToolbar:hover").length > 0) {
  471. return true;
  472. }
  473. return false;
  474. },
  475. /**
  476. * Returns true if this toolbar is currently visible, or false otherwise.
  477. * @return <tt>true</tt> if currently visible, <tt>false</tt> - otherwise
  478. */
  479. isVisible() {
  480. return this.toolbarSelector.is(":visible");
  481. },
  482. /**
  483. * Hides the toolbar with animation or not depending on the animate
  484. * parameter.
  485. */
  486. hide() {
  487. this.toolbarSelector.hide(
  488. "slide", { direction: "up", duration: 300});
  489. },
  490. /**
  491. * Shows the toolbar with animation or not depending on the animate
  492. * parameter.
  493. */
  494. show() {
  495. this.toolbarSelector.show(
  496. "slide", { direction: "up", duration: 300});
  497. }
  498. };
  499. export default Toolbar;