diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..7eeae4728
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,10 @@
+# http://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+max_line_length = 80
+trim_trailing_whitespace = true
diff --git a/.jshintignore b/.jshintignore
index edb07dd2b..3d4784468 100644
--- a/.jshintignore
+++ b/.jshintignore
@@ -2,6 +2,7 @@ node_modules
libs
debian
analytics.js
+lib-jitsi-meet.js
modules/xmpp/strophe.emuc.js
modules/UI/prezi/Prezi.js
diff --git a/Makefile b/Makefile
index 6f5a920db..a323977b1 100644
--- a/Makefile
+++ b/Makefile
@@ -8,10 +8,13 @@ DEPLOY_DIR = libs
BROWSERIFY_FLAGS = -d
OUTPUT_DIR = .
-all: compile uglify deploy clean
+all: update-deps compile uglify deploy clean
+
+update-deps:
+ $(NPM) update
compile:
- $(NPM) update && $(BROWSERIFY) $(BROWSERIFY_FLAGS) -e app.js -s APP | $(EXORCIST) $(OUTPUT_DIR)/app.bundle.js.map > $(OUTPUT_DIR)/app.bundle.js
+ $(BROWSERIFY) $(BROWSERIFY_FLAGS) -e app.js -s APP | $(EXORCIST) $(OUTPUT_DIR)/app.bundle.js.map > $(OUTPUT_DIR)/app.bundle.js
clean:
rm -f $(OUTPUT_DIR)/app.bundle.*
diff --git a/app.js b/app.js
index 889096d2e..d6bb31683 100644
--- a/app.js
+++ b/app.js
@@ -1,4 +1,4 @@
-/* jshint -W117 */
+/* global $, JitsiMeetJS, config, Promise */
/* application specific logic */
require("jquery");
@@ -12,37 +12,314 @@ window.toastr = require("toastr");
require("jQuery-Impromptu");
require("autosize");
-var APP =
-{
+var CQEvents = require('./service/connectionquality/CQEvents');
+var UIEvents = require('./service/UI/UIEvents');
+
+var Commands = {
+ CONNECTION_QUALITY: "connectionQuality",
+ EMAIL: "email"
+};
+
+var APP = {
init: function () {
+ JitsiMeetJS.init();
+ JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);
+
+ this.conference = {
+ localId: undefined,
+ isModerator: false,
+ membersCount: 0,
+ audioMuted: false,
+ videoMuted: false,
+ isLocalId: function (id) {
+ return this.localId === id;
+ },
+ muteAudio: function (mute) {
+ APP.UI.eventEmitter.emit(UIEvents.AUDIO_MUTED, mute);
+ },
+ toggleAudioMuted: function () {
+ this.muteAudio(!this.audioMuted);
+ },
+ muteVideo: function (mute) {
+ APP.UI.eventEmitter.emit(UIEvents.VIDEO_MUTED, mute);
+ },
+ toggleVideoMuted: function () {
+ this.muteVideo(!this.videoMuted);
+ }
+ };
+
this.UI = require("./modules/UI/UI");
this.API = require("./modules/API/API");
this.connectionquality =
require("./modules/connectionquality/connectionquality");
this.statistics = require("./modules/statistics/statistics");
- this.RTC = require("./modules/RTC/RTC");
this.desktopsharing =
require("./modules/desktopsharing/desktopsharing");
- this.xmpp = require("./modules/xmpp/xmpp");
this.keyboardshortcut =
require("./modules/keyboardshortcut/keyboardshortcut");
this.translation = require("./modules/translation/translation");
this.settings = require("./modules/settings/Settings");
- //this.DTMF = require("./modules/DTMF/DTMF");
- this.members = require("./modules/members/MemberList");
this.configFetch = require("./modules/config/HttpConfigFetch");
}
};
-function init() {
- APP.desktopsharing.init();
- APP.RTC.start();
- APP.xmpp.start();
- APP.statistics.start();
- APP.connectionquality.init();
- APP.keyboardshortcut.init();
- APP.members.start();
+var ConnectionEvents = JitsiMeetJS.events.connection;
+var ConnectionErrors = JitsiMeetJS.errors.connection;
+function connect() {
+ var connection = new JitsiMeetJS.JitsiConnection(null, null, {
+ hosts: config.hosts,
+ bosh: config.bosh,
+ clientNode: config.clientNode
+ });
+
+ return new Promise(function (resolve, reject) {
+ var handlers = {};
+
+ var unsubscribe = function () {
+ Object.keys(handlers).forEach(function (event) {
+ connection.removeEventListener(event, handlers[event]);
+ });
+ };
+
+ handlers[ConnectionEvents.CONNECTION_ESTABLISHED] = function () {
+ console.log('CONNECTED');
+ unsubscribe();
+ resolve(connection);
+ };
+
+ var listenForFailure = function (event) {
+ handlers[event] = function () {
+ // convert arguments to array
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift(event);
+ // [event, ...params]
+ console.error('CONNECTION FAILED:', args);
+
+ unsubscribe();
+ reject(args);
+ };
+ };
+
+ listenForFailure(ConnectionEvents.CONNECTION_FAILED);
+ listenForFailure(ConnectionErrors.PASSWORD_REQUIRED);
+ listenForFailure(ConnectionErrors.CONNECTION_ERROR);
+ listenForFailure(ConnectionErrors.OTHER_ERRORS);
+
+ // install event listeners
+ Object.keys(handlers).forEach(function (event) {
+ connection.addEventListener(event, handlers[event]);
+ });
+
+ connection.connect();
+ }).catch(function (err) {
+ if (err[0] === ConnectionErrors.PASSWORD_REQUIRED) {
+ // FIXME ask for password and try again
+ return connect();
+ }
+ console.error('FAILED TO CONNECT', err);
+ APP.UI.notifyConnectionFailed(err[1]);
+
+ throw new Error(err[0]);
+ });
+}
+
+var ConferenceEvents = JitsiMeetJS.events.conference;
+var ConferenceErrors = JitsiMeetJS.errors.conference;
+function initConference(connection, roomName) {
+ var room = connection.initJitsiConference(roomName, {
+ openSctp: config.openSctp,
+ disableAudioLevels: config.disableAudioLevels
+ });
+
+ var users = {};
+ var localTracks = [];
+
+ APP.conference.localId = room.myUserId();
+ Object.defineProperty(APP.conference, "membersCount", {
+ get: function () {
+ return Object.keys(users).length; // FIXME maybe +1?
+ }
+ });
+
+ room.on(ConferenceEvents.USER_JOINED, function (id) {
+ users[id] = {
+ displayName: undefined,
+ tracks: []
+ };
+ // FIXME email???
+ APP.UI.addUser(id);
+ });
+ room.on(ConferenceEvents.USER_LEFT, function (id) {
+ delete users[id];
+ APP.UI.removeUser(id);
+ });
+
+
+ room.on(ConferenceEvents.TRACK_MUTE_CHANGED, function (track) {
+ // FIXME handle mute
+ });
+ room.on(ConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, function (id, lvl) {
+ APP.UI.setAudioLevel(id, lvl);
+ });
+ APP.UI.addListener(UIEvents.AUDIO_MUTED, function (muted) {
+ // FIXME mute or unmute
+ APP.UI.setAudioMuted(muted);
+ APP.conference.audioMuted = muted;
+ });
+ APP.UI.addListener(UIEvents.VIDEO_MUTED, function (muted) {
+ // FIXME mute or unmute
+ APP.UI.setVideoMuted(muted);
+ APP.conference.videoMuted = muted;
+ });
+
+
+ room.on(ConferenceEvents.IN_LAST_N_CHANGED, function (inLastN) {
+ if (config.muteLocalVideoIfNotInLastN) {
+ // TODO mute or unmute if required
+ // mark video on UI
+ // APP.UI.markVideoMuted(true/false);
+ }
+ });
+ room.on(ConferenceEvents.LAST_N_ENDPOINTS_CHANGED, function (ids) {
+ APP.UI.handleLastNEndpoints(ids);
+ });
+ room.on(ConferenceEvents.ACTIVE_SPEAKER_CHANGED, function (id) {
+ APP.UI.markDominantSpiker(id);
+ });
+
+
+ room.on(ConferenceEvents.CONNECTION_INTERRUPTED, function () {
+ APP.UI.markVideoInterrupted(true);
+ });
+ room.on(ConferenceEvents.CONNECTION_RESTORED, function () {
+ APP.UI.markVideoInterrupted(false);
+ });
+
+
+ APP.connectionquality.addListener(
+ CQEvents.LOCALSTATS_UPDATED,
+ function (percent, stats) {
+ APP.UI.updateLocalStats(percent, stats);
+
+ // send local stats to other users
+ room.sendCommand(Commands.CONNECTION_QUALITY, {
+ value: APP.connectionquality.convertToMUCStats(stats),
+ attributes: {
+ id: room.myUserId()
+ }
+ });
+ }
+ );
+ APP.connectionquality.addListener(CQEvents.STOP, function () {
+ APP.UI.hideStats();
+ room.removeCommand(Commands.CONNECTION_QUALITY);
+ });
+ // listen to remote stats
+ room.addCommandListener(Commands.CONNECTION_QUALITY, function (data) {
+ APP.connectionquality.updateRemoteStats(data.attributes.id, data.value);
+ });
+ APP.connectionquality.addListener(
+ CQEvents.REMOTESTATS_UPDATED,
+ function (id, percent, stats) {
+ APP.UI.updateRemoteStats(id, percent, stats);
+ }
+ );
+
+
+ // share email with other users
+ function sendEmail(email) {
+ room.sendCommand(Commands.EMAIL, {
+ value: email,
+ attributes: {
+ id: room.myUserId()
+ }
+ });
+ }
+
+ var email = APP.settings.getEmail();
+ email && sendEmail(email);
+ APP.UI.addListener(UIEvents.EMAIL_CHANGED, function (email) {
+ APP.settings.setEmail(email);
+ APP.UI.setUserAvatar(room.myUserId(), email);
+ sendEmail(email);
+ });
+ room.addCommandListener(Commands.EMAIL, function (data) {
+ APP.UI.setUserAvatar(data.attributes.id, data.value);
+ });
+
+
+ room.on(ConferenceEvents.DISPLAY_NAME_CHANGED, function (id, displayName) {
+ APP.UI.changeDisplayName(id, displayName);
+ });
+ APP.UI.addListener(UIEvents.NICKNAME_CHANGED, function (nickname) {
+ APP.settings.setDisplayName(nickname);
+ room.setDisplayName(nickname);
+ });
+
+
+ APP.UI.addListener(UIEvents.MESSAGE_CREATED, function (message) {
+ room.sendTextMessage(message);
+ });
+
+
+ room.on(ConferenceErrors.PASSWORD_REQUIRED, function () {
+ // FIXME handle
+ });
+ room.on(ConferenceErrors.CONNECTION_ERROR, function () {
+ // FIXME handle
+ });
+
+ APP.UI.addListener(
+ UIEvents.START_MUTED_CHANGED,
+ function (startAudioMuted, startVideoMuted) {
+ // FIXME start muted
+ }
+ );
+
+ return new Promise(function (resolve, reject) {
+ room.on(
+ ConferenceEvents.CONFERENCE_JOINED,
+ function () {
+ resolve();
+ }
+ );
+ APP.UI.closeAuthenticationDialog();
+ if (config.useNicks) {
+ // FIXME check this
+ var nick = APP.UI.askForNickname();
+ }
+ room.join();
+ }).catch(function (err) {
+ if (err[0] === ConferenceErrors.PASSWORD_REQUIRED) {
+ // FIXME ask for password and try again
+ return initConference(connection, roomName);
+ }
+
+ // FIXME else notify that we cannot conenct to the room
+
+ throw new Error(err[0]);
+ });
+}
+
+function init() {
+ connect().then(function (connection) {
+ return initConference(connection, APP.UI.generateRoomName());
+ }).then(function () {
+ APP.UI.start();
+
+ APP.UI.initConference();
+
+ APP.UI.addListener(UIEvents.LANG_CHANGED, function (language) {
+ APP.translation.setLanguage(language);
+ APP.settings.setLanguage(language);
+ });
+
+ APP.desktopsharing.init();
+ APP.statistics.start();
+ APP.connectionquality.init();
+ APP.keyboardshortcut.init();
+ });
}
/**
@@ -90,17 +367,17 @@ $(document).ready(function () {
APP.translation.init();
- if(APP.API.isEnabled())
+ if (APP.API.isEnabled()) {
APP.API.init();
+ }
- APP.UI.start(obtainConfigAndInit);
-
+ obtainConfigAndInit();
});
$(window).bind('beforeunload', function () {
- if(APP.API.isEnabled())
+ if (APP.API.isEnabled()) {
APP.API.dispose();
+ }
});
module.exports = APP;
-
diff --git a/index.html b/index.html
index 93681fc6d..7f48e5e80 100644
--- a/index.html
+++ b/index.html
@@ -14,6 +14,7 @@
+