Call.js

/**
 * This class describes the information and current status of a call.
 */
export default class Call {

    constructor({
            id, callId, accountId,
            localContact, localUri, remoteContact, remoteUri,
            state, stateText, held, muted, speaker,
            connectDuration, totalDuration,
            remoteOfferer, remoteAudioCount, remoteVideoCount, audioCount, videoCount,
            lastStatusCode, lastReason, media, provisionalMedia
        }) {
        let remoteNumber = null;
        let remoteName = null;

        if (remoteUri) {
            let match = remoteUri.match(/"([^"]+)" <sip:([^@]+)@/);

            if (match) {
                remoteName = match[1];
                remoteNumber = match[2];
            } else {
                match = remoteUri.match(/sip:([^@]+)@/);

                if (match) {
                    remoteNumber = match[1];
                }
            }
        }

        this._id = id;
        this._callId = callId;
        this._accountId = accountId;
        this._localContact = localContact;
        this._localUri = localUri;
        this._remoteContact = remoteContact;
        this._remoteUri = remoteUri;
        this._state = state;
        this._stateText = stateText;
        this._held = held;
        this._muted = muted;
        this._speaker = speaker;
        this._connectDuration = connectDuration;
        this._totalDuration = totalDuration;
        this._remoteOfferer = remoteOfferer;
        this._remoteAudioCount = remoteAudioCount;
        this._remoteVideoCount = remoteVideoCount;
        this._remoteNumber = remoteNumber;
        this._remoteName = remoteName;
        this._audioCount = audioCount;
        this._videoCount = videoCount;
        this._lastStatusCode = lastStatusCode;
        this._lastReason = lastReason;

        this._media = media;
        this._provisionalMedia = provisionalMedia;

        this._constructionTime = Math.round(new Date().getTime() / 1000);
    }

    /**
     * Call identification.
     * @returns {int}
     */
    getId() {
        return this._id;
    }

    /**
     * The account ID where this call belongs.
     * @returns {int}
     */
    getAccountId() {
        return this._accountId;
    }

    /**
     * Dialog Call-ID string.
     *
     * @returns {String}
     */
    getCallId() {
        return this._callId;
    }


    /**
     * Up-to-date call duration in seconds.
     * Use local time to calculate actual call duration.
     *
     * @public
     * @returns {int}
     */
    getTotalDuration() {
        let time = Math.round(new Date().getTime() / 1000);
        let offset = time - this._constructionTime;

        return this._totalDuration + offset;
    };

    /**
     * Up-to-date call connected duration (zero when call is not established)
     *
     * @returns {int}
     */
    getConnectDuration() {
        if (this._connectDuration < 0 || this._state == "PJSIP_INV_STATE_DISCONNECTED") {
            return this._connectDuration;
        }

        let time = Math.round(new Date().getTime() / 1000);
        let offset = time - this._constructionTime;

        return this._connectDuration + offset;
    }

    /**
     * Call duration in "MM:SS" format.
     *
     * @public
     * @returns {string}
     */
    getFormattedTotalDuration() {
        return this._formatTime(this.getTotalDuration());
    };

    /**
     * Call duration in "MM:SS" format.
     *
     * @public
     * @returns {string}
     */
    getFormattedConnectDuration() {
        return this._formatTime(this.getConnectDuration());
    };

    /**
     * Local Contact.
     * TODO: Provide example
     * @returns {String}
     */
    getLocalContact() {
        return this._localContact;
    }

    /**
     * Local URI.
     * TODO: Provide example
     * @returns {String}
     */
    getLocalUri() {
        return this._localUri;
    }

    /**
     * Remote contact.
     * TODO: Provide example
     * @returns {String}
     */
    getRemoteContact() {
        return this._remoteContact;
    }

    /**
     * Remote URI.
     * TODO: Provide example
     * @returns {String}
     */
    getRemoteUri() {
        return this._remoteUri;
    }

    /**
     * Callee name. Could be null if no name specified in URI.
     * @returns {String}
     */
    getRemoteName() {
        return this._remoteName;
    }

    /**
     * Callee number
     * @returns {String}
     */
    getRemoteNumber() {
        return this._remoteNumber;
    }

    /**
     * @returns {String}
     */
    getRemoteFormattedNumber() {
        if (this._remoteName && this._remoteNumber) {
            return `${this._remoteName} <${this._remoteNumber}>`;
        } else if (this._remoteNumber) {
            return this._remoteNumber;
        } else {
            return this._remoteUri
        }
    }

    /**
     * Invite session state.
     *
     * PJSIP_INV_STATE_NULL           Before INVITE is sent or received
     * PJSIP_INV_STATE_CALLING        After INVITE is sent
     * PJSIP_INV_STATE_INCOMING       After INVITE is received.
     * PJSIP_INV_STATE_EARLY          After response with To tag.
     * PJSIP_INV_STATE_CONNECTING     After 2xx is sent/received.
     * PJSIP_INV_STATE_CONFIRMED      After ACK is sent/received.
     * PJSIP_INV_STATE_DISCONNECTED   Session is terminated.
     *
     * @returns {String}
     */
    getState() {
        return this._state;
    }

    /**
     * Text describing the state.
     *
     * @returns {String}
     */
    getStateText() {
        return this._stateText;
    }

    isHeld() {
        return this._held;
    }

    isMuted() {
        return this._muted;
    }

    isSpeaker() {
        return this._speaker;
    }

    isTerminated() {
        return this._state === 'PJSIP_INV_STATE_DISCONNECTED';
    }

    /**
     * Flag if remote was SDP offerer
     * @returns {boolean}
     */
    getRemoteOfferer() {
        // TODO Verify whether boolean value
        return this._remoteOfferer;
    }

    /**
     * Number of audio streams offered by remote.
     * @returns {int}
     */
    getRemoteAudioCount() {
        return this._remoteAudioCount;
    }

    /**
     * Number of video streams offered by remote.
     * @returns {int}
     */
    getRemoteVideoCount() {
        return this._remoteVideoCount;
    }

    /**
     * Number of simultaneous active audio streams for this call. If zero - audio is disabled in this call.
     * @returns {int}
     */
    getAudioCount() {
        return this._audioCount;
    }

    /**
     * Number of simultaneous active video streams for this call. If zero - video is disabled in this call.
     * @returns {*}
     */
    getVideoCount() {
        return this._videoCount;
    }

    /**
     * Last status code heard, which can be used as cause code.
     * Possible values:
     * - PJSIP_SC_TRYING / 100
     * - PJSIP_SC_RINGING / 180
     * - PJSIP_SC_CALL_BEING_FORWARDED / 181
     * - PJSIP_SC_QUEUED / 182
     * - PJSIP_SC_PROGRESS / 183
     * - PJSIP_SC_OK / 200
     * - PJSIP_SC_ACCEPTED / 202
     * - PJSIP_SC_MULTIPLE_CHOICES / 300
     * - PJSIP_SC_MOVED_PERMANENTLY / 301
     * - PJSIP_SC_MOVED_TEMPORARILY / 302
     * - PJSIP_SC_USE_PROXY / 305
     * - PJSIP_SC_ALTERNATIVE_SERVICE / 380
     * - PJSIP_SC_BAD_REQUEST / 400
     * - PJSIP_SC_UNAUTHORIZED / 401
     * - PJSIP_SC_PAYMENT_REQUIRED / 402
     * - PJSIP_SC_FORBIDDEN / 403
     * - PJSIP_SC_NOT_FOUND / 404
     * - PJSIP_SC_METHOD_NOT_ALLOWED / 405
     * - PJSIP_SC_NOT_ACCEPTABLE / 406
     * - PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED / 407
     * - PJSIP_SC_REQUEST_TIMEOUT / 408
     * - PJSIP_SC_GONE / 410
     * - PJSIP_SC_REQUEST_ENTITY_TOO_LARGE / 413
     * - PJSIP_SC_REQUEST_URI_TOO_LONG / 414
     * - PJSIP_SC_UNSUPPORTED_MEDIA_TYPE / 415
     * - PJSIP_SC_UNSUPPORTED_URI_SCHEME / 416
     * - PJSIP_SC_BAD_EXTENSION / 420
     * - PJSIP_SC_EXTENSION_REQUIRED / 421
     * - PJSIP_SC_SESSION_TIMER_TOO_SMALL / 422
     * - PJSIP_SC_INTERVAL_TOO_BRIEF / 423
     * - PJSIP_SC_TEMPORARILY_UNAVAILABLE / 480
     * - PJSIP_SC_CALL_TSX_DOES_NOT_EXIST / 481
     * - PJSIP_SC_LOOP_DETECTED / 482
     * - PJSIP_SC_TOO_MANY_HOPS / 483
     * - PJSIP_SC_ADDRESS_INCOMPLETE / 484
     * - PJSIP_AC_AMBIGUOUS / 485
     * - PJSIP_SC_BUSY_HERE / 486
     * - PJSIP_SC_REQUEST_TERMINATED / 487
     * - PJSIP_SC_NOT_ACCEPTABLE_HERE / 488
     * - PJSIP_SC_BAD_EVENT / 489
     * - PJSIP_SC_REQUEST_UPDATED / 490
     * - PJSIP_SC_REQUEST_PENDING / 491
     * - PJSIP_SC_UNDECIPHERABLE / 493
     * - PJSIP_SC_INTERNAL_SERVER_ERROR / 500
     * - PJSIP_SC_NOT_IMPLEMENTED / 501
     * - PJSIP_SC_BAD_GATEWAY / 502
     * - PJSIP_SC_SERVICE_UNAVAILABLE / 503
     * - PJSIP_SC_SERVER_TIMEOUT / 504
     * - PJSIP_SC_VERSION_NOT_SUPPORTED / 505
     * - PJSIP_SC_MESSAGE_TOO_LARGE / 513
     * - PJSIP_SC_PRECONDITION_FAILURE / 580
     * - PJSIP_SC_BUSY_EVERYWHERE / 600
     * - PJSIP_SC_DECLINE / 603
     * - PJSIP_SC_DOES_NOT_EXIST_ANYWHERE / 604
     * - PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE / 606
     * - PJSIP_SC_TSX_TIMEOUT / PJSIP_SC_REQUEST_TIMEOUT
     * - PJSIP_SC_TSX_TRANSPORT_ERROR / PJSIP_SC_SERVICE_UNAVAILABLE
     *
     * @returns {string}
     */
    getLastStatusCode() {
        return this._lastStatusCode;
    }

    /**
     * The reason phrase describing the last status.
     *
     * @returns {string}
     */
    getLastReason() {
        return this._lastReason;
    }

    getMedia() {
        return this._media;
    }

    getProvisionalMedia() {
        return this._provisionalMedia;
    }

    /**
     * Format seconds to "MM:SS" format.
     *
     * @public
     * @returns {string}
     */
    _formatTime(seconds) {
        if (isNaN(seconds) || seconds < 0) {
            return "00:00";
        }
        var hours = parseInt( seconds / 3600 ) % 24;
        var minutes = parseInt( seconds / 60 ) % 60;
        var result = "";
        seconds = seconds % 60;

        if (hours > 0) {
            result += (hours < 10 ? "0" + hours : hours) + ":";
        }

        result += (minutes < 10 ? "0" + minutes : minutes) + ":" + (seconds  < 10 ? "0" + seconds : seconds);
        return result;
    };
}