/* global $, config, getRoomName, loggingConfig, JitsiMeetJS */ /* application specific logic */ const logger = require("jitsi-meet-logger").getLogger(__filename); import "babel-polyfill"; import "jquery"; import "jquery-contextmenu"; import "jquery-ui"; import "strophe"; import "strophe-disco"; import "strophe-caps"; import "jQuery-Impromptu"; import "autosize"; import 'aui'; import 'aui-experimental'; import 'aui-css'; import 'aui-experimental-css'; window.toastr = require("toastr"); const Logger = require("jitsi-meet-logger"); const LogCollector = Logger.LogCollector; import JitsiMeetLogStorage from "./modules/util/JitsiMeetLogStorage"; import URLProcessor from "./modules/config/URLProcessor"; import { generateRoomWithoutSeparator } from './modules/util/RoomnameGenerator'; import UI from "./modules/UI/UI"; import settings from "./modules/settings/Settings"; import conference from './conference'; import ConferenceUrl from './modules/URL/ConferenceUrl'; import API from './modules/API/API'; import UIEvents from './service/UI/UIEvents'; import getTokenData from "./modules/tokendata/TokenData"; import translation from "./modules/translation/translation"; const ConferenceEvents = JitsiMeetJS.events.conference; /** * Tries to push history state with the following parameters: * 'VideoChat', `Room: ${roomName}`, URL. If fail, prints the error and returns * it. */ function pushHistoryState(roomName, URL) { try { window.history.pushState( 'VideoChat', `Room: ${roomName}`, URL ); } catch (e) { logger.warn("Push history state failed with parameters:", 'VideoChat', `Room: ${roomName}`, URL, e); return e; } return null; } /** * Replaces current history state(replaces the URL displayed by the browser). * @param {string} newUrl the URL string which is to be displayed by the browser * to the user. */ function replaceHistoryState (newUrl) { if (window.history && typeof window.history.replaceState === 'function') { window.history.replaceState({}, document.title, newUrl); } } /** * Builds and returns the room name. */ function buildRoomName () { let roomName = getRoomName(); if(!roomName) { let word = generateRoomWithoutSeparator(); roomName = word.toLowerCase(); let historyURL = window.location.href + word; //Trying to push state with current URL + roomName pushHistoryState(word, historyURL); } return roomName; } /** * Adjusts the logging levels. * @private */ function configureLoggingLevels () { // NOTE The library Logger is separated from the app loggers, so the levels // have to be set in two places // Set default logging level const defaultLogLevel = loggingConfig.defaultLogLevel || JitsiMeetJS.logLevels.TRACE; Logger.setLogLevel(defaultLogLevel); JitsiMeetJS.setLogLevel(defaultLogLevel); // NOTE console was used on purpose here to go around the logging // and always print the default logging level to the console console.info("Default logging level set to: " + defaultLogLevel); // Set log level for each logger if (loggingConfig) { Object.keys(loggingConfig).forEach(function(loggerName) { if ('defaultLogLevel' !== loggerName) { const level = loggingConfig[loggerName]; Logger.setLogLevelById(level, loggerName); JitsiMeetJS.setLogLevelById(level, loggerName); } }); } } const APP = { // Used by do_external_connect.js if we receive the attach data after // connect was already executed. status property can be "initialized", // "ready" or "connecting". We are interested in "ready" status only which // means that connect was executed but we have to wait for the attach data. // In status "ready" handler property will be set to a function that will // finish the connect process when the attach data or error is received. connect: { status: "initialized", handler: null }, // Used for automated performance tests connectionTimes: { "index.loaded": window.indexLoadedTime }, UI, settings, conference, translation, /** * The log collector which captures JS console logs for this app. * @type {LogCollector} */ logCollector: null, /** * Indicates if the log collector has been started (it will not be started * if the welcome page is displayed). */ logCollectorStarted : false, /** * After the APP has been initialized provides utility methods for dealing * with the conference room URL(address). * @type ConferenceUrl */ ConferenceUrl : null, connection: null, API, init () { this.initLogging(); this.keyboardshortcut = require("./modules/keyboardshortcut/keyboardshortcut"); this.configFetch = require("./modules/config/HttpConfigFetch"); this.tokenData = getTokenData(); }, initLogging () { // Adjust logging level configureLoggingLevels(); // Create the LogCollector and register it as the global log transport. // It is done early to capture as much logs as possible. Captured logs // will be cached, before the JitsiMeetLogStorage gets ready (statistics // module is initialized). if (!this.logCollector && !loggingConfig.disableLogCollector) { this.logCollector = new LogCollector(new JitsiMeetLogStorage()); Logger.addGlobalTransport(this.logCollector); JitsiMeetJS.addGlobalLogTransport(this.logCollector); } } }; /** * If JWT token data it will be used for local user settings */ function setTokenData() { let localUser = APP.tokenData.caller; if(localUser) { APP.settings.setEmail((localUser.getEmail() || "").trim(), true); APP.settings.setAvatarUrl((localUser.getAvatarUrl() || "").trim()); APP.settings.setDisplayName((localUser.getName() || "").trim(), true); } } function init() { setTokenData(); // Initialize the conference URL handler APP.ConferenceUrl = new ConferenceUrl(window.location); // Clean up the URL displayed by the browser replaceHistoryState(APP.ConferenceUrl.getInviteUrl()); // TODO The execution of the mobile app starts from react/index.native.js. // Similarly, the execution of the Web app should start from // react/index.web.js for the sake of consistency and ease of understanding. // Temporarily though because we are at the beginning of introducing React // into the Web app, allow the execution of the Web app to start from app.js // in order to reduce the complexity of the beginning step. require('./react'); const isUIReady = APP.UI.start(); if (isUIReady) { APP.conference.init({roomName: buildRoomName()}).then(() => { if (APP.logCollector) { // Start the LogCollector's periodic "store logs" task only if // we're in the conference and not on the welcome page. This is // determined by the value of "isUIReady" const above. APP.logCollector.start(); APP.logCollectorStarted = true; // Make an attempt to flush in case a lot of logs have been // cached, before the collector was started. APP.logCollector.flush(); // This event listener will flush the logs, before // the statistics module (CallStats) is stopped. // // NOTE The LogCollector is not stopped, because this event can // be triggered multiple times during single conference // (whenever statistics module is stopped). That includes // the case when Jicofo terminates the single person left in the // room. It will then restart the media session when someone // eventually join the room which will start the stats again. APP.conference.addConferenceListener( ConferenceEvents.BEFORE_STATISTICS_DISPOSED, () => { if (APP.logCollector) { APP.logCollector.flush(); } } ); } APP.UI.initConference(); APP.UI.addListener(UIEvents.LANG_CHANGED, language => { APP.translation.setLanguage(language); APP.settings.setLanguage(language); }); APP.keyboardshortcut.init(); }).catch(err => { APP.UI.hideRingOverLay(); APP.API.notifyConferenceLeft(APP.conference.roomName); logger.error(err); }); } } /** * If we have an HTTP endpoint for getting config.json configured we're going to * read it and override properties from config.js and interfaceConfig.js. * If there is no endpoint we'll just continue with initialization. * Keep in mind that if the endpoint has been configured and we fail to obtain * the config for any reason then the conference won't start and error message * will be displayed to the user. */ function obtainConfigAndInit() { let roomName = APP.conference.roomName; if (config.configLocation) { APP.configFetch.obtainConfig( config.configLocation, roomName, // Get config result callback function(success, error) { if (success) { var now = APP.connectionTimes["configuration.fetched"] = window.performance.now(); logger.log("(TIME) configuration fetched:\t", now); init(); } else { // Show obtain config error, // pass the error object for report APP.UI.messageHandler.openReportDialog( null, "dialog.connectError", error); } }); } else { require("./modules/config/BoshAddressChoice").chooseAddress( config, roomName); init(); } } $(document).ready(function () { var now = APP.connectionTimes["document.ready"] = window.performance.now(); logger.log("(TIME) document ready:\t", now); URLProcessor.setConfigParametersFromUrl(); APP.init(); APP.translation.init(settings.getLanguage()); APP.API.init(APP.tokenData.externalAPISettings); obtainConfigAndInit(); }); $(window).bind('beforeunload', function () { // Stop the LogCollector if (APP.logCollectorStarted) { APP.logCollector.stop(); APP.logCollectorStarted = false; } APP.API.dispose(); }); module.exports = APP;