freeswitch/html5/verto/js/src/jquery.verto.js

2860 lines
82 KiB
JavaScript

/*
* Verto HTML5/Javascript Telephony Signaling and Control Protocol Stack for FreeSWITCH
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Verto HTML5/Javascript Telephony Signaling and Control Protocol Stack for FreeSWITCH
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Anthony Minessale II <anthm@freeswitch.org>
*
* jquery.verto.js - Main interface
*
*/
(function($) {
var sources = [];
var generateGUID = (typeof(window.crypto) !== 'undefined' && typeof(window.crypto.getRandomValues) !== 'undefined') ?
function() {
// If we have a cryptographically secure PRNG, use that
// http://stackoverflow.com/questions/6906916/collisions-when-generating-uuids-in-javascript
var buf = new Uint16Array(8);
window.crypto.getRandomValues(buf);
var S4 = function(num) {
var ret = num.toString(16);
while (ret.length < 4) {
ret = "0" + ret;
}
return ret;
};
return (S4(buf[0]) + S4(buf[1]) + "-" + S4(buf[2]) + "-" + S4(buf[3]) + "-" + S4(buf[4]) + "-" + S4(buf[5]) + S4(buf[6]) + S4(buf[7]));
}
:
function() {
// Otherwise, just use Math.random
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
};
/// MASTER OBJ
$.verto = function(options, callbacks) {
var verto = this;
$.verto.saved.push(verto);
verto.options = $.extend({
login: null,
passwd: null,
socketUrl: null,
tag: null,
localTag: null,
videoParams: {},
audioParams: {},
loginParams: {},
deviceParams: {onResCheck: null},
userVariables: {},
iceServers: false,
ringSleep: 6000,
sessid: null
}, options);
if (verto.options.deviceParams.useCamera) {
$.FSRTC.getValidRes(verto.options.deviceParams.useCamera, verto.options.deviceParams.onResCheck);
}
if (!verto.options.deviceParams.useMic) {
verto.options.deviceParams.useMic = "any";
}
if (!verto.options.deviceParams.useSpeak) {
verto.options.deviceParams.useSpeak = "any";
}
if (verto.options.sessid) {
verto.sessid = verto.options.sessid;
} else {
verto.sessid = localStorage.getItem("verto_session_uuid") || generateGUID();
localStorage.setItem("verto_session_uuid", verto.sessid);
}
verto.dialogs = {};
verto.callbacks = callbacks || {};
verto.eventSUBS = {};
verto.rpcClient = new $.JsonRpcClient({
login: verto.options.login,
passwd: verto.options.passwd,
socketUrl: verto.options.socketUrl,
wsFallbackURL: verto.options.wsFallbackURL,
turnServer: verto.options.turnServer,
loginParams: verto.options.loginParams,
userVariables: verto.options.userVariables,
sessid: verto.sessid,
onmessage: function(e) {
return verto.handleMessage(e.eventData);
},
onWSConnect: function(o) {
o.call('login', {});
},
onWSLogin: function(success) {
if (verto.callbacks.onWSLogin) {
verto.callbacks.onWSLogin(verto, success);
}
},
onWSClose: function(success) {
if (verto.callbacks.onWSClose) {
verto.callbacks.onWSClose(verto, success);
}
verto.purge();
}
});
var tag = verto.options.tag;
if (typeof(tag) === "function") {
tag = tag();
}
if (verto.options.ringFile && verto.options.tag) {
verto.ringer = $("#" + tag);
}
verto.rpcClient.call('login', {});
};
$.verto.prototype.deviceParams = function(obj) {
var verto = this;
for (var i in obj) {
verto.options.deviceParams[i] = obj[i];
}
if (obj.useCamera) {
$.FSRTC.getValidRes(verto.options.deviceParams.useCamera, obj ? obj.onResCheck : undefined);
}
};
$.verto.prototype.videoParams = function(obj) {
var verto = this;
for (var i in obj) {
verto.options.videoParams[i] = obj[i];
}
};
$.verto.prototype.iceServers = function(obj) {
var verto = this;
verto.options.iceServers = obj;
};
$.verto.prototype.loginData = function(params) {
var verto = this;
verto.options.login = params.login;
verto.options.passwd = params.passwd;
verto.rpcClient.loginData(params);
};
$.verto.prototype.logout = function(msg) {
var verto = this;
verto.rpcClient.closeSocket();
if (verto.callbacks.onWSClose) {
verto.callbacks.onWSClose(verto, false);
}
verto.purge();
};
$.verto.prototype.login = function(msg) {
var verto = this;
verto.logout();
verto.rpcClient.call('login', {});
};
$.verto.prototype.message = function(msg) {
var verto = this;
var err = 0;
if (!msg.to) {
console.error("Missing To");
err++;
}
if (!msg.body) {
console.error("Missing Body");
err++;
}
if (err) {
return false;
}
verto.sendMethod("verto.info", {
msg: msg
});
return true;
};
$.verto.prototype.processReply = function(method, success, e) {
var verto = this;
var i;
//console.log("Response: " + method, success, e);
switch (method) {
case "verto.subscribe":
for (i in e.unauthorizedChannels) {
drop_bad(verto, e.unauthorizedChannels[i]);
}
for (i in e.subscribedChannels) {
mark_ready(verto, e.subscribedChannels[i]);
}
break;
case "verto.unsubscribe":
//console.error(e);
break;
}
};
$.verto.prototype.sendMethod = function(method, params) {
var verto = this;
verto.rpcClient.call(method, params,
function(e) {
/* Success */
verto.processReply(method, true, e);
},
function(e) {
/* Error */
verto.processReply(method, false, e);
});
};
function do_sub(verto, channel, obj) {
}
function drop_bad(verto, channel) {
console.error("drop unauthorized channel: " + channel);
delete verto.eventSUBS[channel];
}
function mark_ready(verto, channel) {
for (var j in verto.eventSUBS[channel]) {
verto.eventSUBS[channel][j].ready = true;
console.log("subscribed to channel: " + channel);
if (verto.eventSUBS[channel][j].readyHandler) {
verto.eventSUBS[channel][j].readyHandler(verto, channel);
}
}
}
var SERNO = 1;
function do_subscribe(verto, channel, subChannels, sparams) {
var params = sparams || {};
var local = params.local;
var obj = {
eventChannel: channel,
userData: params.userData,
handler: params.handler,
ready: false,
readyHandler: params.readyHandler,
serno: SERNO++
};
var isnew = false;
if (!verto.eventSUBS[channel]) {
verto.eventSUBS[channel] = [];
subChannels.push(channel);
isnew = true;
}
verto.eventSUBS[channel].push(obj);
if (local) {
obj.ready = true;
obj.local = true;
}
if (!isnew && verto.eventSUBS[channel][0].ready) {
obj.ready = true;
if (obj.readyHandler) {
obj.readyHandler(verto, channel);
}
}
return {
serno: obj.serno,
eventChannel: channel
};
}
$.verto.prototype.subscribe = function(channel, sparams) {
var verto = this;
var r = [];
var subChannels = [];
var params = sparams || {};
if (typeof(channel) === "string") {
r.push(do_subscribe(verto, channel, subChannels, params));
} else {
for (var i in channel) {
r.push(do_subscribe(verto, channel, subChannels, params));
}
}
if (subChannels.length) {
verto.sendMethod("verto.subscribe", {
eventChannel: subChannels.length == 1 ? subChannels[0] : subChannels,
subParams: params.subParams
});
}
return r;
};
$.verto.prototype.unsubscribe = function(handle) {
var verto = this;
var i;
if (!handle) {
for (i in verto.eventSUBS) {
if (verto.eventSUBS[i]) {
verto.unsubscribe(verto.eventSUBS[i]);
}
}
} else {
var unsubChannels = {};
var sendChannels = [];
var channel;
if (typeof(handle) == "string") {
delete verto.eventSUBS[handle];
unsubChannels[handle]++;
} else {
for (i in handle) {
if (typeof(handle[i]) == "string") {
channel = handle[i];
delete verto.eventSUBS[channel];
unsubChannels[channel]++;
} else {
var repl = [];
channel = handle[i].eventChannel;
for (var j in verto.eventSUBS[channel]) {
if (verto.eventSUBS[channel][j].serno == handle[i].serno) {} else {
repl.push(verto.eventSUBS[channel][j]);
}
}
verto.eventSUBS[channel] = repl;
if (verto.eventSUBS[channel].length === 0) {
delete verto.eventSUBS[channel];
unsubChannels[channel]++;
}
}
}
}
for (var u in unsubChannels) {
console.log("Sending Unsubscribe for: ", u);
sendChannels.push(u);
}
if (sendChannels.length) {
verto.sendMethod("verto.unsubscribe", {
eventChannel: sendChannels.length == 1 ? sendChannels[0] : sendChannels
});
}
}
};
$.verto.prototype.broadcast = function(channel, params) {
var verto = this;
var msg = {
eventChannel: channel,
data: {}
};
for (var i in params) {
msg.data[i] = params[i];
}
verto.sendMethod("verto.broadcast", msg);
};
$.verto.prototype.purge = function(callID) {
var verto = this;
var x = 0;
var i;
for (i in verto.dialogs) {
if (!x) {
console.log("purging dialogs");
}
x++;
verto.dialogs[i].setState($.verto.enum.state.purge);
}
for (i in verto.eventSUBS) {
if (verto.eventSUBS[i]) {
console.log("purging subscription: " + i);
delete verto.eventSUBS[i];
}
}
};
$.verto.prototype.hangup = function(callID) {
var verto = this;
if (callID) {
var dialog = verto.dialogs[callID];
if (dialog) {
dialog.hangup();
}
} else {
for (var i in verto.dialogs) {
verto.dialogs[i].hangup();
}
}
};
$.verto.prototype.newCall = function(args, callbacks) {
var verto = this;
if (!verto.rpcClient.socketReady()) {
console.error("Not Connected...");
return;
}
if (args["useCamera"]) {
verto.options.deviceParams["useCamera"] = args["useCamera"];
}
var dialog = new $.verto.dialog($.verto.enum.direction.outbound, this, args);
dialog.invite();
if (callbacks) {
dialog.callbacks = callbacks;
}
return dialog;
};
$.verto.prototype.handleMessage = function(data) {
var verto = this;
if (!(data && data.method)) {
console.error("Invalid Data", data);
return;
}
if (data.params.callID) {
var dialog = verto.dialogs[data.params.callID];
if (data.method === "verto.attach" && dialog) {
delete dialog.verto.dialogs[dialog.callID];
dialog.rtc.stop();
dialog = null;
}
if (dialog) {
switch (data.method) {
case 'verto.bye':
dialog.hangup(data.params);
break;
case 'verto.answer':
dialog.handleAnswer(data.params);
break;
case 'verto.media':
dialog.handleMedia(data.params);
break;
case 'verto.display':
dialog.handleDisplay(data.params);
break;
case 'verto.info':
dialog.handleInfo(data.params);
break;
default:
console.debug("INVALID METHOD OR NON-EXISTANT CALL REFERENCE IGNORED", dialog, data.method);
break;
}
} else {
switch (data.method) {
case 'verto.attach':
data.params.attach = true;
if (data.params.sdp && data.params.sdp.indexOf("m=video") > 0) {
data.params.useVideo = true;
}
if (data.params.sdp && data.params.sdp.indexOf("stereo=1") > 0) {
data.params.useStereo = true;
}
dialog = new $.verto.dialog($.verto.enum.direction.inbound, verto, data.params);
dialog.setState($.verto.enum.state.recovering);
break;
case 'verto.invite':
if (data.params.sdp && data.params.sdp.indexOf("m=video") > 0) {
data.params.wantVideo = true;
}
if (data.params.sdp && data.params.sdp.indexOf("stereo=1") > 0) {
data.params.useStereo = true;
}
dialog = new $.verto.dialog($.verto.enum.direction.inbound, verto, data.params);
break;
default:
console.debug("INVALID METHOD OR NON-EXISTANT CALL REFERENCE IGNORED");
break;
}
}
return {
method: data.method
};
} else {
switch (data.method) {
case 'verto.punt':
verto.purge();
verto.logout();
break;
case 'verto.event':
var list = null;
var key = null;
if (data.params) {
key = data.params.eventChannel;
}
if (key) {
list = verto.eventSUBS[key];
if (!list) {
list = verto.eventSUBS[key.split(".")[0]];
}
}
if (!list && key && key === verto.sessid) {
if (verto.callbacks.onMessage) {
verto.callbacks.onMessage(verto, null, $.verto.enum.message.pvtEvent, data.params);
}
} else if (!list && key && verto.dialogs[key]) {
verto.dialogs[key].sendMessage($.verto.enum.message.pvtEvent, data.params);
} else if (!list) {
if (!key) {
key = "UNDEFINED";
}
console.error("UNSUBBED or invalid EVENT " + key + " IGNORED");
} else {
for (var i in list) {
var sub = list[i];
if (!sub || !sub.ready) {
console.error("invalid EVENT for " + key + " IGNORED");
} else if (sub.handler) {
sub.handler(verto, data.params, sub.userData);
} else if (verto.callbacks.onEvent) {
verto.callbacks.onEvent(verto, data.params, sub.userData);
} else {
console.log("EVENT:", data.params);
}
}
}
break;
case "verto.info":
if (verto.callbacks.onMessage) {
verto.callbacks.onMessage(verto, null, $.verto.enum.message.info, data.params.msg);
}
//console.error(data);
console.debug("MESSAGE from: " + data.params.msg.from, data.params.msg.body);
break;
case 'verto.clientReady':
if (verto.callbacks.onMessage) {
verto.callbacks.onMessage(verto, null, $.verto.enum.message.clientReady, data.params);
}
console.debug("CLIENT READY", data.params);
break;
default:
console.error("INVALID METHOD OR NON-EXISTANT CALL REFERENCE IGNORED", data.method);
break;
}
}
};
var del_array = function(array, name) {
var r = [];
var len = array.length;
for (var i = 0; i < len; i++) {
if (array[i] != name) {
r.push(array[i]);
}
}
return r;
};
var hashArray = function() {
var vha = this;
var hash = {};
var array = [];
vha.reorder = function(a) {
array = a;
var h = hash;
hash = {};
var len = array.length;
for (var i = 0; i < len; i++) {
var key = array[i];
if (h[key]) {
hash[key] = h[key];
delete h[key];
}
}
h = undefined;
};
vha.clear = function() {
hash = undefined;
array = undefined;
hash = {};
array = [];
};
vha.add = function(name, val, insertAt) {
var redraw = false;
if (!hash[name]) {
if (insertAt === undefined || insertAt < 0 || insertAt >= array.length) {
array.push(name);
} else {
var x = 0;
var n = [];
var len = array.length;
for (var i = 0; i < len; i++) {
if (x++==insertAt) {
n.push(name);
}
n.push(array[i]);
}
array = undefined;
array = n;
n = undefined;
redraw = true;
}
}
hash[name] = val;
return redraw;
};
vha.del = function(name) {
var r = false;
if (hash[name]) {
array = del_array(array, name);
delete hash[name];
r = true;
} else {
console.error("can't del nonexistant key " + name);
}
return r;
};
vha.get = function(name) {
return hash[name];
};
vha.order = function() {
return array;
};
vha.hash = function() {
return hash;
};
vha.indexOf = function(name) {
var len = array.length;
for (var i = 0; i < len; i++) {
if (array[i] == name) {
return i;
}
}
};
vha.arrayLen = function() {
return array.length;
};
vha.asArray = function() {
var r = [];
var len = array.length;
for (var i = 0; i < len; i++) {
var key = array[i];
r.push(hash[key]);
}
return r;
};
vha.each = function(cb) {
var len = array.length;
for (var i = 0; i < len; i++) {
cb(array[i], hash[array[i]]);
}
};
vha.dump = function(html) {
var str = "";
vha.each(function(name, val) {
str += "name: " + name + " val: " + JSON.stringify(val) + (html ? "<br>" : "\n");
});
return str;
};
};
$.verto.liveArray = function(verto, context, name, config) {
var la = this;
var lastSerno = 0;
var binding = null;
var user_obj = config.userObj;
var local = false;
// Inherit methods of hashArray
hashArray.call(la);
// Save the hashArray add, del, reorder, clear methods so we can make our own.
la._add = la.add;
la._del = la.del;
la._reorder = la.reorder;
la._clear = la.clear;
la.context = context;
la.name = name;
la.user_obj = user_obj;
la.verto = verto;
la.broadcast = function(channel, obj) {
verto.broadcast(channel, obj);
};
la.errs = 0;
la.clear = function() {
la._clear();
lastSerno = 0;
if (la.onChange) {
la.onChange(la, {
action: "clear"
});
}
};
la.checkSerno = function(serno) {
if (serno < 0) {
return true;
}
if (lastSerno > 0 && serno != (lastSerno + 1)) {
if (la.onErr) {
la.onErr(la, {
lastSerno: lastSerno,
serno: serno
});
}
la.errs++;
console.debug(la.errs);
if (la.errs < 3) {
la.bootstrap(la.user_obj);
}
return false;
} else {
lastSerno = serno;
return true;
}
};
la.reorder = function(serno, a) {
if (la.checkSerno(serno)) {
la._reorder(a);
if (la.onChange) {
la.onChange(la, {
serno: serno,
action: "reorder"
});
}
}
};
la.init = function(serno, val, key, index) {
if (key === null || key === undefined) {
key = serno;
}
if (la.checkSerno(serno)) {
if (la.onChange) {
la.onChange(la, {
serno: serno,
action: "init",
index: index,
key: key,
data: val
});
}
}
};
la.bootObj = function(serno, val) {
if (la.checkSerno(serno)) {
//la.clear();
for (var i in val) {
la._add(val[i][0], val[i][1]);
}
if (la.onChange) {
la.onChange(la, {
serno: serno,
action: "bootObj",
data: val,
redraw: true
});
}
}
};
// @param serno La is the serial number for la particular request.
// @param key If looking at it as a hash table, la represents the key in the hashArray object where you want to store the val object.
// @param index If looking at it as an array, la represents the position in the array where you want to store the val object.
// @param val La is the object you want to store at the key or index location in the hash table / array.
la.add = function(serno, val, key, index) {
if (key === null || key === undefined) {
key = serno;
}
if (la.checkSerno(serno)) {
var redraw = la._add(key, val, index);
if (la.onChange) {
la.onChange(la, {
serno: serno,
action: "add",
index: index,
key: key,
data: val,
redraw: redraw
});
}
}
};
la.modify = function(serno, val, key, index) {
if (key === null || key === undefined) {
key = serno;
}
if (la.checkSerno(serno)) {
la._add(key, val, index);
if (la.onChange) {
la.onChange(la, {
serno: serno,
action: "modify",
key: key,
data: val,
index: index
});
}
}
};
la.del = function(serno, key, index) {
if (key === null || key === undefined) {
key = serno;
}
if (la.checkSerno(serno)) {
if (index === null || index < 0 || index === undefined) {
index = la.indexOf(key);
}
var ok = la._del(key);
if (ok && la.onChange) {
la.onChange(la, {
serno: serno,
action: "del",
key: key,
index: index
});
}
}
};
var eventHandler = function(v, e, la) {
var packet = e.data;
//console.error("READ:", packet);
if (packet.name != la.name) {
return;
}
switch (packet.action) {
case "init":
la.init(packet.wireSerno, packet.data, packet.hashKey, packet.arrIndex);
break;
case "bootObj":
la.bootObj(packet.wireSerno, packet.data);
break;
case "add":
la.add(packet.wireSerno, packet.data, packet.hashKey, packet.arrIndex);
break;
case "modify":
if (! (packet.arrIndex || packet.hashKey)) {
console.error("Invalid Packet", packet);
} else {
la.modify(packet.wireSerno, packet.data, packet.hashKey, packet.arrIndex);
}
break;
case "del":
if (! (packet.arrIndex || packet.hashKey)) {
console.error("Invalid Packet", packet);
} else {
la.del(packet.wireSerno, packet.hashKey, packet.arrIndex);
}
break;
case "clear":
la.clear();
break;
case "reorder":
la.reorder(packet.wireSerno, packet.order);
break;
default:
if (la.checkSerno(packet.wireSerno)) {
if (la.onChange) {
la.onChange(la, {
serno: packet.wireSerno,
action: packet.action,
data: packet.data
});
}
}
break;
}
};
if (la.context) {
binding = la.verto.subscribe(la.context, {
handler: eventHandler,
userData: la,
subParams: config.subParams
});
}
la.destroy = function() {
la._clear();
la.verto.unsubscribe(binding);
};
la.sendCommand = function(cmd, obj) {
var self = la;
self.broadcast(self.context, {
liveArray: {
command: cmd,
context: self.context,
name: self.name,
obj: obj
}
});
};
la.bootstrap = function(obj) {
var self = la;
la.sendCommand("bootstrap", obj);
//self.heartbeat();
};
la.changepage = function(obj) {
var self = la;
self.clear();
self.broadcast(self.context, {
liveArray: {
command: "changepage",
context: la.context,
name: la.name,
obj: obj
}
});
};
la.heartbeat = function(obj) {
var self = la;
var callback = function() {
self.heartbeat.call(self, obj);
};
self.broadcast(self.context, {
liveArray: {
command: "heartbeat",
context: self.context,
name: self.name,
obj: obj
}
});
self.hb_pid = setTimeout(callback, 30000);
};
la.bootstrap(la.user_obj);
};
$.verto.liveTable = function(verto, context, name, jq, config) {
var dt;
var la = new $.verto.liveArray(verto, context, name, {
subParams: config.subParams
});
var lt = this;
lt.liveArray = la;
lt.dataTable = dt;
lt.verto = verto;
lt.destroy = function() {
if (dt) {
dt.fnDestroy();
}
if (la) {
la.destroy();
}
dt = null;
la = null;
};
la.onErr = function(obj, args) {
console.error("Error: ", obj, args);
};
/* back compat so jsonstatus can always be enabled */
function genRow(data) {
if (typeof(data[4]) === "string" && data[4].indexOf("{") > -1) {
var tmp = $.parseJSON(data[4]);
data[4] = tmp.oldStatus;
data[5] = null;
}
return data;
}
function genArray(obj) {
var data = obj.asArray();
for (var i in data) {
data[i] = genRow(data[i]);
}
return data;
}
la.onChange = function(obj, args) {
var index = 0;
var iserr = 0;
if (!dt) {
if (!config.aoColumns) {
if (args.action != "init") {
return;
}
config.aoColumns = [];
for (var i in args.data) {
config.aoColumns.push({
"sTitle": args.data[i]
});
}
}
dt = jq.dataTable(config);
}
if (dt && (args.action == "del" || args.action == "modify")) {
index = args.index;
if (index === undefined && args.key) {
index = la.indexOf(args.key);
}
if (index === undefined) {
console.error("INVALID PACKET Missing INDEX\n", args);
return;
}
}
if (config.onChange) {
config.onChange(obj, args);
}
try {
switch (args.action) {
case "bootObj":
if (!args.data) {
console.error("missing data");
return;
}
dt.fnClearTable();
dt.fnAddData(genArray(obj));
dt.fnAdjustColumnSizing();
break;
case "add":
if (!args.data) {
console.error("missing data");
return;
}
if (args.redraw > -1) {
// specific position, more costly
dt.fnClearTable();
dt.fnAddData(genArray(obj));
} else {
dt.fnAddData(genRow(args.data));
}
dt.fnAdjustColumnSizing();
break;
case "modify":
if (!args.data) {
return;
}
//console.debug(args, index);
dt.fnUpdate(genRow(args.data), index);
dt.fnAdjustColumnSizing();
break;
case "del":
dt.fnDeleteRow(index);
dt.fnAdjustColumnSizing();
break;
case "clear":
dt.fnClearTable();
break;
case "reorder":
// specific position, more costly
dt.fnClearTable();
dt.fnAddData(genArray(obj));
break;
case "hide":
jq.hide();
break;
case "show":
jq.show();
break;
}
} catch(err) {
console.error("ERROR: " + err);
iserr++;
}
if (iserr) {
obj.errs++;
if (obj.errs < 3) {
obj.bootstrap(obj.user_obj);
}
} else {
obj.errs = 0;
}
};
la.onChange(la, {
action: "init"
});
};
var CONFMAN_SERNO = 1;
/*
Conference Manager without jQuery table.
*/
$.verto.conf = function(verto, params) {
var conf = this;
conf.params = $.extend({
dialog: null,
hasVid: false,
laData: null,
onBroadcast: null,
onLaChange: null,
onLaRow: null
}, params);
conf.verto = verto;
conf.serno = CONFMAN_SERNO++;
createMainModeratorMethods();
verto.subscribe(conf.params.laData.modChannel, {
handler: function(v, e) {
if (conf.params.onBroadcast) {
conf.params.onBroadcast(verto, conf, e.data);
}
}
});
verto.subscribe(conf.params.laData.infoChannel, {
handler: function(v, e) {
if (typeof(conf.params.infoCallback) === "function") {
conf.params.infoCallback(v,e);
}
}
});
verto.subscribe(conf.params.laData.chatChannel, {
handler: function(v, e) {
if (typeof(conf.params.chatCallback) === "function") {
conf.params.chatCallback(v,e);
}
}
});
};
$.verto.conf.prototype.modCommand = function(cmd, id, value) {
var conf = this;
conf.verto.rpcClient.call("verto.broadcast", {
"eventChannel": conf.params.laData.modChannel,
"data": {
"application": "conf-control",
"command": cmd,
"id": id,
"value": value
}
});
};
$.verto.conf.prototype.destroy = function() {
var conf = this;
conf.destroyed = true;
conf.params.onBroadcast(conf.verto, conf, 'destroy');
if (conf.params.laData.modChannel) {
conf.verto.unsubscribe(conf.params.laData.modChannel);
}
if (conf.params.laData.chatChannel) {
conf.verto.unsubscribe(conf.params.laData.chatChannel);
}
if (conf.params.laData.infoChannel) {
conf.verto.unsubscribe(conf.params.laData.infoChannel);
}
};
function createMainModeratorMethods() {
$.verto.conf.prototype.listVideoLayouts = function() {
this.modCommand("list-videoLayouts", null, null);
};
$.verto.conf.prototype.play = function(file) {
this.modCommand("play", null, file);
};
$.verto.conf.prototype.stop = function() {
this.modCommand("stop", null, "all");
};
$.verto.conf.prototype.deaf = function(memberID) {
this.modCommand("deaf", parseInt(memberID));
};
$.verto.conf.prototype.undeaf = function(memberID) {
this.modCommand("undeaf", parseInt(memberID));
};
$.verto.conf.prototype.record = function(file) {
this.modCommand("recording", null, ["start", file]);
};
$.verto.conf.prototype.stopRecord = function() {
this.modCommand("recording", null, ["stop", "all"]);
};
$.verto.conf.prototype.snapshot = function(file) {
if (!this.params.hasVid) {
throw 'Conference has no video';
}
this.modCommand("vid-write-png", null, file);
};
$.verto.conf.prototype.setVideoLayout = function(layout, canvasID) {
if (!this.params.hasVid) {
throw 'Conference has no video';
}
if (canvasID) {
this.modCommand("vid-layout", null, [layout, canvasID]);
} else {
this.modCommand("vid-layout", null, layout);
}
};
$.verto.conf.prototype.kick = function(memberID) {
this.modCommand("kick", parseInt(memberID));
};
$.verto.conf.prototype.muteMic = function(memberID) {
this.modCommand("tmute", parseInt(memberID));
};
$.verto.conf.prototype.muteVideo = function(memberID) {
if (!this.params.hasVid) {
throw 'Conference has no video';
}
this.modCommand("tvmute", parseInt(memberID));
};
$.verto.conf.prototype.presenter = function(memberID) {
if (!this.params.hasVid) {
throw 'Conference has no video';
}
this.modCommand("vid-res-id", parseInt(memberID), "presenter");
};
$.verto.conf.prototype.videoFloor = function(memberID) {
if (!this.params.hasVid) {
throw 'Conference has no video';
}
this.modCommand("vid-floor", parseInt(memberID), "force");
};
$.verto.conf.prototype.banner = function(memberID, text) {
if (!this.params.hasVid) {
throw 'Conference has no video';
}
this.modCommand("vid-banner", parseInt(memberID), escape(text));
};
$.verto.conf.prototype.volumeDown = function(memberID) {
this.modCommand("volume_out", parseInt(memberID), "down");
};
$.verto.conf.prototype.volumeUp = function(memberID) {
this.modCommand("volume_out", parseInt(memberID), "up");
};
$.verto.conf.prototype.gainDown = function(memberID) {
this.modCommand("volume_in", parseInt(memberID), "down");
};
$.verto.conf.prototype.gainUp = function(memberID) {
this.modCommand("volume_in", parseInt(memberID), "up");
};
$.verto.conf.prototype.transfer = function(memberID, exten) {
this.modCommand("transfer", parseInt(memberID), exten);
};
$.verto.conf.prototype.sendChat = function(message, type) {
var conf = this;
conf.verto.rpcClient.call("verto.broadcast", {
"eventChannel": conf.params.laData.chatChannel,
"data": {
"action": "send",
"message": message,
"type": type
}
});
};
}
$.verto.modfuncs = {};
$.verto.confMan = function(verto, params) {
var confMan = this;
confMan.params = $.extend({
tableID: null,
statusID: null,
mainModID: null,
dialog: null,
hasVid: false,
laData: null,
onBroadcast: null,
onLaChange: null,
onLaRow: null
}, params);
confMan.verto = verto;
confMan.serno = CONFMAN_SERNO++;
confMan.canvasCount = confMan.params.laData.canvasCount;
function genMainMod(jq) {
var play_id = "play_" + confMan.serno;
var stop_id = "stop_" + confMan.serno;
var recording_id = "recording_" + confMan.serno;
var snapshot_id = "snapshot_" + confMan.serno;
var rec_stop_id = "recording_stop" + confMan.serno;
var div_id = "confman_" + confMan.serno;
var html = "<div id='" + div_id + "'><br>" +
"<button class='ctlbtn' id='" + play_id + "'>Play</button>" +
"<button class='ctlbtn' id='" + stop_id + "'>Stop</button>" +
"<button class='ctlbtn' id='" + recording_id + "'>Record</button>" +
"<button class='ctlbtn' id='" + rec_stop_id + "'>Record Stop</button>" +
(confMan.params.hasVid ? "<button class='ctlbtn' id='" + snapshot_id + "'>PNG Snapshot</button>" : "") +
"<br><br></div>";
jq.html(html);
$.verto.modfuncs.change_video_layout = function(id, canvas_id) {
var val = $("#" + id + " option:selected").text();
if (val !== "none") {
confMan.modCommand("vid-layout", null, [val, canvas_id]);
}
};
if (confMan.params.hasVid) {
for (var j = 0; j < confMan.canvasCount; j++) {
var vlayout_id = "confman_vid_layout_" + j + "_" + confMan.serno;
var vlselect_id = "confman_vl_select_" + j + "_" + confMan.serno;
var vlhtml = "<div id='" + vlayout_id + "'><br>" +
"<b>Video Layout Canvas " + (j+1) +
"</b> <select onChange='$.verto.modfuncs.change_video_layout(\"" + vlayout_id + "\", \"" + (j+1) + "\")' id='" + vlselect_id + "'></select> " +
"<br><br></div>";
jq.append(vlhtml);
}
$("#" + snapshot_id).click(function() {
var file = prompt("Please enter file name", "");
if (file) {
confMan.modCommand("vid-write-png", null, file);
}
});
}
$("#" + play_id).click(function() {
var file = prompt("Please enter file name", "");
if (file) {
confMan.modCommand("play", null, file);
}
});
$("#" + stop_id).click(function() {
confMan.modCommand("stop", null, "all");
});
$("#" + recording_id).click(function() {
var file = prompt("Please enter file name", "");
if (file) {
confMan.modCommand("recording", null, ["start", file]);
}
});
$("#" + rec_stop_id).click(function() {
confMan.modCommand("recording", null, ["stop", "all"]);
});
}
function genControls(jq, rowid) {
var x = parseInt(rowid);
var kick_id = "kick_" + x;
var canvas_in_next_id = "canvas_in_next_" + x;
var canvas_in_prev_id = "canvas_in_prev_" + x;
var canvas_out_next_id = "canvas_out_next_" + x;
var canvas_out_prev_id = "canvas_out_prev_" + x;
var canvas_in_set_id = "canvas_in_set_" + x;
var canvas_out_set_id = "canvas_out_set_" + x;
var layer_set_id = "layer_set_" + x;
var layer_next_id = "layer_next_" + x;
var layer_prev_id = "layer_prev_" + x;
var tmute_id = "tmute_" + x;
var tvmute_id = "tvmute_" + x;
var vbanner_id = "vbanner_" + x;
var tvpresenter_id = "tvpresenter_" + x;
var tvfloor_id = "tvfloor_" + x;
var box_id = "box_" + x;
var gainup_id = "gain_in_up" + x;
var gaindn_id = "gain_in_dn" + x;
var volup_id = "vol_in_up" + x;
var voldn_id = "vol_in_dn" + x;
var transfer_id = "transfer" + x;
var html = "<div id='" + box_id + "'>";
html += "<b>General Controls</b><hr noshade>";
html += "<button class='ctlbtn' id='" + kick_id + "'>Kick</button>" +
"<button class='ctlbtn' id='" + tmute_id + "'>Mute</button>" +
"<button class='ctlbtn' id='" + gainup_id + "'>Gain -</button>" +
"<button class='ctlbtn' id='" + gaindn_id + "'>Gain +</button>" +
"<button class='ctlbtn' id='" + voldn_id + "'>Vol -</button>" +
"<button class='ctlbtn' id='" + volup_id + "'>Vol +</button>" +
"<button class='ctlbtn' id='" + transfer_id + "'>Transfer</button>";
if (confMan.params.hasVid) {
html += "<br><br><b>Video Controls</b><hr noshade>";
html += "<button class='ctlbtn' id='" + tvmute_id + "'>VMute</button>" +
"<button class='ctlbtn' id='" + tvpresenter_id + "'>Presenter</button>" +
"<button class='ctlbtn' id='" + tvfloor_id + "'>Vid Floor</button>" +
"<button class='ctlbtn' id='" + vbanner_id + "'>Banner</button>";
if (confMan.canvasCount > 1) {
html += "<br><br><b>Canvas Controls</b><hr noshade>" +
"<button class='ctlbtn' id='" + canvas_in_set_id + "'>Set Input Canvas</button>" +
"<button class='ctlbtn' id='" + canvas_in_prev_id + "'>Prev Input Canvas</button>" +
"<button class='ctlbtn' id='" + canvas_in_next_id + "'>Next Input Canvas</button>" +
"<br>" +
"<button class='ctlbtn' id='" + canvas_out_set_id + "'>Set Watching Canvas</button>" +
"<button class='ctlbtn' id='" + canvas_out_prev_id + "'>Prev Watching Canvas</button>" +
"<button class='ctlbtn' id='" + canvas_out_next_id + "'>Next Watching Canvas</button>";
}
html += "<br>" +
"<button class='ctlbtn' id='" + layer_set_id + "'>Set Layer</button>" +
"<button class='ctlbtn' id='" + layer_prev_id + "'>Prev Layer</button>" +
"<button class='ctlbtn' id='" + layer_next_id + "'>Next Layer</button>" +
"</div>";
}
jq.html(html);
if (!jq.data("mouse")) {
$("#" + box_id).hide();
}
jq.mouseover(function(e) {
jq.data({"mouse": true});
$("#" + box_id).show();
});
jq.mouseout(function(e) {
jq.data({"mouse": false});
$("#" + box_id).hide();
});
$("#" + transfer_id).click(function() {
var xten = prompt("Enter Extension");
if (xten) {
confMan.modCommand("transfer", x, xten);
}
});
$("#" + kick_id).click(function() {
confMan.modCommand("kick", x);
});
$("#" + layer_set_id).click(function() {
var cid = prompt("Please enter layer ID", "");
if (cid) {
confMan.modCommand("vid-layer", x, cid);
}
});
$("#" + layer_next_id).click(function() {
confMan.modCommand("vid-layer", x, "next");
});
$("#" + layer_prev_id).click(function() {
confMan.modCommand("vid-layer", x, "prev");
});
$("#" + canvas_in_set_id).click(function() {
var cid = prompt("Please enter canvas ID", "");
if (cid) {
confMan.modCommand("vid-canvas", x, cid);
}
});
$("#" + canvas_out_set_id).click(function() {
var cid = prompt("Please enter canvas ID", "");
if (cid) {
confMan.modCommand("vid-watching-canvas", x, cid);
}
});
$("#" + canvas_in_next_id).click(function() {
confMan.modCommand("vid-canvas", x, "next");
});
$("#" + canvas_in_prev_id).click(function() {
confMan.modCommand("vid-canvas", x, "prev");
});
$("#" + canvas_out_next_id).click(function() {
confMan.modCommand("vid-watching-canvas", x, "next");
});
$("#" + canvas_out_prev_id).click(function() {
confMan.modCommand("vid-watching-canvas", x, "prev");
});
$("#" + tmute_id).click(function() {
confMan.modCommand("tmute", x);
});
if (confMan.params.hasVid) {
$("#" + tvmute_id).click(function() {
confMan.modCommand("tvmute", x);
});
$("#" + tvpresenter_id).click(function() {
confMan.modCommand("vid-res-id", x, "presenter");
});
$("#" + tvfloor_id).click(function() {
confMan.modCommand("vid-floor", x, "force");
});
$("#" + vbanner_id).click(function() {
var text = prompt("Please enter text", "");
if (text) {
confMan.modCommand("vid-banner", x, escape(text));
}
});
}
$("#" + gainup_id).click(function() {
confMan.modCommand("volume_in", x, "up");
});
$("#" + gaindn_id).click(function() {
confMan.modCommand("volume_in", x, "down");
});
$("#" + volup_id).click(function() {
confMan.modCommand("volume_out", x, "up");
});
$("#" + voldn_id).click(function() {
confMan.modCommand("volume_out", x, "down");
});
return html;
}
var atitle = "";
var awidth = 0;
//$(".jsDataTable").width(confMan.params.hasVid ? "900px" : "800px");
verto.subscribe(confMan.params.laData.infoChannel, {
handler: function(v, e) {
if (typeof(confMan.params.infoCallback) === "function") {
confMan.params.infoCallback(v,e);
}
}
});
verto.subscribe(confMan.params.laData.chatChannel, {
handler: function(v, e) {
if (typeof(confMan.params.chatCallback) === "function") {
confMan.params.chatCallback(v,e);
}
}
});
if (confMan.params.laData.role === "moderator") {
atitle = "Action";
awidth = 600;
if (confMan.params.mainModID) {
genMainMod($(confMan.params.mainModID));
$(confMan.params.displayID).html("Moderator Controls Ready<br><br>");
} else {
$(confMan.params.mainModID).html("");
}
verto.subscribe(confMan.params.laData.modChannel, {
handler: function(v, e) {
//console.error("MODDATA:", e.data);
if (confMan.params.onBroadcast) {
confMan.params.onBroadcast(verto, confMan, e.data);
}
if (e.data["conf-command"] === "list-videoLayouts") {
for (var j = 0; j < confMan.canvasCount; j++) {
var vlselect_id = "#confman_vl_select_" + j + "_" + confMan.serno;
var vlayout_id = "#confman_vid_layout_" + j + "_" + confMan.serno;
var x = 0;
var options;
$(vlselect_id).selectmenu({});
$(vlselect_id).selectmenu("enable");
$(vlselect_id).empty();
$(vlselect_id).append(new Option("Choose a Layout", "none"));
if (e.data.responseData) {
var rdata = [];
for (var i in e.data.responseData) {
rdata.push(e.data.responseData[i].name);
}
options = rdata.sort(function(a, b) {
var ga = a.substring(0, 6) == "group:" ? true : false;
var gb = b.substring(0, 6) == "group:" ? true : false;
if ((ga || gb) && ga != gb) {
return ga ? -1 : 1;
}
return ( ( a == b ) ? 0 : ( ( a > b ) ? 1 : -1 ) );
});
for (var i in options) {
$(vlselect_id).append(new Option(options[i], options[i]));
x++;
}
}
if (x) {
$(vlselect_id).selectmenu('refresh', true);
} else {
$(vlayout_id).hide();
}
}
} else {
if (!confMan.destroyed && confMan.params.displayID) {
$(confMan.params.displayID).html(e.data.response + "<br><br>");
if (confMan.lastTimeout) {
clearTimeout(confMan.lastTimeout);
confMan.lastTimeout = 0;
}
confMan.lastTimeout = setTimeout(function() { $(confMan.params.displayID).html(confMan.destroyed ? "" : "Moderator Controls Ready<br><br>");}, 4000);
}
}
}
});
if (confMan.params.hasVid) {
confMan.modCommand("list-videoLayouts", null, null);
}
}
var row_callback = null;
if (confMan.params.laData.role === "moderator") {
row_callback = function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
if (!aData[5]) {
var $row = $('td:eq(5)', nRow);
genControls($row, aData);
if (confMan.params.onLaRow) {
confMan.params.onLaRow(verto, confMan, $row, aData);
}
}
};
}
confMan.lt = new $.verto.liveTable(verto, confMan.params.laData.laChannel, confMan.params.laData.laName, $(confMan.params.tableID), {
subParams: {
callID: confMan.params.dialog ? confMan.params.dialog.callID : null
},
"onChange": function(obj, args) {
$(confMan.params.statusID).text("Conference Members: " + " (" + obj.arrayLen() + " Total)");
if (confMan.params.onLaChange) {
confMan.params.onLaChange(verto, confMan, $.verto.enum.confEvent.laChange, obj, args);
}
},
"aaData": [],
"aoColumns": [
{
"sTitle": "ID",
"sWidth": "50"
},
{
"sTitle": "Number",
"sWidth": "250"
},
{
"sTitle": "Name",
"sWidth": "250"
},
{
"sTitle": "Codec",
"sWidth": "100"
},
{
"sTitle": "Status",
"sWidth": confMan.params.hasVid ? "200px" : "150px"
},
{
"sTitle": atitle,
"sWidth": awidth,
}
],
"bAutoWidth": true,
"bDestroy": true,
"bSort": false,
"bInfo": false,
"bFilter": false,
"bLengthChange": false,
"bPaginate": false,
"iDisplayLength": 1400,
"oLanguage": {
"sEmptyTable": "The Conference is Empty....."
},
"fnRowCallback": row_callback
});
};
$.verto.confMan.prototype.modCommand = function(cmd, id, value) {
var confMan = this;
confMan.verto.rpcClient.call("verto.broadcast", {
"eventChannel": confMan.params.laData.modChannel,
"data": {
"application": "conf-control",
"command": cmd,
"id": id,
"value": value
}
});
};
$.verto.confMan.prototype.sendChat = function(message, type) {
var confMan = this;
confMan.verto.rpcClient.call("verto.broadcast", {
"eventChannel": confMan.params.laData.chatChannel,
"data": {
"action": "send",
"message": message,
"type": type
}
});
};
$.verto.confMan.prototype.destroy = function() {
var confMan = this;
confMan.destroyed = true;
if (confMan.lt) {
confMan.lt.destroy();
}
if (confMan.params.laData.chatChannel) {
confMan.verto.unsubscribe(confMan.params.laData.chatChannel);
}
if (confMan.params.laData.modChannel) {
confMan.verto.unsubscribe(confMan.params.laData.modChannel);
}
if (confMan.params.mainModID) {
$(confMan.params.mainModID).html("");
}
};
$.verto.dialog = function(direction, verto, params) {
var dialog = this;
dialog.params = $.extend({
useVideo: verto.options.useVideo,
useStereo: verto.options.useStereo,
screenShare: false,
useCamera: false,
useMic: verto.options.deviceParams.useMic,
useSpeak: verto.options.deviceParams.useSpeak,
tag: verto.options.tag,
localTag: verto.options.localTag,
login: verto.options.login,
videoParams: verto.options.videoParams
}, params);
if (!dialog.params.screenShare) {
dialog.params.useCamera = verto.options.deviceParams.useCamera;
}
dialog.verto = verto;
dialog.direction = direction;
dialog.lastState = null;
dialog.state = dialog.lastState = $.verto.enum.state.new;
dialog.callbacks = verto.callbacks;
dialog.answered = false;
dialog.attach = params.attach || false;
dialog.screenShare = params.screenShare || false;
dialog.useCamera = dialog.params.useCamera;
dialog.useMic = dialog.params.useMic;
dialog.useSpeak = dialog.params.useSpeak;
if (dialog.params.callID) {
dialog.callID = dialog.params.callID;
} else {
dialog.callID = dialog.params.callID = generateGUID();
}
if (typeof(dialog.params.tag) === "function") {
dialog.params.tag = dialog.params.tag();
}
if (dialog.params.tag) {
dialog.audioStream = document.getElementById(dialog.params.tag);
if (dialog.params.useVideo) {
dialog.videoStream = dialog.audioStream;
}
} //else conjure one TBD
if (dialog.params.localTag) {
dialog.localVideo = document.getElementById(dialog.params.localTag);
}
dialog.verto.dialogs[dialog.callID] = dialog;
var RTCcallbacks = {};
if (dialog.direction == $.verto.enum.direction.inbound) {
if (dialog.params.display_direction === "outbound") {
dialog.params.remote_caller_id_name = dialog.params.caller_id_name;
dialog.params.remote_caller_id_number = dialog.params.caller_id_number;
} else {
dialog.params.remote_caller_id_name = dialog.params.callee_id_name;
dialog.params.remote_caller_id_number = dialog.params.callee_id_number;
}
if (!dialog.params.remote_caller_id_name) {
dialog.params.remote_caller_id_name = "Nobody";
}
if (!dialog.params.remote_caller_id_number) {
dialog.params.remote_caller_id_number = "UNKNOWN";
}
RTCcallbacks.onMessage = function(rtc, msg) {
console.debug(msg);
};
RTCcallbacks.onAnswerSDP = function(rtc, sdp) {
console.error("answer sdp", sdp);
};
} else {
dialog.params.remote_caller_id_name = "Outbound Call";
dialog.params.remote_caller_id_number = dialog.params.destination_number;
}
RTCcallbacks.onICESDP = function(rtc) {
console.log("RECV " + rtc.type + " SDP", rtc.mediaData.SDP);
if (dialog.state == $.verto.enum.state.requesting || dialog.state == $.verto.enum.state.answering || dialog.state == $.verto.enum.state.active) {
location.reload();
return;
}
if (rtc.type == "offer") {
if (dialog.state == $.verto.enum.state.active) {
dialog.setState($.verto.enum.state.requesting);
dialog.sendMethod("verto.attach", {
sdp: rtc.mediaData.SDP
});
} else {
dialog.setState($.verto.enum.state.requesting);
dialog.sendMethod("verto.invite", {
sdp: rtc.mediaData.SDP
});
}
} else { //answer
dialog.setState($.verto.enum.state.answering);
dialog.sendMethod(dialog.attach ? "verto.attach" : "verto.answer", {
sdp: dialog.rtc.mediaData.SDP
});
}
};
RTCcallbacks.onICE = function(rtc) {
//console.log("cand", rtc.mediaData.candidate);
if (rtc.type == "offer") {
console.log("offer", rtc.mediaData.candidate);
return;
}
};
RTCcallbacks.onStream = function(rtc, stream) {
if (dialog.verto.options.permissionCallback &&
typeof dialog.verto.options.permissionCallback.onGranted === 'function'){
dialog.verto.options.permissionCallback.onGranted(stream);
}
console.log("stream started");
};
RTCcallbacks.onError = function(e) {
if (dialog.verto.options.permissionCallback &&
typeof dialog.verto.options.permissionCallback.onDenied === 'function'){
dialog.verto.options.permissionCallback.onDenied();
}
console.error("ERROR:", e);
dialog.hangup({cause: "Device or Permission Error"});
};
dialog.rtc = new $.FSRTC({
callbacks: RTCcallbacks,
localVideo: dialog.screenShare ? null : dialog.localVideo,
useVideo: dialog.params.useVideo ? dialog.videoStream : null,
useAudio: dialog.audioStream,
useStereo: dialog.params.useStereo,
videoParams: dialog.params.videoParams,
audioParams: verto.options.audioParams,
iceServers: verto.options.iceServers,
screenShare: dialog.screenShare,
useCamera: dialog.useCamera,
useMic: dialog.useMic,
useSpeak: dialog.useSpeak,
turnServer: verto.options.turnServer
});
dialog.rtc.verto = dialog.verto;
if (dialog.direction == $.verto.enum.direction.inbound) {
if (dialog.attach) {
dialog.answer();
} else {
dialog.ring();
}
}
};
$.verto.dialog.prototype.invite = function() {
var dialog = this;
dialog.rtc.call();
};
$.verto.dialog.prototype.sendMethod = function(method, obj) {
var dialog = this;
obj.dialogParams = {};
for (var i in dialog.params) {
if (i == "sdp" && method != "verto.invite" && method != "verto.attach") {
continue;
}
if ((obj.noDialogParams && i != "callID")) {
continue;
}
obj.dialogParams[i] = dialog.params[i];
}
delete obj.noDialogParams;
dialog.verto.rpcClient.call(method, obj,
function(e) {
/* Success */
dialog.processReply(method, true, e);
},
function(e) {
/* Error */
dialog.processReply(method, false, e);
});
};
function checkStateChange(oldS, newS) {
if (newS == $.verto.enum.state.purge || $.verto.enum.states[oldS.name][newS.name]) {
return true;
}
return false;
}
// Attach audio output device to video element using device/sink ID.
function find_name(id) {
for (var i in $.verto.audioOutDevices) {
var source = $.verto.audioOutDevices[i];
if (source.id === id) {
return(source.label);
}
}
return id;
}
$.verto.dialog.prototype.setAudioPlaybackDevice = function(sinkId, callback, arg) {
var dialog = this;
var element = dialog.audioStream;
if (typeof element.sinkId !== 'undefined') {
var devname = find_name(sinkId);
console.info("Dialog: " + dialog.callID + " Setting speaker:", element, devname);
element.setSinkId(sinkId)
.then(function() {
console.log("Dialog: " + dialog.callID + ' Success, audio output device attached: ' + sinkId);
if (callback) {
callback(true, devname, arg);
}
})
.catch(function(error) {
var errorMessage = error;
if (error.name === 'SecurityError') {
errorMessage = "Dialog: " + dialog.callID + ' You need to use HTTPS for selecting audio output ' +
'device: ' + error;
}
if (callback) {
callback(false, null, arg);
}
console.error(errorMessage);
});
} else {
console.warn("Dialog: " + dialog.callID + ' Browser does not support output device selection.');
if (callback) {
callback(false, null, arg);
}
}
}
$.verto.dialog.prototype.setState = function(state) {
var dialog = this;
if (dialog.state == $.verto.enum.state.ringing) {
dialog.stopRinging();
}
if (dialog.state == state || !checkStateChange(dialog.state, state)) {
console.error("Dialog " + dialog.callID + ": INVALID state change from " + dialog.state.name + " to " + state.name);
dialog.hangup();
return false;
}
console.log("Dialog " + dialog.callID + ": state change from " + dialog.state.name + " to " + state.name);
dialog.lastState = dialog.state;
dialog.state = state;
if (dialog.callbacks.onDialogState) {
dialog.callbacks.onDialogState(this);
}
switch (dialog.state) {
case $.verto.enum.state.early:
case $.verto.enum.state.active:
var speaker = dialog.useSpeak;
console.info("Using Speaker: ", speaker);
if (speaker && speaker !== "any" && speaker !== "none") {
setTimeout(function() {
dialog.setAudioPlaybackDevice(speaker);
}, 500);
}
break;
case $.verto.enum.state.trying:
setTimeout(function() {
if (dialog.state == $.verto.enum.state.trying) {
dialog.setState($.verto.enum.state.hangup);
}
}, 30000);
break;
case $.verto.enum.state.purge:
dialog.setState($.verto.enum.state.destroy);
break;
case $.verto.enum.state.hangup:
if (dialog.lastState.val > $.verto.enum.state.requesting.val && dialog.lastState.val < $.verto.enum.state.hangup.val) {
dialog.sendMethod("verto.bye", {});
}
dialog.setState($.verto.enum.state.destroy);
break;
case $.verto.enum.state.destroy:
if (typeof(dialog.verto.options.tag) === "function") {
$('#' + dialog.params.tag).remove();
}
delete dialog.verto.dialogs[dialog.callID];
if (dialog.params.screenShare) {
dialog.rtc.stopPeer();
} else {
dialog.rtc.stop();
}
break;
}
return true;
};
$.verto.dialog.prototype.processReply = function(method, success, e) {
var dialog = this;
//console.log("Response: " + method + " State:" + dialog.state.name, success, e);
switch (method) {
case "verto.answer":
case "verto.attach":
if (success) {
dialog.setState($.verto.enum.state.active);
} else {
dialog.hangup();
}
break;
case "verto.invite":
if (success) {
dialog.setState($.verto.enum.state.trying);
} else {
dialog.setState($.verto.enum.state.destroy);
}
break;
case "verto.bye":
dialog.hangup();
break;
case "verto.modify":
if (e.holdState) {
if (e.holdState == "held") {
if (dialog.state != $.verto.enum.state.held) {
dialog.setState($.verto.enum.state.held);
}
} else if (e.holdState == "active") {
if (dialog.state != $.verto.enum.state.active) {
dialog.setState($.verto.enum.state.active);
}
}
}
if (success) {}
break;
default:
break;
}
};
$.verto.dialog.prototype.hangup = function(params) {
var dialog = this;
if (params) {
if (params.causeCode) {
dialog.causeCode = params.causeCode;
}
if (params.cause) {
dialog.cause = params.cause;
}
}
if (!dialog.cause && !dialog.causeCode) {
dialog.cause = "NORMAL_CLEARING";
}
if (dialog.state.val >= $.verto.enum.state.new.val && dialog.state.val < $.verto.enum.state.hangup.val) {
dialog.setState($.verto.enum.state.hangup);
} else if (dialog.state.val < $.verto.enum.state.destroy) {
dialog.setState($.verto.enum.state.destroy);
}
};
$.verto.dialog.prototype.stopRinging = function() {
var dialog = this;
if (dialog.verto.ringer) {
dialog.verto.ringer.stop();
}
};
$.verto.dialog.prototype.indicateRing = function() {
var dialog = this;
if (dialog.verto.ringer) {
dialog.verto.ringer.attr("src", dialog.verto.options.ringFile)[0].play();
setTimeout(function() {
dialog.stopRinging();
if (dialog.state == $.verto.enum.state.ringing) {
dialog.indicateRing();
}
},
dialog.verto.options.ringSleep);
}
};
$.verto.dialog.prototype.ring = function() {
var dialog = this;
dialog.setState($.verto.enum.state.ringing);
dialog.indicateRing();
};
$.verto.dialog.prototype.useVideo = function(on) {
var dialog = this;
dialog.params.useVideo = on;
if (on) {
dialog.videoStream = dialog.audioStream;
} else {
dialog.videoStream = null;
}
dialog.rtc.useVideo(dialog.videoStream, dialog.localVideo);
};
$.verto.dialog.prototype.setMute = function(what) {
var dialog = this;
return dialog.rtc.setMute(what);
};
$.verto.dialog.prototype.getMute = function() {
var dialog = this;
return dialog.rtc.getMute();
};
$.verto.dialog.prototype.setVideoMute = function(what) {
var dialog = this;
return dialog.rtc.setVideoMute(what);
};
$.verto.dialog.prototype.getVideoMute = function() {
var dialog = this;
return dialog.rtc.getVideoMute();
};
$.verto.dialog.prototype.useStereo = function(on) {
var dialog = this;
dialog.params.useStereo = on;
dialog.rtc.useStereo(on);
};
$.verto.dialog.prototype.dtmf = function(digits) {
var dialog = this;
if (digits) {
dialog.sendMethod("verto.info", {
dtmf: digits
});
}
};
$.verto.dialog.prototype.rtt = function(obj) {
var dialog = this;
var pobj = {};
if (!obj) {
return false;
}
pobj.code = obj.code;
pobj.chars = obj.chars;
if (pobj.chars || pobj.code) {
dialog.sendMethod("verto.info", {
txt: obj,
noDialogParams: true
});
}
};
$.verto.dialog.prototype.transfer = function(dest, params) {
var dialog = this;
if (dest) {
dialog.sendMethod("verto.modify", {
action: "transfer",
destination: dest,
params: params
});
}
};
$.verto.dialog.prototype.replace = function(replaceCallID, params) {
var dialog = this;
if (replaceCallID) {
dialog.sendMethod("verto.modify", {
action: "replace",
replaceCallID: replaceCallID,
params: params
});
}
};
$.verto.dialog.prototype.hold = function(params) {
var dialog = this;
dialog.sendMethod("verto.modify", {
action: "hold",
params: params
});
};
$.verto.dialog.prototype.unhold = function(params) {
var dialog = this;
dialog.sendMethod("verto.modify", {
action: "unhold",
params: params
});
};
$.verto.dialog.prototype.toggleHold = function(params) {
var dialog = this;
dialog.sendMethod("verto.modify", {
action: "toggleHold",
params: params
});
};
$.verto.dialog.prototype.message = function(msg) {
var dialog = this;
var err = 0;
msg.from = dialog.params.login;
if (!msg.to) {
console.error("Missing To");
err++;
}
if (!msg.body) {
console.error("Missing Body");
err++;
}
if (err) {
return false;
}
dialog.sendMethod("verto.info", {
msg: msg
});
return true;
};
$.verto.dialog.prototype.answer = function(params) {
var dialog = this;
if (!dialog.answered) {
if (!params) {
params = {};
}
params.sdp = dialog.params.sdp;
if (params) {
if (params.useVideo) {
dialog.useVideo(true);
}
dialog.params.callee_id_name = params.callee_id_name;
dialog.params.callee_id_number = params.callee_id_number;
if (params.useCamera) {
dialog.useCamera = params.useCamera;
}
if (params.useMic) {
dialog.useMic = params.useMic;
}
if (params.useSpeak) {
dialog.useSpeak = params.useSpeak;
}
}
dialog.rtc.createAnswer(params);
dialog.answered = true;
}
};
$.verto.dialog.prototype.handleAnswer = function(params) {
var dialog = this;
dialog.gotAnswer = true;
if (dialog.state.val >= $.verto.enum.state.active.val) {
return;
}
if (dialog.state.val >= $.verto.enum.state.early.val) {
dialog.setState($.verto.enum.state.active);
} else {
if (dialog.gotEarly) {
console.log("Dialog " + dialog.callID + " Got answer while still establishing early media, delaying...");
} else {
console.log("Dialog " + dialog.callID + " Answering Channel");
dialog.rtc.answer(params.sdp, function() {
dialog.setState($.verto.enum.state.active);
}, function(e) {
console.error(e);
dialog.hangup();
});
console.log("Dialog " + dialog.callID + "ANSWER SDP", params.sdp);
}
}
};
$.verto.dialog.prototype.cidString = function(enc) {
var dialog = this;
var party = dialog.params.remote_caller_id_name + (enc ? " &lt;" : " <") + dialog.params.remote_caller_id_number + (enc ? "&gt;" : ">");
return party;
};
$.verto.dialog.prototype.sendMessage = function(msg, params) {
var dialog = this;
if (dialog.callbacks.onMessage) {
dialog.callbacks.onMessage(dialog.verto, dialog, msg, params);
}
};
$.verto.dialog.prototype.handleInfo = function(params) {
var dialog = this;
dialog.sendMessage($.verto.enum.message.info, params);
};
$.verto.dialog.prototype.handleDisplay = function(params) {
var dialog = this;
if (params.display_name) {
dialog.params.remote_caller_id_name = params.display_name;
}
if (params.display_number) {
dialog.params.remote_caller_id_number = params.display_number;
}
dialog.sendMessage($.verto.enum.message.display, {});
};
$.verto.dialog.prototype.handleMedia = function(params) {
var dialog = this;
if (dialog.state.val >= $.verto.enum.state.early.val) {
return;
}
dialog.gotEarly = true;
dialog.rtc.answer(params.sdp, function() {
console.log("Dialog " + dialog.callID + "Establishing early media");
dialog.setState($.verto.enum.state.early);
if (dialog.gotAnswer) {
console.log("Dialog " + dialog.callID + "Answering Channel");
dialog.setState($.verto.enum.state.active);
}
}, function(e) {
console.error(e);
dialog.hangup();
});
console.log("Dialog " + dialog.callID + "EARLY SDP", params.sdp);
};
$.verto.ENUM = function(s) {
var i = 0,
o = {};
s.split(" ").map(function(x) {
o[x] = {
name: x,
val: i++
};
});
return Object.freeze(o);
};
$.verto.enum = {};
$.verto.enum.states = Object.freeze({
new: {
requesting: 1,
recovering: 1,
ringing: 1,
destroy: 1,
answering: 1,
hangup: 1
},
requesting: {
trying: 1,
hangup: 1,
active: 1
},
recovering: {
answering: 1,
hangup: 1
},
trying: {
active: 1,
early: 1,
hangup: 1
},
ringing: {
answering: 1,
hangup: 1
},
answering: {
active: 1,
hangup: 1
},
active: {
answering: 1,
requesting: 1,
hangup: 1,
held: 1
},
held: {
hangup: 1,
active: 1
},
early: {
hangup: 1,
active: 1
},
hangup: {
destroy: 1
},
destroy: {},
purge: {
destroy: 1
}
});
$.verto.enum.state = $.verto.ENUM("new requesting trying recovering ringing answering early active held hangup destroy purge");
$.verto.enum.direction = $.verto.ENUM("inbound outbound");
$.verto.enum.message = $.verto.ENUM("display info pvtEvent clientReady");
$.verto.enum = Object.freeze($.verto.enum);
$.verto.saved = [];
$.verto.unloadJobs = [];
var unloadEventName = 'beforeunload';
// Hacks for Mobile Safari
var iOS = ['iPad', 'iPhone', 'iPod'].indexOf(navigator.platform) >= 0;
if (iOS) {
unloadEventName = 'pagehide';
}
$(window).bind(unloadEventName, function() {
for (var f in $.verto.unloadJobs) {
$.verto.unloadJobs[f]();
}
if ($.verto.haltClosure)
return $.verto.haltClosure();
for (var i in $.verto.saved) {
var verto = $.verto.saved[i];
if (verto) {
verto.purge();
verto.logout();
}
}
return $.verto.warnOnUnload;
});
$.verto.videoDevices = [];
$.verto.audioInDevices = [];
$.verto.audioOutDevices = [];
var checkDevices = function(runtime) {
console.info("enumerating devices");
var aud_in = [], aud_out = [], vid = [];
var has_video = 0, has_audio = 0;
var Xstream;
function gotDevices(deviceInfos) {
// Handles being called several times to update labels. Preserve values.
for (var i = 0; i !== deviceInfos.length; ++i) {
var deviceInfo = deviceInfos[i];
var text = "";
console.log(deviceInfo);
console.log(deviceInfo.kind + ": " + deviceInfo.label + " id = " + deviceInfo.deviceId);
if (deviceInfo.kind === 'audioinput') {
text = deviceInfo.label || 'microphone ' + (aud_in.length + 1);
aud_in.push({id: deviceInfo.deviceId, kind: "audio_in", label: text});
} else if (deviceInfo.kind === 'audiooutput') {
text = deviceInfo.label || 'speaker ' + (aud_out.length + 1);
aud_out.push({id: deviceInfo.deviceId, kind: "audio_out", label: text});
} else if (deviceInfo.kind === 'videoinput') {
text = deviceInfo.label || 'camera ' + (vid.length + 1);
vid.push({id: deviceInfo.deviceId, kind: "video", label: text});
} else {
console.log('Some other kind of source/device: ', deviceInfo);
}
}
$.verto.videoDevices = vid;
$.verto.audioInDevices = aud_in;
$.verto.audioOutDevices = aud_out;
console.info("Audio IN Devices", $.verto.audioInDevices);
console.info("Audio Out Devices", $.verto.audioOutDevices);
console.info("Video Devices", $.verto.videoDevices);
if (Xstream) {
Xstream.getTracks().forEach(function(track) {track.stop();});
}
if (runtime) {
runtime(true);
}
}
function handleError(error) {
console.log('device enumeration error: ', error);
if (runtime) runtime(false);
}
function checkTypes(devs) {
for (var i = 0; i !== devs.length; ++i) {
if (devs[i].kind === 'audioinput') {
has_audio++;
} else if (devs[i].kind === 'videoinput') {
has_video++;
}
}
navigator.getUserMedia({ audio: (has_audio > 0 ? true : false), video: (has_video > 0 ? true : false)},
function(stream) {
Xstream = stream;
navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);
},
function(err) {
console.log("The following error occurred: " + err.name);
}
);
}
navigator.mediaDevices.enumerateDevices().then(checkTypes).catch(handleError);
};
$.verto.refreshDevices = function(runtime) {
checkDevices(runtime);
}
$.verto.init = function(obj, runtime) {
if (!obj) {
obj = {};
}
if (!obj.skipPermCheck && !obj.skipDeviceCheck) {
$.FSRTC.checkPerms(function(status) {
checkDevices(runtime);
}, true, true);
} else if (obj.skipPermCheck && !obj.skipDeviceCheck) {
checkDevices(runtime);
} else if (!obj.skipPermCheck && obj.skipDeviceCheck) {
$.FSRTC.checkPerms(function(status) {
runtime(status);
}, true, true);
} else {
runtime(null);
}
}
$.verto.genUUID = function () {
return generateGUID();
}
})(jQuery);