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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. var UI = {};
  2. var VideoLayout = require("./videolayout/VideoLayout.js");
  3. var AudioLevels = require("./audio_levels/AudioLevels.js");
  4. var Prezi = require("./prezi/Prezi.js");
  5. var Etherpad = require("./etherpad/Etherpad.js");
  6. var Chat = require("./side_pannels/chat/Chat.js");
  7. var Toolbar = require("./toolbars/toolbar");
  8. var ToolbarToggler = require("./toolbars/toolbartoggler");
  9. var BottomToolbar = require("./toolbars/BottomToolbar");
  10. var ContactList = require("./side_pannels/contactlist/ContactList");
  11. var Avatar = require("./avatar/Avatar");
  12. //var EventEmitter = require("events");
  13. var SettingsMenu = require("./side_pannels/settings/SettingsMenu");
  14. var Settings = require("./side_pannels/settings/Settings");
  15. var PanelToggler = require("./side_pannels/SidePanelToggler");
  16. var RoomNameGenerator = require("./welcome_page/RoomnameGenerator");
  17. UI.messageHandler = require("./util/MessageHandler");
  18. var messageHandler = UI.messageHandler;
  19. //var eventEmitter = new EventEmitter();
  20. function setupPrezi()
  21. {
  22. $("#reloadPresentationLink").click(function()
  23. {
  24. Prezi.reloadPresentation();
  25. });
  26. }
  27. function setupChat()
  28. {
  29. Chat.init();
  30. $("#toggle_smileys").click(function() {
  31. Chat.toggleSmileys();
  32. });
  33. }
  34. function setupToolbars() {
  35. Toolbar.init();
  36. Toolbar.setupButtonsFromConfig();
  37. BottomToolbar.init();
  38. }
  39. function streamHandler(stream) {
  40. switch (stream.type)
  41. {
  42. case "audio":
  43. VideoLayout.changeLocalAudio(stream);
  44. break;
  45. case "video":
  46. VideoLayout.changeLocalVideo(stream);
  47. break;
  48. case "stream":
  49. VideoLayout.changeLocalStream(stream);
  50. break;
  51. case "desktop":
  52. VideoLayout.changeLocalVideo(stream);
  53. break;
  54. }
  55. }
  56. function registerListeners() {
  57. RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
  58. RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED);
  59. RTC.addStreamListener(function (stream) {
  60. VideoLayout.onRemoteStreamAdded(stream);
  61. }, StreamEventTypes.EVENT_TYPE_REMOTE_CREATED);
  62. // Listen for large video size updates
  63. document.getElementById('largeVideo')
  64. .addEventListener('loadedmetadata', function (e) {
  65. currentVideoWidth = this.videoWidth;
  66. currentVideoHeight = this.videoHeight;
  67. VideoLayout.positionLarge(currentVideoWidth, currentVideoHeight);
  68. });
  69. statistics.addAudioLevelListener(function(jid, audioLevel)
  70. {
  71. var resourceJid;
  72. if(jid === statistics.LOCAL_JID)
  73. {
  74. resourceJid = AudioLevels.LOCAL_LEVEL;
  75. if(RTC.localAudio.isMuted())
  76. {
  77. audioLevel = 0;
  78. }
  79. }
  80. else
  81. {
  82. resourceJid = Strophe.getResourceFromJid(jid);
  83. }
  84. AudioLevels.updateAudioLevel(resourceJid, audioLevel,
  85. UI.getLargeVideoState().userResourceJid);
  86. });
  87. desktopsharing.addListener(function () {
  88. ToolbarToggler.showDesktopSharingButton();
  89. }, DesktopSharingEventTypes.INIT);
  90. desktopsharing.addListener(
  91. Toolbar.changeDesktopSharingButtonState,
  92. DesktopSharingEventTypes.SWITCHING_DONE);
  93. }
  94. function bindEvents()
  95. {
  96. /**
  97. * Resizes and repositions videos in full screen mode.
  98. */
  99. $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange',
  100. function () {
  101. VideoLayout.resizeLargeVideoContainer();
  102. VideoLayout.positionLarge();
  103. isFullScreen = document.fullScreen ||
  104. document.mozFullScreen ||
  105. document.webkitIsFullScreen;
  106. }
  107. );
  108. $(window).resize(function () {
  109. VideoLayout.resizeLargeVideoContainer();
  110. VideoLayout.positionLarge();
  111. });
  112. }
  113. UI.start = function () {
  114. document.title = interfaceConfig.APP_NAME;
  115. if(config.enableWelcomePage && window.location.pathname == "/" &&
  116. (!window.localStorage.welcomePageDisabled || window.localStorage.welcomePageDisabled == "false"))
  117. {
  118. $("#videoconference_page").hide();
  119. var setupWelcomePage = require("./welcome_page/WelcomePage");
  120. setupWelcomePage();
  121. return;
  122. }
  123. if (interfaceConfig.SHOW_JITSI_WATERMARK) {
  124. var leftWatermarkDiv
  125. = $("#largeVideoContainer div[class='watermark leftwatermark']");
  126. leftWatermarkDiv.css({display: 'block'});
  127. leftWatermarkDiv.parent().get(0).href
  128. = interfaceConfig.JITSI_WATERMARK_LINK;
  129. }
  130. if (interfaceConfig.SHOW_BRAND_WATERMARK) {
  131. var rightWatermarkDiv
  132. = $("#largeVideoContainer div[class='watermark rightwatermark']");
  133. rightWatermarkDiv.css({display: 'block'});
  134. rightWatermarkDiv.parent().get(0).href
  135. = interfaceConfig.BRAND_WATERMARK_LINK;
  136. rightWatermarkDiv.get(0).style.backgroundImage
  137. = "url(images/rightwatermark.png)";
  138. }
  139. if (interfaceConfig.SHOW_POWERED_BY) {
  140. $("#largeVideoContainer>a[class='poweredby']").css({display: 'block'});
  141. }
  142. $("#welcome_page").hide();
  143. $('body').popover({ selector: '[data-toggle=popover]',
  144. trigger: 'click hover',
  145. content: function() {
  146. return this.getAttribute("content") +
  147. KeyboardShortcut.getShortcut(this.getAttribute("shortcut"));
  148. }
  149. });
  150. VideoLayout.resizeLargeVideoContainer();
  151. $("#videospace").mousemove(function () {
  152. return ToolbarToggler.showToolbar();
  153. });
  154. // Set the defaults for prompt dialogs.
  155. jQuery.prompt.setDefaults({persistent: false});
  156. // KeyboardShortcut.init();
  157. registerListeners();
  158. bindEvents();
  159. setupPrezi();
  160. setupToolbars();
  161. setupChat();
  162. document.title = interfaceConfig.APP_NAME;
  163. $("#downloadlog").click(function (event) {
  164. dump(event.target);
  165. });
  166. if(config.enableWelcomePage && window.location.pathname == "/" &&
  167. (!window.localStorage.welcomePageDisabled || window.localStorage.welcomePageDisabled == "false"))
  168. {
  169. $("#videoconference_page").hide();
  170. var setupWelcomePage = require("./welcome_page/WelcomePage");
  171. setupWelcomePage();
  172. return;
  173. }
  174. $("#welcome_page").hide();
  175. document.getElementById('largeVideo').volume = 0;
  176. if (!$('#settings').is(':visible')) {
  177. console.log('init');
  178. init();
  179. } else {
  180. loginInfo.onsubmit = function (e) {
  181. if (e.preventDefault) e.preventDefault();
  182. $('#settings').hide();
  183. init();
  184. };
  185. }
  186. toastr.options = {
  187. "closeButton": true,
  188. "debug": false,
  189. "positionClass": "notification-bottom-right",
  190. "onclick": null,
  191. "showDuration": "300",
  192. "hideDuration": "1000",
  193. "timeOut": "2000",
  194. "extendedTimeOut": "1000",
  195. "showEasing": "swing",
  196. "hideEasing": "linear",
  197. "showMethod": "fadeIn",
  198. "hideMethod": "fadeOut",
  199. "reposition": function() {
  200. if(PanelToggler.isVisible()) {
  201. $("#toast-container").addClass("notification-bottom-right-center");
  202. } else {
  203. $("#toast-container").removeClass("notification-bottom-right-center");
  204. }
  205. },
  206. "newestOnTop": false
  207. };
  208. $('#settingsmenu>input').keyup(function(event){
  209. if(event.keyCode === 13) {//enter
  210. SettingsMenu.update();
  211. }
  212. });
  213. $("#updateSettings").click(function () {
  214. SettingsMenu.update();
  215. });
  216. };
  217. UI.setUserAvatar = function (jid, id) {
  218. Avatar.setUserAvatar(jid, id);
  219. };
  220. UI.toggleSmileys = function () {
  221. Chat.toggleSmileys();
  222. };
  223. UI.chatAddError = function(errorMessage, originalText)
  224. {
  225. return Chat.chatAddError(errorMessage, originalText);
  226. };
  227. UI.chatSetSubject = function(text)
  228. {
  229. return Chat.chatSetSubject(text);
  230. };
  231. UI.updateChatConversation = function (from, displayName, message) {
  232. return Chat.updateChatConversation(from, displayName, message);
  233. };
  234. UI.onMucJoined = function (jid, info) {
  235. Toolbar.updateRoomUrl(window.location.href);
  236. document.getElementById('localNick').appendChild(
  237. document.createTextNode(Strophe.getResourceFromJid(jid) + ' (me)')
  238. );
  239. var settings = Settings.getSettings();
  240. // Add myself to the contact list.
  241. ContactList.addContact(jid, settings.email || settings.uid);
  242. // Once we've joined the muc show the toolbar
  243. ToolbarToggler.showToolbar();
  244. // Show authenticate button if needed
  245. Toolbar.showAuthenticateButton(
  246. Moderator.isExternalAuthEnabled() && !Moderator.isModerator());
  247. var displayName = !config.displayJids
  248. ? info.displayName : Strophe.getResourceFromJid(jid);
  249. if (displayName)
  250. $(document).trigger('displaynamechanged',
  251. ['localVideoContainer', displayName + ' (me)']);
  252. };
  253. UI.initEtherpad = function (name) {
  254. Etherpad.init(name);
  255. };
  256. UI.onMucLeft = function (jid) {
  257. console.log('left.muc', jid);
  258. var displayName = $('#participant_' + Strophe.getResourceFromJid(jid) +
  259. '>.displayname').html();
  260. messageHandler.notify(displayName || 'Somebody',
  261. 'disconnected',
  262. 'disconnected');
  263. // Need to call this with a slight delay, otherwise the element couldn't be
  264. // found for some reason.
  265. // XXX(gp) it works fine without the timeout for me (with Chrome 38).
  266. window.setTimeout(function () {
  267. var container = document.getElementById(
  268. 'participant_' + Strophe.getResourceFromJid(jid));
  269. if (container) {
  270. ContactList.removeContact(jid);
  271. VideoLayout.removeConnectionIndicator(jid);
  272. // hide here, wait for video to close before removing
  273. $(container).hide();
  274. VideoLayout.resizeThumbnails();
  275. }
  276. }, 10);
  277. // Unlock large video
  278. if (focusedVideoInfo && focusedVideoInfo.jid === jid)
  279. {
  280. console.info("Focused video owner has left the conference");
  281. focusedVideoInfo = null;
  282. }
  283. };
  284. UI.getSettings = function () {
  285. return Settings.getSettings();
  286. };
  287. UI.toggleFilmStrip = function () {
  288. return BottomToolbar.toggleFilmStrip();
  289. };
  290. UI.toggleChat = function () {
  291. return BottomToolbar.toggleChat();
  292. };
  293. UI.toggleContactList = function () {
  294. return BottomToolbar.toggleContactList();
  295. };
  296. UI.onLocalRoleChange = function (jid, info, pres) {
  297. console.info("My role changed, new role: " + info.role);
  298. var isModerator = Moderator.isModerator();
  299. VideoLayout.showModeratorIndicator();
  300. Toolbar.showAuthenticateButton(
  301. Moderator.isExternalAuthEnabled() && !isModerator);
  302. if (isModerator) {
  303. Toolbar.closeAuthenticationWindow();
  304. messageHandler.notify(
  305. 'Me', 'connected', 'Moderator rights granted !');
  306. }
  307. };
  308. UI.onDisposeConference = function (unload) {
  309. Toolbar.showAuthenticateButton(false);
  310. };
  311. UI.onModeratorStatusChanged = function (isModerator) {
  312. Toolbar.showSipCallButton(isModerator);
  313. Toolbar.showRecordingButton(
  314. isModerator); //&&
  315. // FIXME:
  316. // Recording visible if
  317. // there are at least 2(+ 1 focus) participants
  318. //Object.keys(connection.emuc.members).length >= 3);
  319. if (isModerator && config.etherpad_base) {
  320. Etherpad.init();
  321. }
  322. };
  323. UI.onPasswordReqiured = function (callback) {
  324. // password is required
  325. Toolbar.lockLockButton();
  326. messageHandler.openTwoButtonDialog(null,
  327. '<h2>Password required</h2>' +
  328. '<input id="lockKey" type="text" placeholder="password" autofocus>',
  329. true,
  330. "Ok",
  331. function (e, v, m, f) {},
  332. function (event) {
  333. document.getElementById('lockKey').focus();
  334. },
  335. function (e, v, m, f) {
  336. if (v) {
  337. var lockKey = document.getElementById('lockKey');
  338. if (lockKey.value !== null) {
  339. Toolbar.setSharedKey(lockKey.value);
  340. callback(lockKey.value);
  341. }
  342. }
  343. }
  344. );
  345. };
  346. UI.onAuthenticationRequired = function () {
  347. // This is the loop that will wait for the room to be created by
  348. // someone else. 'auth_required.moderator' will bring us back here.
  349. authRetryId = window.setTimeout(
  350. function () {
  351. Moderator.allocateConferenceFocus(roomName, doJoinAfterFocus);
  352. }, 5000);
  353. // Show prompt only if it's not open
  354. if (authDialog !== null) {
  355. return;
  356. }
  357. // extract room name from 'room@muc.server.net'
  358. var room = roomName.substr(0, roomName.indexOf('@'));
  359. authDialog = messageHandler.openDialog(
  360. 'Stop',
  361. 'Authentication is required to create room:<br/><b>' + room +
  362. '</b></br> You can either authenticate to create the room or ' +
  363. 'just wait for someone else to do so.',
  364. true,
  365. {
  366. Authenticate: 'authNow'
  367. },
  368. function (onSubmitEvent, submitValue) {
  369. // Do not close the dialog yet
  370. onSubmitEvent.preventDefault();
  371. // Open login popup
  372. if (submitValue === 'authNow') {
  373. Toolbar.authenticateClicked();
  374. }
  375. }
  376. );
  377. };
  378. UI.setRecordingButtonState = function (state) {
  379. Toolbar.setRecordingButtonState(state);
  380. };
  381. UI.inputDisplayNameHandler = function (value) {
  382. VideoLayout.inputDisplayNameHandler(value);
  383. };
  384. UI.onMucEntered = function (jid, id, displayName) {
  385. messageHandler.notify(displayName || 'Somebody',
  386. 'connected',
  387. 'connected');
  388. // Add Peer's container
  389. VideoLayout.ensurePeerContainerExists(jid,id);
  390. };
  391. UI.onMucPresenceStatus = function ( jid, info) {
  392. VideoLayout.setPresenceStatus(
  393. 'participant_' + Strophe.getResourceFromJid(jid), info.status);
  394. };
  395. UI.onMucRoleChanged = function (role, displayName) {
  396. VideoLayout.showModeratorIndicator();
  397. if (role === 'moderator') {
  398. var displayName = displayName;
  399. if (!displayName) {
  400. displayName = 'Somebody';
  401. }
  402. messageHandler.notify(
  403. displayName,
  404. 'connected',
  405. 'Moderator rights granted to ' + displayName + '!');
  406. }
  407. };
  408. UI.updateLocalConnectionStats = function(percent, stats)
  409. {
  410. VideoLayout.updateLocalConnectionStats(percent, stats);
  411. };
  412. UI.updateConnectionStats = function(jid, percent, stats)
  413. {
  414. VideoLayout.updateConnectionStats(jid, percent, stats);
  415. };
  416. UI.onStatsStop = function () {
  417. VideoLayout.onStatsStop();
  418. };
  419. UI.getLargeVideoState = function()
  420. {
  421. return VideoLayout.getLargeVideoState();
  422. };
  423. UI.showLocalAudioIndicator = function (mute) {
  424. VideoLayout.showLocalAudioIndicator(mute);
  425. };
  426. UI.generateRoomName = function() {
  427. var roomnode = null;
  428. var path = window.location.pathname;
  429. // determinde the room node from the url
  430. // TODO: just the roomnode or the whole bare jid?
  431. if (config.getroomnode && typeof config.getroomnode === 'function') {
  432. // custom function might be responsible for doing the pushstate
  433. roomnode = config.getroomnode(path);
  434. } else {
  435. /* fall back to default strategy
  436. * this is making assumptions about how the URL->room mapping happens.
  437. * It currently assumes deployment at root, with a rewrite like the
  438. * following one (for nginx):
  439. location ~ ^/([a-zA-Z0-9]+)$ {
  440. rewrite ^/(.*)$ / break;
  441. }
  442. */
  443. if (path.length > 1) {
  444. roomnode = path.substr(1).toLowerCase();
  445. } else {
  446. var word = RoomNameGenerator.generateRoomWithoutSeparator();
  447. roomnode = word.toLowerCase();
  448. window.history.pushState('VideoChat',
  449. 'Room: ' + word, window.location.pathname + word);
  450. }
  451. }
  452. roomName = roomnode + '@' + config.hosts.muc;
  453. };
  454. UI.connectionIndicatorShowMore = function(id)
  455. {
  456. return VideoLayout.connectionIndicators[id].showMore();
  457. };
  458. UI.showToolbar = function () {
  459. return ToolbarToggler.showToolbar();
  460. };
  461. UI.dockToolbar = function (isDock) {
  462. return ToolbarToggler.dockToolbar(isDock);
  463. };
  464. function dump(elem, filename) {
  465. elem = elem.parentNode;
  466. elem.download = filename || 'meetlog.json';
  467. elem.href = 'data:application/json;charset=utf-8,\n';
  468. var data = {};
  469. if (connection.jingle) {
  470. data = connection.jingle.populateData();
  471. }
  472. var metadata = {};
  473. metadata.time = new Date();
  474. metadata.url = window.location.href;
  475. metadata.ua = navigator.userAgent;
  476. if (connection.logger) {
  477. metadata.xmpp = connection.logger.log;
  478. }
  479. data.metadata = metadata;
  480. elem.href += encodeURIComponent(JSON.stringify(data, null, ' '));
  481. return false;
  482. }
  483. module.exports = UI;