JavaScript

Both in VERTO and in SIP.js flavor, the WebRTC Rooms application script is very similar.
Actually, VERTO flavor was the one written first, as a companion to the "Mastering FreeSWITCH" Packt book published on occasion of ClueCon 2016. In that book you can read an almost line by line discussion of it. This new version has some fixes and added features (automatic answer to incoming calls, better management of DTMF sending during a call, possibility to call non numeric extensions, and various polish).

$(window).load(function() { 
    cur_call = null; 
    chatting_with = false; 
    $("#conference").show(); 
    $("#ext").hide(); 
    $("#backbtn").hide(); 
    $("#cidname").hide(); 
    $("#callbtn").hide(); 
    $("#hupbtn").hide(); 
    $("#chatwin").hide(); 
    $("#chatmsg").hide(); 
    $("#chatsend").hide(); 
    $("#webcam").hide(); 
    $("#video1").hide(); 
    $("#login").keyup(function(event) { 
        if (event.keyCode == 13 && !event.shiftKey) { 
            $("#loginbtn").trigger("click"); 
        } 
    }); 
    $("#ext").keyup(function(event) { 
        if (event.keyCode == 13 && !event.shiftKey) { 
            $("#callbtn").trigger("click"); 
        } 
    }); 
}); 

At startup both flavor execute the "load" function, that sets which HTML elements are visible, and how to react when the user presses "Enter" while writing in the login or extension input area.

When the user fill the login input and presses Enter or click on the login button, the init() function is executed. Inside init() we create the main JavaScript object, our own WebRTC User Agent client. Let's look at the SIP.js flavor, VERTO is very much similar:

function init() { 
    cur_call = null; 
    chatting_with = false; 
 
    var nameHost; 
    var which_server; 
 
    nameHost = window.location.hostname; 
 
    which_server = "wss://" + nameHost + ":" + "3384"; 
    console.error("which_server=", which_server); 
 
    ua = new SIP.UA({ 
        wsServers: which_server, 
        uri: $("#login").val() + "@" + nameHost, 
        password: $("#passwd").val(), 
        userAgentString: 'SIP.js/0.7.7 Sara', 
        traceSip: true, 
    }); 
 
    $("#cidname").keyup(function(event) { 
        if (event.keyCode == 13 && !event.shiftKey) { 
            $("#callbtn").trigger("click"); 
        } 
    }); 
 
    setupChat(); 
 
    $(document).keypress(function(event) { 
        var key = String.fromCharCode(event.keyCode || event.charCode); 
        var i = parseInt(key); 
        var tag = event.target.tagName.toLowerCase(); 
 
        if (tag != 'input') { 
            if (key === "#" || key === "*" || key === "0" || (i > 0 && i <= 9)) { 
                cur_call.dtmf(key); 
            } 
        } 
    }); 
} 
 

We first made sure there are no calls and chat data structures left dangling, then establish which server we're connecting to (useful in case of massive scaling with domain based partitioning), then we use values we got from HTML to create our main object, in this case "ua".

We prepare the input field for the callee extension to react at Enter press.

Then we call the setupChat() function, which is slightly different in each flavor. This is because, in the VERTO flavor, we fill the "from" fields while in SIP.js those are automatically setup by the main object. The following is VERTO:

function setupChat() { 
    $("#chatwin").html(""); 
 
    $("#chatsend").click(function() { 
        if (!cur_call && chatting_with) { 
            return; 
        } 
        cur_call.message({ 
            to: chatting_with, 
            body: $("#chatmsg").val(), 
            from_msg_name: cur_call.params.caller_id_name, 
            from_msg_number: cur_call.params.caller_id_number 
        }); 
        $("#chatmsg").val(""); 
    }); 
 
    $("#chatmsg").keyup(function(event) { 
        if (event.keyCode == 13 && !event.shiftKey) { 
            $("#chatsend").trigger("click"); 
        } 
    }); 
} 
 

We then have the management of many buttons and situations: hangup, back, call, and so on.

The "call" button execute the function docall(), again, very similar in both flavors: you use the INVITE method in SIP.js (already!) and the newCall method on VERTO. They get more or less similar arguments: in VERTO you have setup callbacks for events when the main object is initialized, while in SIP.js you setup your callbacks now.

Let's look at the VERTO flavor:

function docall() { 
    if (cur_call) { 
        return; 
    } 
    cur_call = verto.newCall({ 
        destination_number: $("#ext").val(), 
        caller_id_name: $("#login").val(), 
        caller_id_number: $("#login").val(), 
        useVideo: true, 
        useStereo: true, 
        useCamera: 'any', 
        useSpeak: 'any', 
        useMic: 'any' 
    }); 
} 

The remaining parts of the scripts both setup their callbacks to react to incoming chat messages, to hangups, and to the establishment of the session (eg, remote peer answered).

Here's a screenshot of the FreeSWITCH console, showing chatplan info, SIP SIMPLE message, and Verto message. Yesss!

Let's look at the different ways to react to an incoming call (both flavors answer automatically for you).
SIP.js registered the function handleInvite as callback for when a call is incoming. Here's the implementation:

function handleInvite(s) { 
 
    cur_call = s; 
 
    s.accept({ 
        media: { 
            constraints: { 
                audio: true, 
                video: true 
            }, 
            render: { 
                remote: document.getElementById('webcam') 
            } 
        } 
    }); 
    $("#ext").val(s.remoteIdentity.uri.toString()); 
    cur_call.on('accepted', onAccepted.bind(cur_call)); 
    cur_call.once('bye', onTerminated.bind(cur_call)); 
    cur_call.once('failed', onTerminated.bind(cur_call)); 
    cur_call.once('cancel', onTerminated.bind(cur_call)); 
    $("#chatwin").html(""); 
} 
 

It accepts the call, gives its own constraints to the media negotiation, sets some callback for call events (success, remote hangup, and so on), then sets the contents of a couple of HTML fields.

The VERTO flavor script also registered callbacks to the incoming events and state changes (similar to a GUI message pump programming style):

    onDialogState: function(d) { 
        if (!cur_call) { 
            cur_call = d; 
        } 
        switch (d.state) { 
            case $.verto.enum.state.ringing: 
                console.error("RINGING"); 
 
                $("#webcam").show(); 
                $("#video1").show(); 
                cur_call.answer({ 
                    callee_id_name: "ciao", 
                    callee_id_number: "1234567", 
                    useVideo: true, 
                    useStereo: true, 
                    useCamera: true, 
                    useMic: true 
                }); 
                break;