<commit_before>/* global $, APP, JitsiMeetJS, config, interfaceConfig */
import {openConnection} from './connection';
//FIXME:
import createRoomLocker from './modules/UI/authentication/RoomLocker';
//FIXME:
import AuthHandler from './modules/UI/authentication/AuthHandler';

import ConnectionQuality from './modules/connectionquality/connectionquality';

import CQEvents from './service/connectionquality/CQEvents';
import UIEvents from './service/UI/UIEvents';

const ConnectionEvents = JitsiMeetJS.events.connection;
const ConnectionErrors = JitsiMeetJS.errors.connection;

const ConferenceEvents = JitsiMeetJS.events.conference;
const ConferenceErrors = JitsiMeetJS.errors.conference;

const TrackEvents = JitsiMeetJS.events.track;
const TrackErrors = JitsiMeetJS.errors.track;

let room, connection, localAudio, localVideo, roomLocker;

/**
 * Known custom conference commands.
 */
const Commands = {
    CONNECTION_QUALITY: "stats",
    EMAIL: "email",
    ETHERPAD: "etherpad",
    SHARED_VIDEO: "shared-video"
};

import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/LargeVideo";

/**
 * Open Connection. When authentication failed it shows auth dialog.
 * @param roomName the room name to use
 * @returns Promise<JitsiConnection>
 */
function connect(roomName) {
    return openConnection({retry: true, roomName: roomName})
            .catch(function (err) {
        if (err === ConnectionErrors.PASSWORD_REQUIRED) {
            APP.UI.notifyTokenAuthFailed();
        } else {
            APP.UI.notifyConnectionFailed(err);
        }
        throw err;
    });
}

/**
 * Share email with other users.
 * @param {string} email new email
 */
function sendEmail (email) {
    room.sendCommand(Commands.EMAIL, {
        value: email,
        attributes: {
            id: room.myUserId()
        }
    });
}

/**
 * Get user nickname by user id.
 * @param {string} id user id
 * @returns {string?} user nickname or undefined if user is unknown.
 */
function getDisplayName (id) {
    if (APP.conference.isLocalId(id)) {
        return APP.settings.getDisplayName();
    }

    let participant = room.getParticipantById(id);
    if (participant && participant.getDisplayName()) {
        return participant.getDisplayName();
    }
}

/**
 * Mute or unmute local audio stream if it exists.
 * @param {boolean} muted if audio stream should be muted or unmuted.
 */
function muteLocalAudio (muted) {
    if (!localAudio) {
        return;
    }

    if (muted) {
        localAudio.mute().then(function(value) {},
            function(value) {
                console.warn('Audio Mute was rejected:', value);
            }
        );
    } else {
        localAudio.unmute().then(function(value) {},
            function(value) {
                console.warn('Audio unmute was rejected:', value);
            }
        );
    }
}

/**
 * Mute or unmute local video stream if it exists.
 * @param {boolean} muted if video stream should be muted or unmuted.
 */
function muteLocalVideo (muted) {
    if (!localVideo) {
        return;
    }

    if (muted) {
        localVideo.mute().then(function(value) {},
            function(value) {
                console.warn('Video mute was rejected:', value);
            }
        );
    } else {
        localVideo.unmute().then(function(value) {},
            function(value) {
                console.warn('Video unmute was rejected:', value);
            }
        );
    }
}

/**
 * Disconnect from the conference and optionally request user feedback.
 * @param {boolean} [requestFeedback=false] if user feedback should be requested
 */
function hangup (requestFeedback = false) {
    let promise = Promise.resolve();

    if (requestFeedback) {
        promise = APP.UI.requestFeedback();
    }

    promise.then(function () {
        connection.disconnect();

        if (!config.enableWelcomePage) {
            return;
        }
        // redirect to welcome page
        setTimeout(() => {
            APP.settings.setWelcomePageEnabled(true);
            window.location.pathname = "/";
        }, 3000);
    }, function (err) {
        console.error('Failed to hangup the call:', err);
    });
}

/**
 * Create local tracks of specified types.
 * @param {string[]} devices required track types ('audio', 'video' etc.)
 * @returns {Promise<JitsiLocalTrack[]>}
 */
function createLocalTracks (...devices) {
    return JitsiMeetJS.createLocalTracks({
        // copy array to avoid mutations inside library
        devices: devices.slice(0),
        resolution: config.resolution,
        cameraDeviceId: APP.settings.getCameraDeviceId(),
        micDeviceId: APP.settings.getMicDeviceId(),
        // adds any ff fake device settings if any
        firefox_fake_device: config.firefox_fake_device
    }).catch(function (err) {
        console.error('failed to create local tracks', ...devices, err);
        return Promise.reject(err);
    });
}

class ConferenceConnector {
    constructor(resolve, reject) {
        this._resolve = resolve;
        this._reject = reject;
        this.reconnectTimeout = null;
        room.on(ConferenceEvents.CONFERENCE_JOINED,
            this._handleConferenceJoined.bind(this));
        room.on(ConferenceEvents.CONFERENCE_FAILED,
            this._onConferenceFailed.bind(this));
        room.on(ConferenceEvents.CONFERENCE_ERROR,
            this._onConferenceError.bind(this));
    }
    _handleConferenceFailed(err, msg) {
        this._unsubscribe();
        this._reject(err);
    }
    _onConferenceFailed(err, ...params) {
        console.error('CONFERENCE FAILED:', err, ...params);
        switch (err) {
            // room is locked by the password
        case ConferenceErrors.PASSWORD_REQUIRED:
            APP.UI.markRoomLocked(true);
            roomLocker.requirePassword().then(function () {
                room.join(roomLocker.password);
            });
            break;

        case ConferenceErrors.CONNECTION_ERROR:
            {
                let [msg] = params;
                APP.UI.notifyConnectionFailed(msg);
            }
            break;

        case ConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE:
            APP.UI.notifyBridgeDown();
            break;

            // not enough rights to create conference
        case ConferenceErrors.AUTHENTICATION_REQUIRED:
            // schedule reconnect to check if someone else created the room
            this.reconnectTimeout = setTimeout(function () {
                room.join();
            }, 5000);

            // notify user that auth is required

            AuthHandler.requireAuth(room, roomLocker.password);
            break;

        case ConferenceErrors.RESERVATION_ERROR:
            {
                let [code, msg] = params;
                APP.UI.notifyReservationError(code, msg);
            }
            break;

        case ConferenceErrors.GRACEFUL_SHUTDOWN:
            APP.UI.notifyGracefulShutdown();
            break;

        case ConferenceErrors.JINGLE_FATAL_ERROR:
            APP.UI.notifyInternalError();
            break;

        case ConferenceErrors.CONFERENCE_DESTROYED:
            {
                let [reason] = params;
                APP.UI.hideStats();
                APP.UI.notifyConferenceDestroyed(reason);
            }
            break;

        case ConferenceErrors.FOCUS_DISCONNECTED:
            {
                let [focus, retrySec] = params;
                APP.UI.notifyFocusDisconnected(focus, retrySec);
            }
            break;

        case ConferenceErrors.FOCUS_LEFT:
            room.leave().then(() => connection.disconnect());
            APP.UI.notifyFocusLeft();
            break;

        case ConferenceErrors.CONFERENCE_MAX_USERS:
            connection.disconnect();
            APP.UI.notifyMaxUsersLimitReached();
            break;
        default:
            this._handleConferenceFailed(err, ...params);
        }
    }
    _onConferenceError(err, ...params) {
        console.error('CONFERENCE Error:', err, params);
        switch (err) {
        case ConferenceErrors.CHAT_ERROR:
            {
                let [code, msg] = params;
                APP.UI.showChatError(code, msg);
            }
            break;
        default:
            console.error("Unknown error.");
        }
    }
    _unsubscribe() {
        room.off(
            ConferenceEvents.CONFERENCE_JOINED, this._handleConferenceJoined);
        room.off(
            ConferenceEvents.CONFERENCE_FAILED, this._onConferenceFailed);
        if (this.reconnectTimeout !== null) {
            clearTimeout(this.reconnectTimeout);
        }
        AuthHandler.closeAuth();
    }
    _handleConferenceJoined() {
        this._unsubscribe();
        this._resolve();
    }
    connect() {
        room.join();
    }
}

export default {
    localId: undefined,
    isModerator: false,
    audioMuted: false,
    videoMuted: false,
    isSharingScreen: false,
    isDesktopSharingEnabled: false,
    /**
     * Open new connection and join to the conference.
     * @param {object} options
     * @param {string} roomName name of the conference
     * @returns {Promise}
     */
    init(options) {
        this.roomName = options.roomName;
        JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);

        // attaches global error handler
        window.onerror = JitsiMeetJS.getGlobalOnErrorHandler;

        return JitsiMeetJS.init(config).then(() => {
            return Promise.all([<commit_msg>Adds a check and reuses any previous onerror handlers if they exist.<commit_after>/* global $, APP, JitsiMeetJS, config, interfaceConfig */
import {openConnection} from './connection';
//FIXME:
import createRoomLocker from './modules/UI/authentication/RoomLocker';
//FIXME:
import AuthHandler from './modules/UI/authentication/AuthHandler';

import ConnectionQuality from './modules/connectionquality/connectionquality';

import CQEvents from './service/connectionquality/CQEvents';
import UIEvents from './service/UI/UIEvents';

const ConnectionEvents = JitsiMeetJS.events.connection;
const ConnectionErrors = JitsiMeetJS.errors.connection;

const ConferenceEvents = JitsiMeetJS.events.conference;
const ConferenceErrors = JitsiMeetJS.errors.conference;

const TrackEvents = JitsiMeetJS.events.track;
const TrackErrors = JitsiMeetJS.errors.track;

let room, connection, localAudio, localVideo, roomLocker;

/**
 * Known custom conference commands.
 */
const Commands = {
    CONNECTION_QUALITY: "stats",
    EMAIL: "email",
    ETHERPAD: "etherpad",
    SHARED_VIDEO: "shared-video"
};

import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/LargeVideo";

/**
 * Open Connection. When authentication failed it shows auth dialog.
 * @param roomName the room name to use
 * @returns Promise<JitsiConnection>
 */
function connect(roomName) {
    return openConnection({retry: true, roomName: roomName})
            .catch(function (err) {
        if (err === ConnectionErrors.PASSWORD_REQUIRED) {
            APP.UI.notifyTokenAuthFailed();
        } else {
            APP.UI.notifyConnectionFailed(err);
        }
        throw err;
    });
}

/**
 * Share email with other users.
 * @param {string} email new email
 */
function sendEmail (email) {
    room.sendCommand(Commands.EMAIL, {
        value: email,
        attributes: {
            id: room.myUserId()
        }
    });
}

/**
 * Get user nickname by user id.
 * @param {string} id user id
 * @returns {string?} user nickname or undefined if user is unknown.
 */
function getDisplayName (id) {
    if (APP.conference.isLocalId(id)) {
        return APP.settings.getDisplayName();
    }

    let participant = room.getParticipantById(id);
    if (participant && participant.getDisplayName()) {
        return participant.getDisplayName();
    }
}

/**
 * Mute or unmute local audio stream if it exists.
 * @param {boolean} muted if audio stream should be muted or unmuted.
 */
function muteLocalAudio (muted) {
    if (!localAudio) {
        return;
    }

    if (muted) {
        localAudio.mute().then(function(value) {},
            function(value) {
                console.warn('Audio Mute was rejected:', value);
            }
        );
    } else {
        localAudio.unmute().then(function(value) {},
            function(value) {
                console.warn('Audio unmute was rejected:', value);
            }
        );
    }
}

/**
 * Mute or unmute local video stream if it exists.
 * @param {boolean} muted if video stream should be muted or unmuted.
 */
function muteLocalVideo (muted) {
    if (!localVideo) {
        return;
    }

    if (muted) {
        localVideo.mute().then(function(value) {},
            function(value) {
                console.warn('Video mute was rejected:', value);
            }
        );
    } else {
        localVideo.unmute().then(function(value) {},
            function(value) {
                console.warn('Video unmute was rejected:', value);
            }
        );
    }
}

/**
 * Disconnect from the conference and optionally request user feedback.
 * @param {boolean} [requestFeedback=false] if user feedback should be requested
 */
function hangup (requestFeedback = false) {
    let promise = Promise.resolve();

    if (requestFeedback) {
        promise = APP.UI.requestFeedback();
    }

    promise.then(function () {
        connection.disconnect();

        if (!config.enableWelcomePage) {
            return;
        }
        // redirect to welcome page
        setTimeout(() => {
            APP.settings.setWelcomePageEnabled(true);
            window.location.pathname = "/";
        }, 3000);
    }, function (err) {
        console.error('Failed to hangup the call:', err);
    });
}

/**
 * Create local tracks of specified types.
 * @param {string[]} devices required track types ('audio', 'video' etc.)
 * @returns {Promise<JitsiLocalTrack[]>}
 */
function createLocalTracks (...devices) {
    return JitsiMeetJS.createLocalTracks({
        // copy array to avoid mutations inside library
        devices: devices.slice(0),
        resolution: config.resolution,
        cameraDeviceId: APP.settings.getCameraDeviceId(),
        micDeviceId: APP.settings.getMicDeviceId(),
        // adds any ff fake device settings if any
        firefox_fake_device: config.firefox_fake_device
    }).catch(function (err) {
        console.error('failed to create local tracks', ...devices, err);
        return Promise.reject(err);
    });
}

class ConferenceConnector {
    constructor(resolve, reject) {
        this._resolve = resolve;
        this._reject = reject;
        this.reconnectTimeout = null;
        room.on(ConferenceEvents.CONFERENCE_JOINED,
            this._handleConferenceJoined.bind(this));
        room.on(ConferenceEvents.CONFERENCE_FAILED,
            this._onConferenceFailed.bind(this));
        room.on(ConferenceEvents.CONFERENCE_ERROR,
            this._onConferenceError.bind(this));
    }
    _handleConferenceFailed(err, msg) {
        this._unsubscribe();
        this._reject(err);
    }
    _onConferenceFailed(err, ...params) {
        console.error('CONFERENCE FAILED:', err, ...params);
        switch (err) {
            // room is locked by the password
        case ConferenceErrors.PASSWORD_REQUIRED:
            APP.UI.markRoomLocked(true);
            roomLocker.requirePassword().then(function () {
                room.join(roomLocker.password);
            });
            break;

        case ConferenceErrors.CONNECTION_ERROR:
            {
                let [msg] = params;
                APP.UI.notifyConnectionFailed(msg);
            }
            break;

        case ConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE:
            APP.UI.notifyBridgeDown();
            break;

            // not enough rights to create conference
        case ConferenceErrors.AUTHENTICATION_REQUIRED:
            // schedule reconnect to check if someone else created the room
            this.reconnectTimeout = setTimeout(function () {
                room.join();
            }, 5000);

            // notify user that auth is required

            AuthHandler.requireAuth(room, roomLocker.password);
            break;

        case ConferenceErrors.RESERVATION_ERROR:
            {
                let [code, msg] = params;
                APP.UI.notifyReservationError(code, msg);
            }
            break;

        case ConferenceErrors.GRACEFUL_SHUTDOWN:
            APP.UI.notifyGracefulShutdown();
            break;

        case ConferenceErrors.JINGLE_FATAL_ERROR:
            APP.UI.notifyInternalError();
            break;

        case ConferenceErrors.CONFERENCE_DESTROYED:
            {
                let [reason] = params;
                APP.UI.hideStats();
                APP.UI.notifyConferenceDestroyed(reason);
            }
            break;

        case ConferenceErrors.FOCUS_DISCONNECTED:
            {
                let [focus, retrySec] = params;
                APP.UI.notifyFocusDisconnected(focus, retrySec);
            }
            break;

        case ConferenceErrors.FOCUS_LEFT:
            room.leave().then(() => connection.disconnect());
            APP.UI.notifyFocusLeft();
            break;

        case ConferenceErrors.CONFERENCE_MAX_USERS:
            connection.disconnect();
            APP.UI.notifyMaxUsersLimitReached();
            break;
        default:
            this._handleConferenceFailed(err, ...params);
        }
    }
    _onConferenceError(err, ...params) {
        console.error('CONFERENCE Error:', err, params);
        switch (err) {
        case ConferenceErrors.CHAT_ERROR:
            {
                let [code, msg] = params;
                APP.UI.showChatError(code, msg);
            }
            break;
        default:
            console.error("Unknown error.");
        }
    }
    _unsubscribe() {
        room.off(
            ConferenceEvents.CONFERENCE_JOINED, this._handleConferenceJoined);
        room.off(
            ConferenceEvents.CONFERENCE_FAILED, this._onConferenceFailed);
        if (this.reconnectTimeout !== null) {
            clearTimeout(this.reconnectTimeout);
        }
        AuthHandler.closeAuth();
    }
    _handleConferenceJoined() {
        this._unsubscribe();
        this._resolve();
    }
    connect() {
        room.join();
    }
}

export default {
    localId: undefined,
    isModerator: false,
    audioMuted: false,
    videoMuted: false,
    isSharingScreen: false,
    isDesktopSharingEnabled: false,
    /**
     * Open new connection and join to the conference.
     * @param {object} options
     * @param {string} roomName name of the conference
     * @returns {Promise}
     */
    init(options) {
        this.roomName = options.roomName;
        JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);

        // attaches global error handler, if there is already one, respect it
        if(JitsiMeetJS.getGlobalOnErrorHandler){
            var oldOnErrorHandler = window.onerror;
            window.onerror = function (message, source, lineno, colno, error) {

                JitsiMeetJS.getGlobalOnErrorHandler(
                    message, source, lineno, colno, error);

                if(oldOnErrorHandler)
                    oldOnErrorHandler(message, source, lineno, colno, error);
            };
        }

        return JitsiMeetJS.init(config).then(() => {
            return Promise.all([
{'fraction_non_alphanumeric': 0.10810950146929937, 'fraction_numerical': 0.000927978553384544, 'mean_word_length': 2.1784368343437652, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '173417', 'n_tokens_mistral': 5475, 'n_tokens_neox': 5297, 'n_words': 1184}