check in raw verto js files and directory structure for development
|
@ -0,0 +1,3 @@
|
|||
This needs to be fleshed out more.
|
||||
It would be nice to develop a web app here that implements a web phone.
|
||||
Do not base it on the demo because it was just tossed together.
|
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 19 KiB |
|
@ -0,0 +1,64 @@
|
|||
.nthChildTest > div:nth-child(odd) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsDataTable > thead > tr > th {
|
||||
|
||||
border-width:4px;
|
||||
padding: 2px;
|
||||
font-size:10pt;
|
||||
text-align: left;
|
||||
|
||||
}
|
||||
|
||||
.jsDataTable > thead > tr > th.notSortable {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.jsDataTable > tbody > tr > td {
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.jsDataTable {
|
||||
font-family: verdana;
|
||||
font-size:10pt;
|
||||
|
||||
}
|
||||
|
||||
.jsDataTable > tbody > tr:nth-child(odd),
|
||||
.jsDataTable > tbody > tr.odd {
|
||||
background-color: #ffffee;
|
||||
}
|
||||
.jsDataTable > tbody > tr:nth-child(even),
|
||||
.jsDataTable > tbody > tr.even {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.jsDataTable > thead th.sortAsc,
|
||||
.jsDataTable > thead th.sortDesc {
|
||||
color:ffffff;
|
||||
|
||||
background-color: #7777ff;
|
||||
background-position: right center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.jsDataTable > thead th.sortAsc {
|
||||
background-image: url(/images/table/asc.png);
|
||||
}
|
||||
|
||||
.jsDataTable > thead th.sortDesc {
|
||||
background-image: url(/images/table/desc.png);
|
||||
}
|
||||
|
||||
.jsDataTable.clickable > tbody > tr,
|
||||
.clickable > .jsDataTable > tbody > tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jsDataTable.clickable > tbody > tr.nonDataRow,
|
||||
.clickable > .jsDataTable > tbody > tr.nonDataRow {
|
||||
cursor: auto;
|
||||
}
|
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 773 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 22 KiB |
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
document.location = "https://" + window.location.hostname + "/verto/verto.html";
|
||||
</script>
|
||||
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*!
|
||||
* jQuery Cookie Plugin v1.3.1
|
||||
* https://github.com/carhartl/jquery-cookie
|
||||
*
|
||||
* Copyright 2013 Klaus Hartl
|
||||
* Released under the MIT license
|
||||
*/
|
||||
(function ($, document, undefined) {
|
||||
|
||||
var pluses = /\+/g;
|
||||
|
||||
function raw(s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
function decoded(s) {
|
||||
return unRfc2068(decodeURIComponent(s.replace(pluses, ' ')));
|
||||
}
|
||||
|
||||
function unRfc2068(value) {
|
||||
if (value.indexOf('"') === 0) {
|
||||
// This is a quoted cookie as according to RFC2068, unescape
|
||||
value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function fromJSON(value) {
|
||||
return config.json ? JSON.parse(value) : value;
|
||||
}
|
||||
|
||||
var config = $.cookie = function (key, value, options) {
|
||||
|
||||
// write
|
||||
if (value !== undefined) {
|
||||
options = $.extend({}, config.defaults, options);
|
||||
|
||||
if (value === null) {
|
||||
options.expires = -1;
|
||||
}
|
||||
|
||||
if (typeof options.expires === 'number') {
|
||||
var days = options.expires, t = options.expires = new Date();
|
||||
t.setDate(t.getDate() + days);
|
||||
}
|
||||
|
||||
value = config.json ? JSON.stringify(value) : String(value);
|
||||
|
||||
return (document.cookie = [
|
||||
encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value),
|
||||
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
||||
options.path ? '; path=' + options.path : '',
|
||||
options.domain ? '; domain=' + options.domain : '',
|
||||
options.secure ? '; secure' : ''
|
||||
].join(''));
|
||||
}
|
||||
|
||||
// read
|
||||
var decode = config.raw ? raw : decoded;
|
||||
var cookies = document.cookie.split('; ');
|
||||
var result = key ? null : {};
|
||||
for (var i = 0, l = cookies.length; i < l; i++) {
|
||||
var parts = cookies[i].split('=');
|
||||
var name = decode(parts.shift());
|
||||
var cookie = decode(parts.join('='));
|
||||
|
||||
if (key && key === name) {
|
||||
result = fromJSON(cookie);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
result[name] = fromJSON(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
config.defaults = {};
|
||||
|
||||
$.removeCookie = function (key, options) {
|
||||
if ($.cookie(key) !== null) {
|
||||
$.cookie(key, null, options);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
})(jQuery, document);
|
|
@ -0,0 +1,23 @@
|
|||
/*! jQuery JSON plugin 2.4.0 | code.google.com/p/jquery-json */
|
||||
(function($){'use strict';var escape=/["\\\x00-\x1f\x7f-\x9f]/g,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},hasOwn=Object.prototype.hasOwnProperty;$.toJSON=typeof JSON==='object'&&JSON.stringify?JSON.stringify:function(o){if(o===null){return'null';}
|
||||
var pairs,k,name,val,type=$.type(o);if(type==='undefined'){return undefined;}
|
||||
if(type==='number'||type==='boolean'){return String(o);}
|
||||
if(type==='string'){return $.quoteString(o);}
|
||||
if(typeof o.toJSON==='function'){return $.toJSON(o.toJSON());}
|
||||
if(type==='date'){var month=o.getUTCMonth()+1,day=o.getUTCDate(),year=o.getUTCFullYear(),hours=o.getUTCHours(),minutes=o.getUTCMinutes(),seconds=o.getUTCSeconds(),milli=o.getUTCMilliseconds();if(month<10){month='0'+month;}
|
||||
if(day<10){day='0'+day;}
|
||||
if(hours<10){hours='0'+hours;}
|
||||
if(minutes<10){minutes='0'+minutes;}
|
||||
if(seconds<10){seconds='0'+seconds;}
|
||||
if(milli<100){milli='0'+milli;}
|
||||
if(milli<10){milli='0'+milli;}
|
||||
return'"'+year+'-'+month+'-'+day+'T'+
|
||||
hours+':'+minutes+':'+seconds+'.'+milli+'Z"';}
|
||||
pairs=[];if($.isArray(o)){for(k=0;k<o.length;k++){pairs.push($.toJSON(o[k])||'null');}
|
||||
return'['+pairs.join(',')+']';}
|
||||
if(typeof o==='object'){for(k in o){if(hasOwn.call(o,k)){type=typeof k;if(type==='number'){name='"'+k+'"';}else if(type==='string'){name=$.quoteString(k);}else{continue;}
|
||||
type=typeof o[k];if(type!=='function'&&type!=='undefined'){val=$.toJSON(o[k]);pairs.push(name+':'+val);}}}
|
||||
return'{'+pairs.join(',')+'}';}};$.evalJSON=typeof JSON==='object'&&JSON.parse?JSON.parse:function(str){return eval('('+str+')');};$.secureEvalJSON=typeof JSON==='object'&&JSON.parse?JSON.parse:function(str){var filtered=str.replace(/\\["\\\/bfnrtu]/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,'');if(/^[\],:{}\s]*$/.test(filtered)){return eval('('+str+')');}
|
||||
throw new SyntaxError('Error parsing JSON, source is not valid.');};$.quoteString=function(str){if(str.match(escape)){return'"'+str.replace(escape,function(a){var c=meta[a];if(typeof c==='string'){return c;}
|
||||
c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16);})+'"';}
|
||||
return'"'+str+'"';};}(jQuery));
|
|
@ -0,0 +1,196 @@
|
|||
|
||||
(function($){function findLine(sdpLines,prefix,substr){return findLineInRange(sdpLines,0,-1,prefix,substr);}
|
||||
function findLineInRange(sdpLines,startLine,endLine,prefix,substr){var realEndLine=(endLine!=-1)?endLine:sdpLines.length;for(var i=startLine;i<realEndLine;++i){if(sdpLines[i].indexOf(prefix)===0){if(!substr||sdpLines[i].toLowerCase().indexOf(substr.toLowerCase())!==-1){return i;}}}
|
||||
return null;}
|
||||
function getCodecPayloadType(sdpLine){var pattern=new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+');var result=sdpLine.match(pattern);return(result&&result.length==2)?result[1]:null;}
|
||||
function setDefaultCodec(mLine,payload){var elements=mLine.split(' ');var newLine=[];var index=0;for(var i=0;i<elements.length;i++){if(index===3)
|
||||
newLine[index++]=payload;if(elements[i]!==payload)newLine[index++]=elements[i];}
|
||||
return newLine.join(' ');}
|
||||
$.FSRTC=function(options){this.options=$.extend({useVideo:null,useStereo:false,userData:null,videoParams:{},callbacks:{onICEComplete:function(){},onICE:function(){},onOfferSDP:function(){}}},options);this.mediaData={SDP:null,profile:{},candidateList:[]};this.constraints={optional:[{'DtlsSrtpKeyAgreement':'true'}],mandatory:{OfferToReceiveAudio:true,OfferToReceiveVideo:this.options.useVideo?true:false,}};if(self.options.useVideo){self.options.useVideo.style.display='none';}
|
||||
setCompat();checkCompat();};$.FSRTC.prototype.useVideo=function(obj){var self=this;if(obj){self.options.useVideo=obj;self.constraints.mandatory.OfferToReceiveVideo=true;}else{self.options.useVideo=null;self.constraints.mandatory.OfferToReceiveVideo=false;}
|
||||
if(self.options.useVideo){self.options.useVideo.style.display='none';}};$.FSRTC.prototype.useStereo=function(on){var self=this;self.options.useStereo=on;};$.FSRTC.prototype.stereoHack=function(sdp){var self=this;if(!self.options.useStereo){return sdp;}
|
||||
var sdpLines=sdp.split('\r\n');var opusIndex=findLine(sdpLines,'a=rtpmap','opus/48000'),opusPayload;if(opusIndex){opusPayload=getCodecPayloadType(sdpLines[opusIndex]);}
|
||||
var fmtpLineIndex=findLine(sdpLines,'a=fmtp:'+opusPayload.toString());if(fmtpLineIndex===null)return sdp;sdpLines[fmtpLineIndex]=sdpLines[fmtpLineIndex].concat('; stereo=1');sdp=sdpLines.join('\r\n');return sdp;};function setCompat(){$.FSRTC.moz=!!navigator.mozGetUserMedia;if(!navigator.getUserMedia){navigator.getUserMedia=navigator.mozGetUserMedia||navigator.webkitGetUserMedia||navigator.msGetUserMedia;}}
|
||||
function checkCompat(){if(!navigator.getUserMedia){alert('This application cannot function in this browser.');return false;}
|
||||
return true;}
|
||||
function onStreamError(self){console.log('There has been a problem retrieving the streams - did you allow access?');}
|
||||
function onStreamSuccess(self){console.log("Stream Success");}
|
||||
function onICE(self,candidate){self.mediaData.candidate=candidate;self.mediaData.candidateList.push(self.mediaData.candidate);doCallback(self,"onICE");}
|
||||
function doCallback(self,func,arg){if(func in self.options.callbacks){self.options.callbacks[func](self,arg);}}
|
||||
function onICEComplete(self,candidate){console.log("ICE Complete");doCallback(self,"onICEComplete");}
|
||||
function onChannelError(self,e){console.error("Channel Error",e);doCallback(self,"onError",e);}
|
||||
function onICESDP(self,sdp){self.mediaData.SDP=self.stereoHack(sdp.sdp);console.log("ICE SDP");doCallback(self,"onICESDP");}
|
||||
function onAnswerSDP(self,sdp){self.answer.SDP=self.stereoHack(sdp.sdp);console.log("ICE ANSWER SDP");doCallback(self,"onAnswerSDP",self.answer.SDP);}
|
||||
function onMessage(self,msg){console.log("Message");doCallback(self,"onICESDP",msg);}
|
||||
function onRemoteStream(self,stream){if(self.options.useVideo){self.options.useVideo.style.display='block';}
|
||||
var element=self.options.useAudio;console.log("REMOTE STREAM",stream,element);if(typeof element.srcObject!=='undefined'){element.srcObject=stream;}else if(typeof element.mozSrcObject!=='undefined'){element.mozSrcObject=stream;}else if(typeof element.src!=='undefined'){element.src=URL.createObjectURL(stream);}else{console.error('Error attaching stream to element.');}
|
||||
self.options.useAudio.play();self.remoteStream=stream;}
|
||||
function onOfferSDP(self,sdp){self.mediaData.SDP=self.stereoHack(sdp.sdp);console.log("Offer SDP");doCallback(self,"onOfferSDP");}
|
||||
$.FSRTC.prototype.answer=function(sdp,onSuccess,onError){this.peer.addAnswerSDP({type:"answer",sdp:sdp},onSuccess,onError);};$.FSRTC.prototype.stop=function(){var self=this;if(self.options.useVideo){self.options.useVideo.style.display='none';}
|
||||
if(self.localStream){self.localStream.stop();self.localStream=null;}
|
||||
if(self.peer){console.log("stopping peer");self.peer.stop();}};$.FSRTC.prototype.createAnswer=function(sdp){var self=this;self.type="answer";self.remoteSDP=sdp;console.debug("inbound sdp: ",sdp);function onSuccess(stream){self.localStream=stream;self.peer=RTCPeerConnection({type:self.type,attachStream:self.localStream,onICE:function(candidate){return onICE(self,candidate);},onICEComplete:function(){return onICEComplete(self);},onRemoteStream:function(stream){return onRemoteStream(self,stream);},onICESDP:function(sdp){return onICESDP(self,sdp);},onChannelError:function(e){return onChannelError(self,e);},constraints:self.constraints,offerSDP:{type:"offer",sdp:self.remoteSDP}});onStreamSuccess(self);}
|
||||
function onError(){onStreamError(self);}
|
||||
getUserMedia({constraints:{audio:true,video:this.options.useVideo?{mandatory:this.options.videoParams,optional:[]}:null},video:this.options.useVideo?true:false,onsuccess:onSuccess,onerror:onError});};$.FSRTC.prototype.call=function(profile){checkCompat();var self=this;self.type="offer";function onSuccess(stream){self.localStream=stream;self.peer=RTCPeerConnection({type:self.type,attachStream:self.localStream,onICE:function(candidate){return onICE(self,candidate);},onICEComplete:function(){return onICEComplete(self);},onRemoteStream:function(stream){return onRemoteStream(self,stream);},onOfferSDP:function(sdp){return onOfferSDP(self,sdp);},onICESDP:function(sdp){return onICESDP(self,sdp);},onChannelError:function(e){return onChannelError(self,e);},constraints:self.constraints});onStreamSuccess(self);}
|
||||
function onError(){onStreamError(self);}
|
||||
getUserMedia({constraints:{audio:true,video:this.options.useVideo?{mandatory:this.options.videoParams,optional:[]}:null},video:this.options.useVideo?true:false,onsuccess:onSuccess,onerror:onError});};window.moz=!!navigator.mozGetUserMedia;function RTCPeerConnection(options){var w=window,PeerConnection=w.mozRTCPeerConnection||w.webkitRTCPeerConnection,SessionDescription=w.mozRTCSessionDescription||w.RTCSessionDescription,IceCandidate=w.mozRTCIceCandidate||w.RTCIceCandidate;var STUN={url:!moz?'stun:stun.l.google.com:19302':'stun:23.21.150.121'};var TURN={url:'turn:homeo@turn.bistri.com:80',credential:'homeo'};var iceServers={iceServers:options.iceServers||[STUN]};if(!moz&&!options.iceServers){if(parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2])>=28)TURN={url:'turn:turn.bistri.com:80',credential:'homeo',username:'homeo'};iceServers.iceServers=[STUN];}
|
||||
var optional={optional:[]};if(!moz){optional.optional=[{DtlsSrtpKeyAgreement:true},{RtpDataChannels:options.onChannelMessage?true:false}];}
|
||||
var peer=new PeerConnection(iceServers,optional);openOffererChannel();var x=0;peer.onicecandidate=function(event){console.log("WTF ICE",event);if(event.candidate){options.onICE(event.candidate);}else{if(options.onICEComplete){options.onICEComplete();}
|
||||
if(options.type=="offer"){if(!moz&&!x&&options.onICESDP){options.onICESDP(peer.localDescription);}}else{if(!x&&options.onICESDP){options.onICESDP(peer.localDescription);}}}};if(options.attachStream)peer.addStream(options.attachStream);if(options.attachStreams&&options.attachStream.length){var streams=options.attachStreams;for(var i=0;i<streams.length;i++){peer.addStream(streams[i]);}}
|
||||
peer.onaddstream=function(event){var remoteMediaStream=event.stream;remoteMediaStream.onended=function(){if(options.onRemoteStreamEnded)options.onRemoteStreamEnded(remoteMediaStream);};if(options.onRemoteStream)options.onRemoteStream(remoteMediaStream);};var constraints=options.constraints||{optional:[],mandatory:{OfferToReceiveAudio:true,OfferToReceiveVideo:true}};function createOffer(){if(!options.onOfferSDP)return;peer.createOffer(function(sessionDescription){sessionDescription.sdp=serializeSdp(sessionDescription.sdp);peer.setLocalDescription(sessionDescription);options.onOfferSDP(sessionDescription);if(moz&&options.onICESDP){options.onICESDP(sessionDescription);}},onSdpError,constraints);}
|
||||
function createAnswer(){if(options.type!="answer")return;peer.setRemoteDescription(new SessionDescription(options.offerSDP),onSdpSuccess,onSdpError);peer.createAnswer(function(sessionDescription){sessionDescription.sdp=serializeSdp(sessionDescription.sdp);peer.setLocalDescription(sessionDescription);if(options.onAnswerSDP){options.onAnswerSDP(sessionDescription);}},onSdpError,constraints);}
|
||||
if((options.onChannelMessage&&!moz)||!options.onChannelMessage){createOffer();createAnswer();}
|
||||
function setBandwidth(sdp){sdp=sdp.replace(/b=AS([^\r\n]+\r\n)/g,'');sdp=sdp.replace(/a=mid:data\r\n/g,'a=mid:data\r\nb=AS:1638400\r\n');return sdp;}
|
||||
function getInteropSDP(sdp){var chars='ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),extractedChars='';function getChars(){extractedChars+=chars[parseInt(Math.random()*40)]||'';if(extractedChars.length<40)getChars();return extractedChars;}
|
||||
if(options.onAnswerSDP)sdp=sdp.replace(/(a=crypto:0 AES_CM_128_HMAC_SHA1_32)(.*?)(\r\n)/g,'');var inline=getChars()+'\r\n'+(extractedChars='');sdp=sdp.indexOf('a=crypto')==-1?sdp.replace(/c=IN/g,'a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:'+inline+'c=IN'):sdp;return sdp;}
|
||||
function serializeSdp(sdp){return sdp;}
|
||||
var channel;function openOffererChannel(){if(!options.onChannelMessage||(moz&&!options.onOfferSDP))return;_openOffererChannel();if(!moz)return;navigator.mozGetUserMedia({audio:true,fake:true},function(stream){peer.addStream(stream);createOffer();},useless);}
|
||||
function _openOffererChannel(){channel=peer.createDataChannel(options.channel||'RTCDataChannel',moz?{}:{reliable:false});if(moz)channel.binaryType='blob';setChannelEvents();}
|
||||
function setChannelEvents(){channel.onmessage=function(event){if(options.onChannelMessage)options.onChannelMessage(event);};channel.onopen=function(){if(options.onChannelOpened)options.onChannelOpened(channel);};channel.onclose=function(event){if(options.onChannelClosed)options.onChannelClosed(event);console.warn('WebRTC DataChannel closed',event);};channel.onerror=function(event){if(options.onChannelError)options.onChannelError(event);console.error('WebRTC DataChannel error',event);};}
|
||||
if(options.onAnswerSDP&&moz&&options.onChannelMessage)openAnswererChannel();function openAnswererChannel(){peer.ondatachannel=function(event){channel=event.channel;channel.binaryType='blob';setChannelEvents();};if(!moz)return;navigator.mozGetUserMedia({audio:true,fake:true},function(stream){peer.addStream(stream);createAnswer();},useless);}
|
||||
function useless(){log('Error in fake:true');}
|
||||
function onSdpSuccess(){}
|
||||
function onSdpError(e){if(options.onChannelError){options.onChannelError(e);}
|
||||
console.error('sdp error:',e);}
|
||||
return{addAnswerSDP:function(sdp,cbSuccess,cbError){peer.setRemoteDescription(new SessionDescription(sdp),cbSuccess?cbSuccess:onSdpSuccess,cbError?cbError:onSdpError);},addICE:function(candidate){peer.addIceCandidate(new IceCandidate({sdpMLineIndex:candidate.sdpMLineIndex,candidate:candidate.candidate}));},peer:peer,channel:channel,sendData:function(message){if(channel){channel.send(message);}},stop:function(){peer.close();if(options.attachStream){options.attachStream.stop();}}};}
|
||||
var video_constraints={mandatory:{},optional:[]};function getUserMedia(options){var n=navigator,media;n.getMedia=n.webkitGetUserMedia||n.mozGetUserMedia;n.getMedia(options.constraints||{audio:true,video:video_constraints},streaming,options.onerror||function(e){console.error(e);});function streaming(stream){var video=options.video;if(video){video[moz?'mozSrcObject':'src']=moz?stream:window.webkitURL.createObjectURL(stream);}
|
||||
if(options.onsuccess){options.onsuccess(stream);}
|
||||
media=stream;}
|
||||
return media;}})(jQuery);(function($){$.JsonRpcClient=function(options){var self=this;this.options=$.extend({ajaxUrl:null,socketUrl:null,onmessage:null,login:null,passwd:null,sessid:null,getSocket:function(onmessage_cb){return self._getSocket(onmessage_cb);}},options);this.wsOnMessage=function(event){self._wsOnMessage(event);};};$.JsonRpcClient.prototype._ws_socket=null;$.JsonRpcClient.prototype._ws_callbacks={};$.JsonRpcClient.prototype._current_id=1;$.JsonRpcClient.prototype.call=function(method,params,success_cb,error_cb){if(!params){params={};}
|
||||
if(this.options.sessid){params.sessid=this.options.sessid;}
|
||||
var request={jsonrpc:'2.0',method:method,params:params,id:this._current_id++};if(!success_cb){success_cb=function(e){console.log("Success: ",e);};}
|
||||
if(!error_cb){error_cb=function(e){console.log("Error: ",e);};}
|
||||
var socket=this.options.getSocket(this.wsOnMessage);if(socket!==null){this._wsCall(socket,request,success_cb,error_cb);return;}
|
||||
if(this.options.ajaxUrl===null){throw"$.JsonRpcClient.call used with no websocket and no http endpoint.";}
|
||||
$.ajax({type:'POST',url:this.options.ajaxUrl,data:$.toJSON(request),dataType:'json',cache:false,success:function(data){if('error'in data)error_cb(data.error,this);success_cb(data.result,this);},error:function(jqXHR,textStatus,errorThrown){try{var response=$.parseJSON(jqXHR.responseText);if('console'in window)console.log(response);error_cb(response.error,this);}
|
||||
catch(err){error_cb({error:jqXHR.responseText},this);}}});};$.JsonRpcClient.prototype.notify=function(method,params){if(this.options.sessid){params.sessid=this.options.sessid;}
|
||||
var request={jsonrpc:'2.0',method:method,params:params};var socket=this.options.getSocket(this.wsOnMessage);if(socket!==null){this._wsCall(socket,request);return;}
|
||||
if(this.options.ajaxUrl===null){throw"$.JsonRpcClient.notify used with no websocket and no http endpoint.";}
|
||||
$.ajax({type:'POST',url:this.options.ajaxUrl,data:$.toJSON(request),dataType:'json',cache:false});};$.JsonRpcClient.prototype.batch=function(callback,all_done_cb,error_cb){var batch=new $.JsonRpcClient._batchObject(this,all_done_cb,error_cb);callback(batch);batch._execute();};$.JsonRpcClient.prototype.socketReady=function(){if(this._ws_socket===null||this._ws_socket.readyState>1){return false;}
|
||||
return true;}
|
||||
$.JsonRpcClient.prototype.closeSocket=function(){if(self.socketReady()){this._ws_socket.onclose=function(w){console.log("Closing Socket")}
|
||||
this._ws_socket.close();}}
|
||||
$.JsonRpcClient.prototype.loginData=function(params){self.options.login=params.login;self.options.passwd=params.passwd;}
|
||||
$.JsonRpcClient.prototype.connectSocket=function(onmessage_cb){var self=this;if(!self.socketReady()){self.authing=false;this._ws_socket=new WebSocket(this.options.socketUrl);if(this._ws_socket){this._ws_socket.onmessage=onmessage_cb;this._ws_socket.onclose=function(w){if(!self.ws_sleep){self.ws_sleep=2;}
|
||||
self.ws_cnt=0;if(self.options.onWSClose){self.options.onWSClose(self);}
|
||||
console.error("Websocket Lost sleep: "+self.ws_sleep+"sec");setTimeout(function(){console.log("Attempting Reconnection....");self.connectSocket(onmessage_cb);},self.ws_sleep*1000);if(++self.ws_cnt>=150){self.ws_sleep=30;}}
|
||||
this._ws_socket.onopen=function(){this.ws_sleep=2;this.ws_cnt=0;if(self.options.onWSConnect){self.options.onWSConnect(self);}
|
||||
var req;while(req=$.JsonRpcClient.q.pop()){self._ws_socket.send(req);}}}}
|
||||
return this._ws_socket?true:false;}
|
||||
$.JsonRpcClient.prototype._getSocket=function(onmessage_cb){if(this.options.socketUrl===null||!("WebSocket"in window))return null;this.connectSocket(onmessage_cb);return this._ws_socket;};$.JsonRpcClient.q=[];$.JsonRpcClient.prototype._wsCall=function(socket,request,success_cb,error_cb){var request_json=$.toJSON(request);if(socket.readyState<1){self=this;$.JsonRpcClient.q.push(request_json);}
|
||||
else{socket.send(request_json);}
|
||||
if('id'in request&&typeof success_cb!=='undefined'){this._ws_callbacks[request.id]={request:request_json,request_obj:request,success_cb:success_cb,error_cb:error_cb};}};$.JsonRpcClient.prototype._wsOnMessage=function(event){var response;try{response=$.parseJSON(event.data);if(typeof response==='object'&&'jsonrpc'in response&&response.jsonrpc==='2.0'){if('result'in response&&this._ws_callbacks[response.id]){var success_cb=this._ws_callbacks[response.id].success_cb;delete this._ws_callbacks[response.id];success_cb(response.result,this);return;}
|
||||
else if('error'in response&&this._ws_callbacks[response.id]){var error_cb=this._ws_callbacks[response.id].error_cb;var orig_req=this._ws_callbacks[response.id].request;if(!self.authing&&response.error.code==-32000&&self.options.login&&self.options.passwd){self.authing=true;this.call("login",{login:self.options.login,passwd:self.options.passwd},this._ws_callbacks[response.id].request_obj.method=="login"?function(e){self.authing=false;console.log("logged in");delete self._ws_callbacks[response.id];if(self.options.onWSLogin){self.options.onWSLogin(true,self);}}:function(e){self.authing=false;console.log("logged in, resending request id: "+response.id);var socket=self.options.getSocket(self.wsOnMessage);if(socket!==null){socket.send(orig_req);}
|
||||
if(self.options.onWSLogin){self.options.onWSLogin(true,self);}},function(e){console.log("error logging in, request id:",response.id);delete self._ws_callbacks[response.id];error_cb(response.error,this);if(self.options.onWSLogin){self.options.onWSLogin(false,self);}});return;}
|
||||
delete this._ws_callbacks[response.id];error_cb(response.error,this);return;}}}
|
||||
catch(err){console.log("ERROR: "+err);return;}
|
||||
if(typeof this.options.onmessage==='function'){event.eventData=response;if(!event.eventData){event.eventData={};}
|
||||
var reply=this.options.onmessage(event);if(reply&&typeof reply==="object"&&event.eventData.id){var msg={jsonrpc:"2.0",id:event.eventData.id,result:reply};var socket=self.options.getSocket(self.wsOnMessage);if(socket!==null){socket.send($.toJSON(msg));}}}};$.JsonRpcClient._batchObject=function(jsonrpcclient,all_done_cb,error_cb){this._requests=[];this.jsonrpcclient=jsonrpcclient;this.all_done_cb=all_done_cb;this.error_cb=typeof error_cb==='function'?error_cb:function(){};};$.JsonRpcClient._batchObject.prototype.call=function(method,params,success_cb,error_cb){if(!params){params={};}
|
||||
if(this.options.sessid){params.sessid=this.options.sessid;}
|
||||
if(!success_cb){success_cb=function(e){console.log("Success: ",e);};}
|
||||
if(!error_cb){error_cb=function(e){console.log("Error: ",e);};}
|
||||
this._requests.push({request:{jsonrpc:'2.0',method:method,params:params,id:this.jsonrpcclient._current_id++},success_cb:success_cb,error_cb:error_cb});};$.JsonRpcClient._batchObject.prototype.notify=function(method,params){if(this.options.sessid){params.sessid=this.options.sessid;}
|
||||
this._requests.push({request:{jsonrpc:'2.0',method:method,params:params}});};$.JsonRpcClient._batchObject.prototype._execute=function(){var self=this;if(this._requests.length===0)return;var batch_request=[];var handlers={};var socket=self.jsonrpcclient.options.getSocket(self.jsonrpcclient.wsOnMessage);if(socket!==null){for(var i=0;i<this._requests.length;i++){var call=this._requests[i];var success_cb=('success_cb'in call)?call.success_cb:undefined;var error_cb=('error_cb'in call)?call.error_cb:undefined;self.jsonrpcclient._wsCall(socket,call.request,success_cb,error_cb);}
|
||||
if(typeof all_done_cb==='function')all_done_cb(result);return;}
|
||||
for(var i=0;i<this._requests.length;i++){var call=this._requests[i];batch_request.push(call.request);if('id'in call.request){handlers[call.request.id]={success_cb:call.success_cb,error_cb:call.error_cb};}}
|
||||
var success_cb=function(data){self._batchCb(data,handlers,self.all_done_cb);};if(self.jsonrpcclient.options.ajaxUrl===null){throw"$.JsonRpcClient.batch used with no websocket and no http endpoint.";}
|
||||
$.ajax({url:self.jsonrpcclient.options.ajaxUrl,data:$.toJSON(batch_request),dataType:'json',cache:false,type:'POST',error:function(jqXHR,textStatus,errorThrown){self.error_cb(jqXHR,textStatus,errorThrown);},success:success_cb});};$.JsonRpcClient._batchObject.prototype._batchCb=function(result,handlers,all_done_cb){for(var i=0;i<result.length;i++){var response=result[i];if('error'in response){if(response.id===null||!(response.id in handlers)){if('console'in window)console.log(response);}
|
||||
else handlers[response.id].error_cb(response.error,this);}
|
||||
else{if(!(response.id in handlers)&&'console'in window)console.log(response);else handlers[response.id].success_cb(response.result,this);}}
|
||||
if(typeof all_done_cb==='function')all_done_cb(result);};})(jQuery);(function($){var generateGUID=(typeof(window.crypto)!=='undefined'&&typeof(window.crypto.getRandomValues)!=='undefined')?function(){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(){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);});};$.verto=function(options,callbacks){var verto=this;$.verto.saved.push(verto);verto.options=$.extend({login:null,passwd:null,socketUrl:null,tag:null,videoParams:{},ringSleep:6000},options);verto.sessid=$.cookie('verto_session_uuid')||generateGUID();$.cookie('verto_session_uuid',verto.sessid,{expires:1});verto.dialogs={};verto.callbacks=callbacks||{};verto.eventSUBS={};verto.rpcClient=new $.JsonRpcClient({login:verto.options.login,passwd:verto.options.passwd,socketUrl:verto.options.socketUrl,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();}});if(verto.options.ringFile&&verto.options.tag){verto.ringer=$("#"+verto.options.tag);}
|
||||
verto.rpcClient.call('login',{});};$.verto.prototype.loginData=function(params){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();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":break;}};$.verto.prototype.sendMethod=function(method,params){var verto=this;verto.rpcClient.call(method,params,function(e){verto.processReply(method,true,e);},function(e){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;}
|
||||
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.params.callID){var dialog=verto.dialogs[data.params.callID];if(dialog){switch(data.method){case'verto.bye':dialog.hangup();break;case'verto.answer':dialog.handleAnswer(data.params.sdp);break;case'verto.media':dialog.handleMedia(data.params.sdp);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",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);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.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&&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;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;hashArray.call(la);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)){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});}}};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;if(packet.name!=la.name){return;}
|
||||
console.error("READ:",packet);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.bootstrap=function(obj){var self=la;self.clear();self.broadcast(self.context,{liveArray:{command:"bootstrap",context:self.context,name:self.name,obj:obj}});};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);};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;}}
|
||||
try{switch(args.action){case"bootObj":if(!args.data){console.error("missing data");return;}
|
||||
dt.fnClearTable();dt.fnAddData(obj.asArray());dt.fnAdjustColumnSizing();break;case"add":if(!args.data){console.error("missing data");return;}
|
||||
if(args.redraw>-1){dt.fnClearTable();dt.fnAddData(obj.asArray());}else{dt.fnAddData(args.data);}
|
||||
dt.fnAdjustColumnSizing();break;case"modify":if(!args.data){return;}
|
||||
console.debug(args,index);dt.fnUpdate(args.data,index);dt.fnAdjustColumnSizing();break;case"del":dt.fnDeleteRow(index);dt.fnAdjustColumnSizing();break;case"clear":dt.fnClearTable();break;case"reorder":dt.fnClearTable();dt.fnAddData(obj.asArray());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;}
|
||||
if(config.onChange){config.onChange(obj,args);}};la.onChange(la,{action:"init"});};$.verto.dialog=function(direction,verto,params){var dialog=this;dialog.params=$.extend({useVideo:verto.options.useVideo,useStereo:verto.options.useStereo,tag:verto.options.tag},params);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;if(dialog.params.callID){dialog.callID=dialog.params.callID;}else{dialog.callID=dialog.params.callID=generateGUID();}
|
||||
if(dialog.params.tag){dialog.audioStream=document.getElementById(dialog.params.tag);if(dialog.params.useVideo){dialog.videoStream=dialog.audioStream;}}
|
||||
dialog.verto.dialogs[dialog.callID]=dialog;var RTCcallbacks={};if(dialog.direction==$.verto.enum.direction.inbound){dialog.params.remote_caller_id_name=dialog.params.caller_id_name;dialog.params.remote_caller_id_number=dialog.params.caller_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){if(rtc.type=="offer"){console.log("offer",rtc.mediaData.SDP);dialog.setState($.verto.enum.state.requesting);dialog.sendMethod("verto.invite",{sdp:rtc.mediaData.SDP});}else{dialog.setState($.verto.enum.state.answering);dialog.sendMethod(dialog.attach?"verto.attach":"verto.answer",{sdp:dialog.rtc.mediaData.SDP});}};RTCcallbacks.onICE=function(rtc){if(rtc.type=="offer"){console.log("offer",rtc.mediaData.candidate);return;}};RTCcallbacks.onError=function(e){console.error("ERROR:",e);dialog.hangup();};dialog.rtc=new $.FSRTC({callbacks:RTCcallbacks,useVideo:dialog.videoStream,useAudio:dialog.audioStream,useStereo:dialog.params.useStereo,videoParams:verto.options.videoParams,});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;}
|
||||
obj.dialogParams[i]=dialog.params[i];}
|
||||
dialog.verto.rpcClient.call(method,obj,function(e){dialog.processReply(method,true,e);},function(e){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;}
|
||||
$.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.error("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.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:delete verto.dialogs[dialog.callID];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(sdp){var dialog=this;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);};$.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.transfer=function(dest,params){var dialog=this;if(dest){cur_call.sendMethod("verto.modify",{action:"transfer",destination:dest,params:params});}};$.verto.dialog.prototype.hold=function(params){var dialog=this;cur_call.sendMethod("verto.modify",{action:"hold",params:params});};$.verto.dialog.prototype.unhold=function(params){var dialog=this;cur_call.sendMethod("verto.modify",{action:"unhold",params:params});};$.verto.dialog.prototype.toggleHold=function(params){var dialog=this;cur_call.sendMethod("verto.modify",{action:"toggleHold",params:params});};$.verto.dialog.prototype.message=function(msg){var dialog=this;var err=0;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){if(params.useVideo){dialog.useVideo(true);}}
|
||||
dialog.rtc.createAnswer(dialog.params.sdp);dialog.answered=true;}};$.verto.dialog.prototype.handleAnswer=function(sdp){var dialog=this;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{dialog.rtc.answer(sdp,function(){dialog.setState($.verto.enum.state.active);},function(e){console.error(e);dialog.hangup();});console.log("ANSWER SDP",sdp);}};$.verto.dialog.prototype.cidString=function(enc){var dialog=this;var party=dialog.params.remote_caller_id_name+(enc?" <":" <")+dialog.params.remote_caller_id_number+(enc?">":">");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.msg);};$.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(sdp){var dialog=this;if(dialog.state.val>=$.verto.enum.state.early.val){return;}
|
||||
dialog.rtc.answer(sdp,function(){dialog.setState($.verto.enum.state.early);},function(e){console.error(e);dialog.hangup();});console.log("EARLY SDP",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,ringing:1,destroy:1,answering:1},requesting:{trying:1,hangup:1},trying:{active:1,early:1,hangup:1},ringing:{answering:1,hangup:1},answering:{active:1,hangup:1},active:{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 ringing answering early active held hangup destroy purge");$.verto.enum.direction=$.verto.ENUM("inbound outbound");$.verto.enum.message=$.verto.ENUM("display info pvtEvent");$.verto.enum=Object.freeze($.verto.enum);$.verto.saved=[];$(window).bind('beforeunload',function(){for(var i in $.verto.saved){var verto=$.verto.saved[i];if(verto){verto.logout();verto.purge();}}
|
||||
return $.verto.warnOnUnload;});})(jQuery);
|
|
@ -0,0 +1,261 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/jquery.mobile-1.3.2.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="css/jsontable.css" />
|
||||
<link rel="shortcut icon" href="favicon.ico" />
|
||||
<meta charset="utf-8" />
|
||||
<title>FreeSWITCH Verto Demo</title>
|
||||
|
||||
<style type="text/css">
|
||||
.pageheader {
|
||||
font-size: 22px;
|
||||
font-weight: normal;
|
||||
height: 27px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div data-role="page" id="page-login" align="center">
|
||||
<div data-role="header" class="page-header">
|
||||
FreeSWITCH Verto Demo
|
||||
</div>
|
||||
<br>
|
||||
<a target="_CC2104" href="https://www.cluecon.com"><img border="0" width="300" src="img/cc_banner2014.gif"></a>
|
||||
|
||||
<div data-role="fieldcontain">
|
||||
<label for="name">Login</label>
|
||||
<input type="text" size="20" id="login"/>
|
||||
</div>
|
||||
|
||||
<div data-role="fieldcontain">
|
||||
<label for="name">Password</label>
|
||||
<input type="password" size="20" id="passwd"/>
|
||||
</div>
|
||||
|
||||
<div data-role="fieldcontain">
|
||||
<label for="name">CID Name</label>
|
||||
<input type="text" size="30" id="name"/>
|
||||
</div>
|
||||
|
||||
<div data-role="fieldcontain">
|
||||
<label for="name">CID Number</label>
|
||||
<input type="text" size="20" id="cid"/>
|
||||
</div>
|
||||
|
||||
<div data-role="fieldcontain">
|
||||
<label for="name">Hostname</label>
|
||||
<input type="text" size="20" id="hostName"/>
|
||||
</div>
|
||||
|
||||
<div data-role="fieldcontain">
|
||||
<label for="name">Websocket URL</label>
|
||||
<input type="text" size="20" id="wsURL"/>
|
||||
</div>
|
||||
|
||||
<br><br>
|
||||
<button data-inline="true" id="loginbtn">Log In</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div data-role="page" id="page-incall" align="center">
|
||||
<div data-role="header" id="calltitle" class="pageheader">
|
||||
Verto IN CALL
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<a target="_CC2104" href="https://www.cluecon.com"><img border="0" width="300" src="img/cc_banner2014.gif"></a>
|
||||
<br><br>
|
||||
|
||||
<div id="conf">
|
||||
<div style="color:black;font-family: verdana" align="center" id="mcount"></div>
|
||||
<table cellspacing="0" cellpadding="0" border="0" align="center" id="example" class="jsDataTable">
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="display" style="font-weight:bold;font-size:18px"></div>
|
||||
<br>
|
||||
<div data-role="fieldcontain" id="xferdiv">
|
||||
<input data-mini="true" type="text" id="xferto"><br>
|
||||
<button data-inline="true" id="cancelxferbtn">Cancel Transfer</button>
|
||||
<button data-inline="true" class="startxferbtn">Complete Transfer</button>
|
||||
</div>
|
||||
|
||||
<div id=keypad>
|
||||
<button class="dtmf" data-inline="true">1</button>
|
||||
<button class="dtmf" data-inline="true">2</button>
|
||||
<button class="dtmf" data-inline="true">3</button>
|
||||
<br>
|
||||
<button class="dtmf" data-inline="true">4</button>
|
||||
<button class="dtmf" data-inline="true">5</button>
|
||||
<button class="dtmf" data-inline="true">6</button>
|
||||
<br>
|
||||
<button class="dtmf" data-inline="true">7</button>
|
||||
<button class="dtmf" data-inline="true">8</button>
|
||||
<button class="dtmf" data-inline="true">9</button>
|
||||
<br>
|
||||
<button class="dtmf" data-inline="true">*</button>
|
||||
<button class="dtmf" data-inline="true">0</button>
|
||||
<button class="dtmf" data-inline="true">#</button>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<button data-inline="true" id="hold">HOLD</button>
|
||||
<button data-inline="true" id="hupbtn">End Call</button>
|
||||
<button data-inline="true" class="startxferbtn">Transfer</button>
|
||||
|
||||
<div id="media">
|
||||
<video width=800 id="webcam" autoplay="autoplay" hidden="true"></video>
|
||||
</div>
|
||||
<br><br>
|
||||
</div>
|
||||
|
||||
<div data-role="page" id="page-main" align="center">
|
||||
<div data-role="header" class="pageheader">
|
||||
FreeSWITCH Verto Demo
|
||||
</div>
|
||||
<br>
|
||||
<a target="_CC2104" href="https://www.cluecon.com"><img border="0" width="300" src="img/cc_banner2014.gif"></a>
|
||||
|
||||
<div id="offline">
|
||||
<h2>LOGGING IN</h2>
|
||||
<img src="images/login.gif"></img>
|
||||
<div id="errordisplay" style="font-weight:bold;font-size:18px;color:#ae0000"></div>
|
||||
</div>
|
||||
|
||||
<div id="online" align="center">
|
||||
<div id="text"></div>
|
||||
<div data-role="fieldcontain">
|
||||
<input type="text" id="ext"/><br>
|
||||
<div id="dialpad">
|
||||
<button class="dialbtn" data-inline="true">1</button>
|
||||
<button class="dialbtn" data-inline="true">2</button>
|
||||
<button class="dialbtn" data-inline="true">3</button>
|
||||
<br>
|
||||
<button class="dialbtn" data-inline="true">4</button>
|
||||
<button class="dialbtn" data-inline="true">5</button>
|
||||
<button class="dialbtn" data-inline="true">6</button>
|
||||
<br>
|
||||
<button class="dialbtn" data-inline="true">7</button>
|
||||
<button class="dialbtn" data-inline="true">8</button>
|
||||
<button class="dialbtn" data-inline="true">9</button>
|
||||
<br>
|
||||
<button class="dialbtn" data-inline="true">*</button>
|
||||
<button class="dialbtn" data-inline="true">0</button>
|
||||
<button class="dialbtn" data-inline="true">#</button>
|
||||
</div>
|
||||
|
||||
<button data-inline="true" id="clearbtn">Clear</button>
|
||||
<button data-inline="true" id="callbtn">Call</button>
|
||||
<br><br>
|
||||
<button data-inline="true"id="logoutbtn">Log Out</button>
|
||||
</div>
|
||||
<br>
|
||||
<label><input id="use_vid" type="checkbox" value="foo" > Use Video</label>
|
||||
<label><input id="use_stereo" type="checkbox" value="foo" > Stereo Audio</label>
|
||||
<br>
|
||||
<!--
|
||||
<b>TEXT</b><br>To: <input type="text" size="20" id="textto"/> MSG: <input type="text" size="40" id="textmsg"/>
|
||||
<button id="vtxtbtn">Send</button>
|
||||
-->
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<table width=500 style="font-size:11pt;font-face:arial">
|
||||
<tr><td align=center colspan=2><b>FreeSWITCH VERTO WebRTC Demo Directory<Br><br></td></tr>
|
||||
<tr><td width=100><b>Dial</b></td><td><b>Desc</b></td></tr>
|
||||
<tr><td align=left colspan=2><hr></td></tr>
|
||||
|
||||
<tr><td>3d1</td><td> 3D sound demo #1</td></tr>
|
||||
<tr><td>3d2</td><td> 3D sound demo #2</td></tr>
|
||||
|
||||
<tr><td>stereo1</td><td> Stereo sound demo #1</td></tr>
|
||||
<tr><td>stereo2</td><td> Stereo sound demo #2</td></tr>
|
||||
<tr><td>stereo3</td><td> Stereo sound demo #3</td></tr>
|
||||
<tr><td>3500</td><td>Local 48k Stereo Conference</td></tr>
|
||||
<tr><td> </td></tr>
|
||||
<tr><td>cluecon</td><td> ClueCon Hotline</td></tr>
|
||||
<tr><td><number></td><td>Call a US/Canda Number</td></tr>
|
||||
|
||||
<tr><td>vuc</td><td>VoIP Users Conference</td></tr>
|
||||
<tr><td>888</td><td>FreeSWITCH Community Conference</td></tr>
|
||||
|
||||
<tr><td>3300</td><td>Local 48k Conference</td></tr>
|
||||
<tr><td>5000</td><td>Try the Demo IVR</td></tr>
|
||||
<tr><td>9664</td><td>Listen to Hold Music</td></tr>
|
||||
<tr><td>9386</td><td>Funny Prompts</td></tr>
|
||||
<tr><td>9198</td><td>Tetris (tone generator)</td></tr>
|
||||
<tr><td colspan=2><Br>
|
||||
<center>
|
||||
<br><br><br>
|
||||
</td></tr>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
</div><!-- /page -->
|
||||
|
||||
<div data-role="page" id="dialog-logout" data-close-btn="none">
|
||||
<div data-role="header">
|
||||
<h2>Logged Out</h2>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<p>You have been logged out or disconnected from the server.</p>
|
||||
|
||||
<button onclick="$('#dialog-logout').dialog('close')">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-role="page" id="dialog-login-error" data-close-btn="none">
|
||||
<div data-role="header">
|
||||
<h2>Login Error</h2>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<p>Error logging in</p>
|
||||
|
||||
<button onclick="$('#dialog-login-error').dialog('close')">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-role="page" id="dialog-hold" data-close-btn="none">
|
||||
<div data-role="header">
|
||||
<h2>Call On hold</h2>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<p>The call is on hold</p>
|
||||
|
||||
<button onclick="$('#dialog-hold').dialog('close');cur_call.toggleHold();">Resume Call</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-role="page" id="dialog-incoming-call" data-close-btn="none">
|
||||
<div data-role="header">
|
||||
<h2>Incoming Call</h2>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div id="dialog-incoming-call-txt"><p>Incoming Call</p></div>
|
||||
<button id="ansbtn">Answer</button>
|
||||
<div id="vansdiv"><button id="vansbtn">Answer Video</button></div>
|
||||
<button id="declinebtn">Decline</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript" src="js/jquery-2.0.3.min.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.mobile-1.3.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.json-2.4.min.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.cookie.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.dataTables.js"></script>
|
||||
<script type="text/javascript" src="js/verto-min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="verto.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,434 @@
|
|||
'use strict';
|
||||
var cur_call = null;
|
||||
var la = null;
|
||||
var $display = $("#display");
|
||||
var verto;
|
||||
var ringing = false;
|
||||
|
||||
function display(msg) {
|
||||
$("#calltitle").html(msg);
|
||||
}
|
||||
|
||||
function clearLa() {
|
||||
if (la) {
|
||||
la.destroy();
|
||||
la = null;
|
||||
}
|
||||
|
||||
$("#conf").hide();
|
||||
}
|
||||
|
||||
function goto_dialog(where) {
|
||||
$.mobile.changePage("#dialog-" + where, {
|
||||
role: "dialog"
|
||||
});
|
||||
}
|
||||
|
||||
function goto_page(where) {
|
||||
$.mobile.changePage("#page-" + where);
|
||||
}
|
||||
|
||||
var first_login = false;
|
||||
var online_visible = false;
|
||||
function online(on) {
|
||||
if (on) {
|
||||
$("#online").show();
|
||||
$("#offline").hide();
|
||||
first_login = true;
|
||||
} else {
|
||||
if (first_login && online_visible) {
|
||||
goto_dialog("logout");
|
||||
}
|
||||
|
||||
$("#online").hide();
|
||||
$("#offline").show();
|
||||
}
|
||||
|
||||
online_visible = on;
|
||||
}
|
||||
|
||||
function check_vid() {
|
||||
var use_vid = $("#use_vid").is(':checked');
|
||||
return use_vid;
|
||||
}
|
||||
|
||||
var callbacks = {
|
||||
|
||||
onMessage: function(verto, dialog, msg, data) {
|
||||
|
||||
switch (msg) {
|
||||
case $.verto.enum.message.pvtEvent:
|
||||
console.error("pvtEvent", data.pvtData.action);
|
||||
if (data.pvtData) {
|
||||
switch (data.pvtData.action) {
|
||||
case "conference-liveArray-part":
|
||||
clearLa();
|
||||
break;
|
||||
case "conference-liveArray-join":
|
||||
|
||||
$(".jsDataTable").width(check_vid() ? "650px" : "550px");
|
||||
|
||||
la = new $.verto.liveTable(verto, data.pvtData.laChannel, data.pvtData.laName, $('#example'), {
|
||||
subParams: {
|
||||
callID: dialog.callID
|
||||
},
|
||||
|
||||
"onChange": function(obj, args) {
|
||||
//var len = obj.asArray().length;
|
||||
$("#mcount").text("Conference Members: " + " (" + obj.arrayLen() + " Total)");
|
||||
},
|
||||
|
||||
"aaData": [],
|
||||
"aoColumns": [{
|
||||
"sTitle": "ID"
|
||||
},
|
||||
{
|
||||
"sTitle": "Number"
|
||||
},
|
||||
{
|
||||
"sTitle": "Name"
|
||||
},
|
||||
{
|
||||
"sTitle": "Codec"
|
||||
},
|
||||
{
|
||||
"sTitle": "Status",
|
||||
"sWidth": check_vid() ? "300px" : "150px"
|
||||
}],
|
||||
"bAutoWidth": true,
|
||||
"bDestroy": true,
|
||||
"bSort": false,
|
||||
"bInfo": false,
|
||||
"bFilter": false,
|
||||
"bLengthChange": false,
|
||||
"bPaginate": false,
|
||||
"iDisplayLength": 1000,
|
||||
|
||||
"oLanguage": {
|
||||
"sEmptyTable": "The Conference is Empty....."
|
||||
}
|
||||
});
|
||||
|
||||
$("#conf").show();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case $.verto.enum.message.info:
|
||||
$("#text").html("Message from: <b>" + data.from + "</b>:<br>" + "<pre>" + data.body + "</pre>");
|
||||
break;
|
||||
case $.verto.enum.message.display:
|
||||
var party = dialog.params.remote_caller_id_name + "<" + dialog.params.remote_caller_id_number + ">";
|
||||
display("Talking to: " + dialog.cidString());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onDialogState: function(d) {
|
||||
cur_call = d;
|
||||
|
||||
switch (d.state) {
|
||||
case $.verto.enum.state.ringing:
|
||||
display("Call From: " + d.cidString());
|
||||
|
||||
$("#ansbtn").click(function() {
|
||||
ringing = false;
|
||||
cur_call.answer({
|
||||
useStereo: $("#use_stereo").is(':checked')
|
||||
});
|
||||
$('#dialog-incoming-call').dialog('close');
|
||||
});
|
||||
|
||||
$("#declinebtn").click(function() {
|
||||
ringing = false;
|
||||
cur_call.hangup();
|
||||
$('#dialog-incoming-call').dialog('close');
|
||||
});
|
||||
|
||||
ringing = true;
|
||||
|
||||
goto_dialog("incoming-call");
|
||||
$("#dialog-incoming-call-txt").text("Incoming call from: " + d.cidString());
|
||||
|
||||
if (d.params.wantVideo) {
|
||||
$("#vansbtn").click(function() {
|
||||
ringing = false;
|
||||
$("#use_vid").prop("checked", true);
|
||||
cur_call.answer({
|
||||
useVideo: true,
|
||||
useStereo: $("#use_stereo").is(':checked')
|
||||
});
|
||||
});
|
||||
// the buttons in this jquery mobile wont hide .. gotta wrap them in a div as a workaround
|
||||
$("#vansdiv").show();
|
||||
} else {
|
||||
$("#vansdiv").hide();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case $.verto.enum.state.active:
|
||||
display("Talking to: " + d.cidString());
|
||||
goto_page("incall");
|
||||
break;
|
||||
case $.verto.enum.state.hangup:
|
||||
case $.verto.enum.state.destroy:
|
||||
clearLa();
|
||||
goto_page("main");
|
||||
cur_call = null;
|
||||
break;
|
||||
case $.verto.enum.state.held:
|
||||
break;
|
||||
default:
|
||||
display("");
|
||||
break;
|
||||
}
|
||||
},
|
||||
onWSLogin: function(v, success) {
|
||||
display("");
|
||||
|
||||
if (success) {
|
||||
online(true);
|
||||
|
||||
verto.subscribe("presence", {
|
||||
handler: function(v, e) {
|
||||
console.error("PRESENCE:", e);
|
||||
}
|
||||
});
|
||||
if (!window.location.hash) {
|
||||
goto_page("main");
|
||||
}
|
||||
} else {
|
||||
goto_page("login");
|
||||
goto_dialog("login-error");
|
||||
}
|
||||
|
||||
},
|
||||
onWSClose: function(v, success) {
|
||||
if ($('#online').is(':visible')) {
|
||||
display("");
|
||||
online(false);
|
||||
}
|
||||
var today = new Date();
|
||||
$("#errordisplay").html("Connection Error.<br>Last Attempt: " + today);
|
||||
goto_page("main");
|
||||
},
|
||||
|
||||
onEvent: function(v, e) {
|
||||
console.debug("w00t", e);
|
||||
},
|
||||
};
|
||||
|
||||
$("#hold").click(function(e) {
|
||||
cur_call.toggleHold();
|
||||
goto_dialog("hold");
|
||||
});
|
||||
|
||||
$("#cancelxferbtn").click(function(e) {
|
||||
$("#xferto").val("");
|
||||
$("#xferdiv").hide();
|
||||
});
|
||||
|
||||
$(".startxferbtn").click(function(e) {
|
||||
if ($('#xferdiv').is(':visible')) {
|
||||
var xfer = $("#xferto").val();
|
||||
if (xfer) {
|
||||
cur_call.transfer(xfer);
|
||||
}
|
||||
$("#xferto").val("");
|
||||
$("#xferdiv").hide();
|
||||
} else {
|
||||
$("#xferdiv").show();
|
||||
}
|
||||
});
|
||||
|
||||
$("#clearbtn").click(function(e) {
|
||||
$("#ext").val("");
|
||||
});
|
||||
|
||||
$(".dialbtn").click(function(e) {
|
||||
$("#ext").val($("#ext").val() + e.currentTarget.textContent);
|
||||
});
|
||||
|
||||
$(".dtmf").click(function(e) {
|
||||
if ($('#xferdiv').is(':visible')) {
|
||||
$("#xferto").val($("#xferto").val() + e.currentTarget.textContent);
|
||||
} else {
|
||||
cur_call.dtmf(e.currentTarget.textContent);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$("#hupbtn").click(function() {
|
||||
verto.hangup();
|
||||
cur_call = null;
|
||||
});
|
||||
|
||||
$("#webcam").click(function() {
|
||||
check_vid();
|
||||
});
|
||||
|
||||
$("#callbtn").click(function() {
|
||||
$('#ext').trigger('change');
|
||||
|
||||
if (cur_call) {
|
||||
return;
|
||||
}
|
||||
|
||||
cur_call = verto.newCall({
|
||||
destination_number: $("#ext").val(),
|
||||
caller_id_name: $("#name").val(),
|
||||
caller_id_number: $("#cid").val(),
|
||||
useVideo: check_vid(),
|
||||
useStereo: $("#use_stereo").is(':checked')
|
||||
});
|
||||
});
|
||||
|
||||
function pop(id, cname, dft) {
|
||||
var tmp = $.cookie(cname) || dft;
|
||||
$.cookie(cname, tmp, {
|
||||
expires: 365
|
||||
});
|
||||
$(id).val(tmp).change(function() {
|
||||
$.cookie(cname, $(id).val(), {
|
||||
expires: 365
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function init() {
|
||||
pop("#ext", "verto_demo_ext", "3500");
|
||||
pop("#name", "verto_demo_name", "FreeSWITCH User");
|
||||
pop("#cid", "verto_demo_cid", "1008");
|
||||
pop("#textto", "verto_demo_textto", "1000");
|
||||
|
||||
pop("#login", "verto_demo_login", "1008");
|
||||
pop("#passwd", "verto_demo_passwd", "1234");
|
||||
|
||||
pop("#hostName", "verto_demo_hostname", window.location.hostname);
|
||||
pop("#wsURL", "verto_demo_wsurl", "wss://" + window.location.hostname + ":8082");
|
||||
|
||||
var tmp = $.cookie("verto_demo_vid_checked") || "false";
|
||||
$.cookie("verto_demo_vid_checked", tmp, {
|
||||
expires: 365
|
||||
});
|
||||
|
||||
$("#use_vid").prop("checked", tmp === "true").change(function(e) {
|
||||
tmp = $("#use_vid").is(':checked');
|
||||
$.cookie("verto_demo_vid_checked", tmp ? "true" : "false", {
|
||||
expires: 365
|
||||
});
|
||||
});
|
||||
|
||||
tmp = $.cookie("verto_demo_stereo_checked") || "false";
|
||||
$.cookie("verto_demo_stereo_checked", tmp, {
|
||||
expires: 365
|
||||
});
|
||||
|
||||
$("#use_stereo").prop("checked", tmp === "true").change(function(e) {
|
||||
tmp = $("#use_stereo").is(':checked');
|
||||
$.cookie("verto_demo_stereo_checked", tmp ? "true" : "false", {
|
||||
expires: 365
|
||||
});
|
||||
});
|
||||
|
||||
verto = new $.verto({
|
||||
login: $("#login").val() + "@" + $("#hostName").val(),
|
||||
passwd: $("#passwd").val(),
|
||||
socketUrl: $("#wsURL").val(),
|
||||
tag: "webcam",
|
||||
ringFile: "sounds/bell_ring2.wav",
|
||||
videoParams: {
|
||||
"minWidth": "1280",
|
||||
"minHeight": "720"
|
||||
}
|
||||
},
|
||||
callbacks);
|
||||
|
||||
$("#login").change(function(e) {
|
||||
$("#cid").val(e.currentTarget.value);
|
||||
$.cookie("verto_demo_cid", e.currentTarget.value, {
|
||||
expires: 365
|
||||
});
|
||||
});
|
||||
|
||||
$("#vtxtbtn").click(function() {
|
||||
verto.message({
|
||||
to: $("#textto").val(),
|
||||
body: $("#textmsg").val()
|
||||
});
|
||||
$("#textmsg").val("");
|
||||
});
|
||||
|
||||
$("#logoutbtn").click(function() {
|
||||
verto.logout();
|
||||
goto_page("login");
|
||||
online(false);
|
||||
});
|
||||
|
||||
$("#loginbtn").click(function() {
|
||||
online(false);
|
||||
verto.loginData({
|
||||
login: $("#login").val() + "@" + $("#hostName").val(),
|
||||
passwd: $("#passwd").val()
|
||||
});
|
||||
verto.login();
|
||||
goto_page("main");
|
||||
});
|
||||
|
||||
$("#xferdiv").hide();
|
||||
$("#webcam").hide();
|
||||
|
||||
online(false);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
init();
|
||||
$("#page-incall").on("pagebeforechange", function(event) {});
|
||||
});
|
||||
|
||||
$(document).bind("pagebeforechange", function(e, data) {
|
||||
if (typeof(data.toPage) !== "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (window.location.hash) {
|
||||
|
||||
case "#page-incall":
|
||||
|
||||
console.error(e, data);
|
||||
setTimeout(function() {
|
||||
if (!cur_call) {
|
||||
goto_page("main");
|
||||
}
|
||||
},
|
||||
10000);
|
||||
break;
|
||||
|
||||
case "#page-main":
|
||||
|
||||
console.error(e, data);
|
||||
setTimeout(function() {
|
||||
if (cur_call && !ringing) {
|
||||
goto_page("incall");
|
||||
}
|
||||
},
|
||||
2000);
|
||||
break;
|
||||
|
||||
case "#page-login":
|
||||
|
||||
setTimeout(function() {
|
||||
if (online_visible) {
|
||||
goto_page("main");
|
||||
}
|
||||
},
|
||||
1000);
|
||||
break;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
JSFILES=src/jquery.FSRTC.js src/jquery.jsonrpcclient.js src/jquery.verto.js
|
||||
|
||||
all: jsmin verto-min.js
|
||||
|
||||
jsmin: jsmin.c
|
||||
$(CC) $< -o $@
|
||||
|
||||
verto-min.js: jsmin $(JSFILES)
|
||||
cat $(JSFILES) | ./jsmin > $@
|
||||
|
||||
clean:
|
||||
rm -f verto-min.js jsmin *~
|
||||
|
||||
install-demo: all
|
||||
cp verto-min.js ../demo/js
|
|
@ -0,0 +1,10 @@
|
|||
This file needs to say more.
|
||||
Documentation for the api needs to be developed with jsdoc-toolkit http://pulkitgoyal.in/documenting-jquery-plugins-jsdoc-toolkit/
|
||||
|
||||
Dependancies for DEMO
|
||||
jquery-2.0.3.min.js
|
||||
jquery-2.0.3.min.map
|
||||
jquery.cookie.js
|
||||
jquery.dataTables.js
|
||||
jquery.json-2.4.min.js
|
||||
jquery.mobile-1.3.2.min.js
|
|
@ -0,0 +1,306 @@
|
|||
/* jsmin.c
|
||||
2013-03-29
|
||||
|
||||
Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int theA;
|
||||
static int theB;
|
||||
static int theLookahead = EOF;
|
||||
static int theX = EOF;
|
||||
static int theY = EOF;
|
||||
|
||||
|
||||
static void
|
||||
error(char* s)
|
||||
{
|
||||
fputs("JSMIN Error: ", stderr);
|
||||
fputs(s, stderr);
|
||||
fputc('\n', stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* isAlphanum -- return true if the character is a letter, digit, underscore,
|
||||
dollar sign, or non-ASCII character.
|
||||
*/
|
||||
|
||||
static int
|
||||
isAlphanum(int c)
|
||||
{
|
||||
return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
|
||||
(c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' ||
|
||||
c > 126);
|
||||
}
|
||||
|
||||
|
||||
/* get -- return the next character from stdin. Watch out for lookahead. If
|
||||
the character is a control character, translate it to a space or
|
||||
linefeed.
|
||||
*/
|
||||
|
||||
static int
|
||||
get()
|
||||
{
|
||||
int c = theLookahead;
|
||||
theLookahead = EOF;
|
||||
if (c == EOF) {
|
||||
c = getc(stdin);
|
||||
}
|
||||
if (c >= ' ' || c == '\n' || c == EOF) {
|
||||
return c;
|
||||
}
|
||||
if (c == '\r') {
|
||||
return '\n';
|
||||
}
|
||||
return ' ';
|
||||
}
|
||||
|
||||
|
||||
/* peek -- get the next character without getting it.
|
||||
*/
|
||||
|
||||
static int
|
||||
peek()
|
||||
{
|
||||
theLookahead = get();
|
||||
return theLookahead;
|
||||
}
|
||||
|
||||
|
||||
/* next -- get the next character, excluding comments. peek() is used to see
|
||||
if a '/' is followed by a '/' or '*'.
|
||||
*/
|
||||
|
||||
static int
|
||||
next()
|
||||
{
|
||||
int c = get();
|
||||
if (c == '/') {
|
||||
switch (peek()) {
|
||||
case '/':
|
||||
for (;;) {
|
||||
c = get();
|
||||
if (c <= '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '*':
|
||||
get();
|
||||
while (c != ' ') {
|
||||
switch (get()) {
|
||||
case '*':
|
||||
if (peek() == '/') {
|
||||
get();
|
||||
c = ' ';
|
||||
}
|
||||
break;
|
||||
case EOF:
|
||||
error("Unterminated comment.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
theY = theX;
|
||||
theX = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/* action -- do something! What you do is determined by the argument:
|
||||
1 Output A. Copy B to A. Get the next B.
|
||||
2 Copy B to A. Get the next B. (Delete A).
|
||||
3 Get the next B. (Delete B).
|
||||
action treats a string as a single character. Wow!
|
||||
action recognizes a regular expression if it is preceded by ( or , or =.
|
||||
*/
|
||||
|
||||
static void
|
||||
action(int d)
|
||||
{
|
||||
switch (d) {
|
||||
case 1:
|
||||
putc(theA, stdout);
|
||||
if (
|
||||
(theY == '\n' || theY == ' ') &&
|
||||
(theA == '+' || theA == '-' || theA == '*' || theA == '/') &&
|
||||
(theB == '+' || theB == '-' || theB == '*' || theB == '/')
|
||||
) {
|
||||
putc(theY, stdout);
|
||||
}
|
||||
case 2:
|
||||
theA = theB;
|
||||
if (theA == '\'' || theA == '"' || theA == '`') {
|
||||
for (;;) {
|
||||
putc(theA, stdout);
|
||||
theA = get();
|
||||
if (theA == theB) {
|
||||
break;
|
||||
}
|
||||
if (theA == '\\') {
|
||||
putc(theA, stdout);
|
||||
theA = get();
|
||||
}
|
||||
if (theA == EOF) {
|
||||
error("Unterminated string literal.");
|
||||
}
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
theB = next();
|
||||
if (theB == '/' && (
|
||||
theA == '(' || theA == ',' || theA == '=' || theA == ':' ||
|
||||
theA == '[' || theA == '!' || theA == '&' || theA == '|' ||
|
||||
theA == '?' || theA == '+' || theA == '-' || theA == '~' ||
|
||||
theA == '*' || theA == '/' || theA == '{' || theA == '\n'
|
||||
)) {
|
||||
putc(theA, stdout);
|
||||
if (theA == '/' || theA == '*') {
|
||||
putc(' ', stdout);
|
||||
}
|
||||
putc(theB, stdout);
|
||||
for (;;) {
|
||||
theA = get();
|
||||
if (theA == '[') {
|
||||
for (;;) {
|
||||
putc(theA, stdout);
|
||||
theA = get();
|
||||
if (theA == ']') {
|
||||
break;
|
||||
}
|
||||
if (theA == '\\') {
|
||||
putc(theA, stdout);
|
||||
theA = get();
|
||||
}
|
||||
if (theA == EOF) {
|
||||
error("Unterminated set in Regular Expression literal.");
|
||||
}
|
||||
}
|
||||
} else if (theA == '/') {
|
||||
switch (peek()) {
|
||||
case '/':
|
||||
case '*':
|
||||
error("Unterminated set in Regular Expression literal.");
|
||||
}
|
||||
break;
|
||||
} else if (theA =='\\') {
|
||||
putc(theA, stdout);
|
||||
theA = get();
|
||||
}
|
||||
if (theA == EOF) {
|
||||
error("Unterminated Regular Expression literal.");
|
||||
}
|
||||
putc(theA, stdout);
|
||||
}
|
||||
theB = next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* jsmin -- Copy the input to the output, deleting the characters which are
|
||||
insignificant to JavaScript. Comments will be removed. Tabs will be
|
||||
replaced with spaces. Carriage returns will be replaced with linefeeds.
|
||||
Most spaces and linefeeds will be removed.
|
||||
*/
|
||||
|
||||
static void
|
||||
jsmin()
|
||||
{
|
||||
if (peek() == 0xEF) {
|
||||
get();
|
||||
get();
|
||||
get();
|
||||
}
|
||||
theA = '\n';
|
||||
action(3);
|
||||
while (theA != EOF) {
|
||||
switch (theA) {
|
||||
case ' ':
|
||||
action(isAlphanum(theB) ? 1 : 2);
|
||||
break;
|
||||
case '\n':
|
||||
switch (theB) {
|
||||
case '{':
|
||||
case '[':
|
||||
case '(':
|
||||
case '+':
|
||||
case '-':
|
||||
case '!':
|
||||
case '~':
|
||||
action(1);
|
||||
break;
|
||||
case ' ':
|
||||
action(3);
|
||||
break;
|
||||
default:
|
||||
action(isAlphanum(theB) ? 1 : 2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch (theB) {
|
||||
case ' ':
|
||||
action(isAlphanum(theA) ? 1 : 3);
|
||||
break;
|
||||
case '\n':
|
||||
switch (theA) {
|
||||
case '}':
|
||||
case ']':
|
||||
case ')':
|
||||
case '+':
|
||||
case '-':
|
||||
case '"':
|
||||
case '\'':
|
||||
case '`':
|
||||
action(1);
|
||||
break;
|
||||
default:
|
||||
action(isAlphanum(theA) ? 1 : 3);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
action(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* main -- Output any command line arguments as comments
|
||||
and then minify the input.
|
||||
*/
|
||||
extern int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < argc; i += 1) {
|
||||
fprintf(stdout, "// %s\n", argv[i]);
|
||||
}
|
||||
jsmin();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,758 @@
|
|||
/*
|
||||
* 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.FSRTC.js - WebRTC Glue code
|
||||
*
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
// Find the line in sdpLines that starts with |prefix|, and, if specified,
|
||||
// contains |substr| (case-insensitive search).
|
||||
function findLine(sdpLines, prefix, substr) {
|
||||
return findLineInRange(sdpLines, 0, -1, prefix, substr);
|
||||
}
|
||||
|
||||
// Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix|
|
||||
// and, if specified, contains |substr| (case-insensitive search).
|
||||
function findLineInRange(sdpLines, startLine, endLine, prefix, substr) {
|
||||
var realEndLine = (endLine != -1) ? endLine : sdpLines.length;
|
||||
for (var i = startLine; i < realEndLine; ++i) {
|
||||
if (sdpLines[i].indexOf(prefix) === 0) {
|
||||
if (!substr || sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Gets the codec payload type from an a=rtpmap:X line.
|
||||
function getCodecPayloadType(sdpLine) {
|
||||
var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+');
|
||||
var result = sdpLine.match(pattern);
|
||||
return (result && result.length == 2) ? result[1] : null;
|
||||
}
|
||||
|
||||
// Returns a new m= line with the specified codec as the first one.
|
||||
function setDefaultCodec(mLine, payload) {
|
||||
var elements = mLine.split(' ');
|
||||
var newLine = [];
|
||||
var index = 0;
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
if (index === 3) // Format of media starts from the fourth.
|
||||
newLine[index++] = payload; // Put target payload to the first.
|
||||
if (elements[i] !== payload) newLine[index++] = elements[i];
|
||||
}
|
||||
return newLine.join(' ');
|
||||
}
|
||||
|
||||
$.FSRTC = function(options) {
|
||||
this.options = $.extend({
|
||||
useVideo: null,
|
||||
useStereo: false,
|
||||
userData: null,
|
||||
videoParams: {},
|
||||
callbacks: {
|
||||
onICEComplete: function() {},
|
||||
onICE: function() {},
|
||||
onOfferSDP: function() {}
|
||||
}
|
||||
},
|
||||
options);
|
||||
|
||||
this.mediaData = {
|
||||
SDP: null,
|
||||
profile: {},
|
||||
candidateList: []
|
||||
};
|
||||
|
||||
this.constraints = {
|
||||
optional: [{
|
||||
'DtlsSrtpKeyAgreement': 'true'
|
||||
}],
|
||||
mandatory: {
|
||||
OfferToReceiveAudio: true,
|
||||
OfferToReceiveVideo: this.options.useVideo ? true : false,
|
||||
}
|
||||
};
|
||||
|
||||
if (self.options.useVideo) {
|
||||
self.options.useVideo.style.display = 'none';
|
||||
}
|
||||
|
||||
setCompat();
|
||||
checkCompat();
|
||||
};
|
||||
|
||||
$.FSRTC.prototype.useVideo = function(obj) {
|
||||
var self = this;
|
||||
|
||||
if (obj) {
|
||||
self.options.useVideo = obj;
|
||||
self.constraints.mandatory.OfferToReceiveVideo = true;
|
||||
} else {
|
||||
self.options.useVideo = null;
|
||||
self.constraints.mandatory.OfferToReceiveVideo = false;
|
||||
}
|
||||
|
||||
if (self.options.useVideo) {
|
||||
self.options.useVideo.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
$.FSRTC.prototype.useStereo = function(on) {
|
||||
var self = this;
|
||||
self.options.useStereo = on;
|
||||
};
|
||||
|
||||
// Sets Opus in stereo if stereo is enabled, by adding the stereo=1 fmtp param.
|
||||
$.FSRTC.prototype.stereoHack = function(sdp) {
|
||||
var self = this;
|
||||
|
||||
if (!self.options.useStereo) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
var sdpLines = sdp.split('\r\n');
|
||||
|
||||
// Find opus payload.
|
||||
var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000'),
|
||||
opusPayload;
|
||||
if (opusIndex) {
|
||||
opusPayload = getCodecPayloadType(sdpLines[opusIndex]);
|
||||
}
|
||||
|
||||
// Find the payload in fmtp line.
|
||||
var fmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + opusPayload.toString());
|
||||
if (fmtpLineIndex === null) return sdp;
|
||||
|
||||
// Append stereo=1 to fmtp line.
|
||||
sdpLines[fmtpLineIndex] = sdpLines[fmtpLineIndex].concat('; stereo=1');
|
||||
|
||||
sdp = sdpLines.join('\r\n');
|
||||
return sdp;
|
||||
};
|
||||
|
||||
function setCompat() {
|
||||
$.FSRTC.moz = !!navigator.mozGetUserMedia;
|
||||
//navigator.getUserMedia || (navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia);
|
||||
if (!navigator.getUserMedia) {
|
||||
navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia;
|
||||
}
|
||||
}
|
||||
|
||||
function checkCompat() {
|
||||
if (!navigator.getUserMedia) {
|
||||
alert('This application cannot function in this browser.');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function onStreamError(self) {
|
||||
console.log('There has been a problem retrieving the streams - did you allow access?');
|
||||
|
||||
}
|
||||
|
||||
function onStreamSuccess(self) {
|
||||
console.log("Stream Success");
|
||||
}
|
||||
|
||||
function onICE(self, candidate) {
|
||||
self.mediaData.candidate = candidate;
|
||||
self.mediaData.candidateList.push(self.mediaData.candidate);
|
||||
|
||||
doCallback(self, "onICE");
|
||||
}
|
||||
|
||||
function doCallback(self, func, arg) {
|
||||
if (func in self.options.callbacks) {
|
||||
self.options.callbacks[func](self, arg);
|
||||
}
|
||||
}
|
||||
|
||||
function onICEComplete(self, candidate) {
|
||||
console.log("ICE Complete");
|
||||
doCallback(self, "onICEComplete");
|
||||
}
|
||||
|
||||
function onChannelError(self, e) {
|
||||
console.error("Channel Error", e);
|
||||
doCallback(self, "onError", e);
|
||||
}
|
||||
|
||||
function onICESDP(self, sdp) {
|
||||
self.mediaData.SDP = self.stereoHack(sdp.sdp);
|
||||
console.log("ICE SDP");
|
||||
doCallback(self, "onICESDP");
|
||||
}
|
||||
|
||||
function onAnswerSDP(self, sdp) {
|
||||
self.answer.SDP = self.stereoHack(sdp.sdp);
|
||||
console.log("ICE ANSWER SDP");
|
||||
doCallback(self, "onAnswerSDP", self.answer.SDP);
|
||||
}
|
||||
|
||||
function onMessage(self, msg) {
|
||||
console.log("Message");
|
||||
doCallback(self, "onICESDP", msg);
|
||||
}
|
||||
|
||||
function onRemoteStream(self, stream) {
|
||||
if (self.options.useVideo) {
|
||||
self.options.useVideo.style.display = 'block';
|
||||
}
|
||||
|
||||
var element = self.options.useAudio;
|
||||
console.log("REMOTE STREAM", stream, element);
|
||||
|
||||
if (typeof element.srcObject !== 'undefined') {
|
||||
element.srcObject = stream;
|
||||
} else if (typeof element.mozSrcObject !== 'undefined') {
|
||||
element.mozSrcObject = stream;
|
||||
} else if (typeof element.src !== 'undefined') {
|
||||
element.src = URL.createObjectURL(stream);
|
||||
} else {
|
||||
console.error('Error attaching stream to element.');
|
||||
}
|
||||
|
||||
self.options.useAudio.play();
|
||||
self.remoteStream = stream;
|
||||
}
|
||||
|
||||
function onOfferSDP(self, sdp) {
|
||||
self.mediaData.SDP = self.stereoHack(sdp.sdp);
|
||||
console.log("Offer SDP");
|
||||
doCallback(self, "onOfferSDP");
|
||||
}
|
||||
|
||||
$.FSRTC.prototype.answer = function(sdp, onSuccess, onError) {
|
||||
this.peer.addAnswerSDP({
|
||||
type: "answer",
|
||||
sdp: sdp
|
||||
},
|
||||
onSuccess, onError);
|
||||
};
|
||||
|
||||
$.FSRTC.prototype.stop = function() {
|
||||
var self = this;
|
||||
|
||||
if (self.options.useVideo) {
|
||||
self.options.useVideo.style.display = 'none';
|
||||
}
|
||||
|
||||
if (self.localStream) {
|
||||
self.localStream.stop();
|
||||
self.localStream = null;
|
||||
}
|
||||
|
||||
if (self.peer) {
|
||||
console.log("stopping peer");
|
||||
self.peer.stop();
|
||||
}
|
||||
};
|
||||
|
||||
$.FSRTC.prototype.createAnswer = function(sdp) {
|
||||
var self = this;
|
||||
self.type = "answer";
|
||||
self.remoteSDP = sdp;
|
||||
console.debug("inbound sdp: ", sdp);
|
||||
|
||||
function onSuccess(stream) {
|
||||
self.localStream = stream;
|
||||
|
||||
self.peer = RTCPeerConnection({
|
||||
type: self.type,
|
||||
attachStream: self.localStream,
|
||||
onICE: function(candidate) {
|
||||
return onICE(self, candidate);
|
||||
},
|
||||
onICEComplete: function() {
|
||||
return onICEComplete(self);
|
||||
},
|
||||
onRemoteStream: function(stream) {
|
||||
return onRemoteStream(self, stream);
|
||||
},
|
||||
onICESDP: function(sdp) {
|
||||
return onICESDP(self, sdp);
|
||||
},
|
||||
onChannelError: function(e) {
|
||||
return onChannelError(self, e);
|
||||
},
|
||||
constraints: self.constraints,
|
||||
offerSDP: {
|
||||
type: "offer",
|
||||
sdp: self.remoteSDP
|
||||
}
|
||||
});
|
||||
|
||||
onStreamSuccess(self);
|
||||
}
|
||||
|
||||
function onError() {
|
||||
onStreamError(self);
|
||||
}
|
||||
|
||||
getUserMedia({
|
||||
constraints: {
|
||||
audio: true,
|
||||
video: this.options.useVideo ? {
|
||||
mandatory: this.options.videoParams,
|
||||
optional: []
|
||||
} : null
|
||||
},
|
||||
video: this.options.useVideo ? true : false,
|
||||
onsuccess: onSuccess,
|
||||
onerror: onError
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$.FSRTC.prototype.call = function(profile) {
|
||||
checkCompat();
|
||||
|
||||
var self = this;
|
||||
|
||||
self.type = "offer";
|
||||
|
||||
function onSuccess(stream) {
|
||||
self.localStream = stream;
|
||||
|
||||
self.peer = RTCPeerConnection({
|
||||
type: self.type,
|
||||
attachStream: self.localStream,
|
||||
onICE: function(candidate) {
|
||||
return onICE(self, candidate);
|
||||
},
|
||||
onICEComplete: function() {
|
||||
return onICEComplete(self);
|
||||
},
|
||||
onRemoteStream: function(stream) {
|
||||
return onRemoteStream(self, stream);
|
||||
},
|
||||
onOfferSDP: function(sdp) {
|
||||
return onOfferSDP(self, sdp);
|
||||
},
|
||||
onICESDP: function(sdp) {
|
||||
return onICESDP(self, sdp);
|
||||
},
|
||||
onChannelError: function(e) {
|
||||
return onChannelError(self, e);
|
||||
},
|
||||
constraints: self.constraints
|
||||
});
|
||||
|
||||
onStreamSuccess(self);
|
||||
}
|
||||
|
||||
function onError() {
|
||||
onStreamError(self);
|
||||
}
|
||||
|
||||
getUserMedia({
|
||||
constraints: {
|
||||
audio: true,
|
||||
video: this.options.useVideo ? {
|
||||
mandatory: this.options.videoParams,
|
||||
optional: []
|
||||
} : null
|
||||
},
|
||||
video: this.options.useVideo ? true : false,
|
||||
onsuccess: onSuccess,
|
||||
onerror: onError
|
||||
});
|
||||
|
||||
/*
|
||||
navigator.getUserMedia({
|
||||
video: this.options.useVideo,
|
||||
audio: true
|
||||
}, onSuccess, onError);
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
// DERIVED from RTCPeerConnection-v1.5
|
||||
// 2013, @muazkh - github.com/muaz-khan
|
||||
// MIT License - https://www.webrtc-experiment.com/licence/
|
||||
// Documentation - https://github.com/muaz-khan/WebRTC-Experiment/tree/master/RTCPeerConnection
|
||||
window.moz = !!navigator.mozGetUserMedia;
|
||||
|
||||
function RTCPeerConnection(options) {
|
||||
|
||||
var w = window,
|
||||
PeerConnection = w.mozRTCPeerConnection || w.webkitRTCPeerConnection,
|
||||
SessionDescription = w.mozRTCSessionDescription || w.RTCSessionDescription,
|
||||
IceCandidate = w.mozRTCIceCandidate || w.RTCIceCandidate;
|
||||
|
||||
var STUN = {
|
||||
url: !moz ? 'stun:stun.l.google.com:19302' : 'stun:23.21.150.121'
|
||||
};
|
||||
|
||||
var TURN = {
|
||||
url: 'turn:homeo@turn.bistri.com:80',
|
||||
credential: 'homeo'
|
||||
};
|
||||
|
||||
var iceServers = {
|
||||
iceServers: options.iceServers || [STUN]
|
||||
};
|
||||
|
||||
if (!moz && !options.iceServers) {
|
||||
if (parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2]) >= 28) TURN = {
|
||||
url: 'turn:turn.bistri.com:80',
|
||||
credential: 'homeo',
|
||||
username: 'homeo'
|
||||
};
|
||||
|
||||
iceServers.iceServers = [STUN];
|
||||
}
|
||||
|
||||
var optional = {
|
||||
optional: []
|
||||
};
|
||||
|
||||
if (!moz) {
|
||||
optional.optional = [{
|
||||
DtlsSrtpKeyAgreement: true
|
||||
},
|
||||
{
|
||||
RtpDataChannels: options.onChannelMessage ? true : false
|
||||
}];
|
||||
}
|
||||
|
||||
var peer = new PeerConnection(iceServers, optional);
|
||||
|
||||
openOffererChannel();
|
||||
var x = 0;
|
||||
|
||||
peer.onicecandidate = function(event) {
|
||||
console.log("WTF ICE", event);
|
||||
|
||||
if (event.candidate) {
|
||||
options.onICE(event.candidate);
|
||||
} else {
|
||||
if (options.onICEComplete) {
|
||||
options.onICEComplete();
|
||||
}
|
||||
|
||||
if (options.type == "offer") {
|
||||
if (!moz && !x && options.onICESDP) {
|
||||
options.onICESDP(peer.localDescription);
|
||||
//x = 1;
|
||||
/*
|
||||
x = 1;
|
||||
peer.createOffer(function(sessionDescription) {
|
||||
sessionDescription.sdp = serializeSdp(sessionDescription.sdp);
|
||||
peer.setLocalDescription(sessionDescription);
|
||||
if (options.onICESDP) {
|
||||
options.onICESDP(sessionDescription);
|
||||
}
|
||||
}, onSdpError, constraints);
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
if (!x && options.onICESDP) {
|
||||
options.onICESDP(peer.localDescription);
|
||||
//x = 1;
|
||||
/*
|
||||
x = 1;
|
||||
peer.createAnswer(function(sessionDescription) {
|
||||
sessionDescription.sdp = serializeSdp(sessionDescription.sdp);
|
||||
peer.setLocalDescription(sessionDescription);
|
||||
if (options.onICESDP) {
|
||||
options.onICESDP(sessionDescription);
|
||||
}
|
||||
}, onSdpError, constraints);
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// attachStream = MediaStream;
|
||||
if (options.attachStream) peer.addStream(options.attachStream);
|
||||
|
||||
// attachStreams[0] = audio-stream;
|
||||
// attachStreams[1] = video-stream;
|
||||
// attachStreams[2] = screen-capturing-stream;
|
||||
if (options.attachStreams && options.attachStream.length) {
|
||||
var streams = options.attachStreams;
|
||||
for (var i = 0; i < streams.length; i++) {
|
||||
peer.addStream(streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
peer.onaddstream = function(event) {
|
||||
var remoteMediaStream = event.stream;
|
||||
|
||||
// onRemoteStreamEnded(MediaStream)
|
||||
remoteMediaStream.onended = function() {
|
||||
if (options.onRemoteStreamEnded) options.onRemoteStreamEnded(remoteMediaStream);
|
||||
};
|
||||
|
||||
// onRemoteStream(MediaStream)
|
||||
if (options.onRemoteStream) options.onRemoteStream(remoteMediaStream);
|
||||
|
||||
//console.debug('on:add:stream', remoteMediaStream);
|
||||
};
|
||||
|
||||
var constraints = options.constraints || {
|
||||
optional: [],
|
||||
mandatory: {
|
||||
OfferToReceiveAudio: true,
|
||||
OfferToReceiveVideo: true
|
||||
}
|
||||
};
|
||||
|
||||
// onOfferSDP(RTCSessionDescription)
|
||||
function createOffer() {
|
||||
if (!options.onOfferSDP) return;
|
||||
|
||||
peer.createOffer(function(sessionDescription) {
|
||||
sessionDescription.sdp = serializeSdp(sessionDescription.sdp);
|
||||
peer.setLocalDescription(sessionDescription);
|
||||
options.onOfferSDP(sessionDescription);
|
||||
if (moz && options.onICESDP) {
|
||||
options.onICESDP(sessionDescription);
|
||||
}
|
||||
},
|
||||
onSdpError, constraints);
|
||||
}
|
||||
|
||||
// onAnswerSDP(RTCSessionDescription)
|
||||
function createAnswer() {
|
||||
if (options.type != "answer") return;
|
||||
|
||||
//options.offerSDP.sdp = addStereo(options.offerSDP.sdp);
|
||||
peer.setRemoteDescription(new SessionDescription(options.offerSDP), onSdpSuccess, onSdpError);
|
||||
peer.createAnswer(function(sessionDescription) {
|
||||
sessionDescription.sdp = serializeSdp(sessionDescription.sdp);
|
||||
peer.setLocalDescription(sessionDescription);
|
||||
if (options.onAnswerSDP) {
|
||||
options.onAnswerSDP(sessionDescription);
|
||||
}
|
||||
},
|
||||
onSdpError, constraints);
|
||||
}
|
||||
|
||||
// if Mozilla Firefox & DataChannel; offer/answer will be created later
|
||||
if ((options.onChannelMessage && !moz) || !options.onChannelMessage) {
|
||||
createOffer();
|
||||
createAnswer();
|
||||
}
|
||||
|
||||
// DataChannel Bandwidth
|
||||
function setBandwidth(sdp) {
|
||||
// remove existing bandwidth lines
|
||||
sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, '');
|
||||
sdp = sdp.replace(/a=mid:data\r\n/g, 'a=mid:data\r\nb=AS:1638400\r\n');
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
// old: FF<>Chrome interoperability management
|
||||
function getInteropSDP(sdp) {
|
||||
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
|
||||
extractedChars = '';
|
||||
|
||||
function getChars() {
|
||||
extractedChars += chars[parseInt(Math.random() * 40)] || '';
|
||||
if (extractedChars.length < 40) getChars();
|
||||
|
||||
return extractedChars;
|
||||
}
|
||||
|
||||
// usually audio-only streaming failure occurs out of audio-specific crypto line
|
||||
// a=crypto:1 AES_CM_128_HMAC_SHA1_32 --------- kAttributeCryptoVoice
|
||||
if (options.onAnswerSDP) sdp = sdp.replace(/(a=crypto:0 AES_CM_128_HMAC_SHA1_32)(.*?)(\r\n)/g, '');
|
||||
|
||||
// video-specific crypto line i.e. SHA1_80
|
||||
// a=crypto:1 AES_CM_128_HMAC_SHA1_80 --------- kAttributeCryptoVideo
|
||||
var inline = getChars() + '\r\n' + (extractedChars = '');
|
||||
sdp = sdp.indexOf('a=crypto') == -1 ? sdp.replace(/c=IN/g, 'a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:' + inline + 'c=IN') : sdp;
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function serializeSdp(sdp) {
|
||||
//if (!moz) sdp = setBandwidth(sdp);
|
||||
//sdp = getInteropSDP(sdp);
|
||||
//console.debug(sdp);
|
||||
return sdp;
|
||||
}
|
||||
|
||||
// DataChannel management
|
||||
var channel;
|
||||
|
||||
function openOffererChannel() {
|
||||
if (!options.onChannelMessage || (moz && !options.onOfferSDP)) return;
|
||||
|
||||
_openOffererChannel();
|
||||
|
||||
if (!moz) return;
|
||||
navigator.mozGetUserMedia({
|
||||
audio: true,
|
||||
fake: true
|
||||
},
|
||||
function(stream) {
|
||||
peer.addStream(stream);
|
||||
createOffer();
|
||||
},
|
||||
useless);
|
||||
}
|
||||
|
||||
function _openOffererChannel() {
|
||||
channel = peer.createDataChannel(options.channel || 'RTCDataChannel', moz ? {} : {
|
||||
reliable: false
|
||||
});
|
||||
|
||||
if (moz) channel.binaryType = 'blob';
|
||||
|
||||
setChannelEvents();
|
||||
}
|
||||
|
||||
function setChannelEvents() {
|
||||
channel.onmessage = function(event) {
|
||||
if (options.onChannelMessage) options.onChannelMessage(event);
|
||||
};
|
||||
|
||||
channel.onopen = function() {
|
||||
if (options.onChannelOpened) options.onChannelOpened(channel);
|
||||
};
|
||||
channel.onclose = function(event) {
|
||||
if (options.onChannelClosed) options.onChannelClosed(event);
|
||||
|
||||
console.warn('WebRTC DataChannel closed', event);
|
||||
};
|
||||
channel.onerror = function(event) {
|
||||
if (options.onChannelError) options.onChannelError(event);
|
||||
|
||||
console.error('WebRTC DataChannel error', event);
|
||||
};
|
||||
}
|
||||
|
||||
if (options.onAnswerSDP && moz && options.onChannelMessage) openAnswererChannel();
|
||||
|
||||
function openAnswererChannel() {
|
||||
peer.ondatachannel = function(event) {
|
||||
channel = event.channel;
|
||||
channel.binaryType = 'blob';
|
||||
setChannelEvents();
|
||||
};
|
||||
|
||||
if (!moz) return;
|
||||
navigator.mozGetUserMedia({
|
||||
audio: true,
|
||||
fake: true
|
||||
},
|
||||
function(stream) {
|
||||
peer.addStream(stream);
|
||||
createAnswer();
|
||||
},
|
||||
useless);
|
||||
}
|
||||
|
||||
// fake:true is also available on chrome under a flag!
|
||||
function useless() {
|
||||
log('Error in fake:true');
|
||||
}
|
||||
|
||||
function onSdpSuccess() {}
|
||||
|
||||
function onSdpError(e) {
|
||||
if (options.onChannelError) {
|
||||
options.onChannelError(e);
|
||||
}
|
||||
console.error('sdp error:', e);
|
||||
}
|
||||
|
||||
return {
|
||||
addAnswerSDP: function(sdp, cbSuccess, cbError) {
|
||||
|
||||
peer.setRemoteDescription(new SessionDescription(sdp), cbSuccess ? cbSuccess : onSdpSuccess, cbError ? cbError : onSdpError);
|
||||
},
|
||||
addICE: function(candidate) {
|
||||
peer.addIceCandidate(new IceCandidate({
|
||||
sdpMLineIndex: candidate.sdpMLineIndex,
|
||||
candidate: candidate.candidate
|
||||
}));
|
||||
},
|
||||
|
||||
peer: peer,
|
||||
channel: channel,
|
||||
sendData: function(message) {
|
||||
if (channel) {
|
||||
channel.send(message);
|
||||
}
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
peer.close();
|
||||
if (options.attachStream) {
|
||||
options.attachStream.stop();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
// getUserMedia
|
||||
var video_constraints = {
|
||||
mandatory: {},
|
||||
optional: []
|
||||
};
|
||||
|
||||
function getUserMedia(options) {
|
||||
var n = navigator,
|
||||
media;
|
||||
n.getMedia = n.webkitGetUserMedia || n.mozGetUserMedia;
|
||||
n.getMedia(options.constraints || {
|
||||
audio: true,
|
||||
video: video_constraints
|
||||
},
|
||||
streaming, options.onerror ||
|
||||
function(e) {
|
||||
console.error(e);
|
||||
});
|
||||
|
||||
function streaming(stream) {
|
||||
var video = options.video;
|
||||
if (video) {
|
||||
video[moz ? 'mozSrcObject' : 'src'] = moz ? stream : window.webkitURL.createObjectURL(stream);
|
||||
//video.play();
|
||||
}
|
||||
if (options.onsuccess) {
|
||||
options.onsuccess(stream);
|
||||
}
|
||||
media = stream;
|
||||
}
|
||||
|
||||
return media;
|
||||
}
|
||||
|
||||
})(jQuery);
|
|
@ -0,0 +1,653 @@
|
|||
/*
|
||||
* 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 jquery.jsonrpclient.js modified for Verto HTML5/Javascript Telephony Signaling and Control Protocol Stack for FreeSWITCH
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Textalk AB http://textalk.se/
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* jquery.jsonrpclient.js - JSON RPC client code
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* This plugin requires jquery.json.js to be available, or at least the methods $.toJSON and
|
||||
* $.parseJSON.
|
||||
*
|
||||
* The plan is to make use of websockets if they are available, but work just as well with only
|
||||
* http if not.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* var foo = new $.JsonRpcClient({ ajaxUrl: '/backend/jsonrpc' });
|
||||
* foo.call(
|
||||
* 'bar', [ 'A parameter', 'B parameter' ],
|
||||
* function(result) { alert('Foo bar answered: ' + result.my_answer); },
|
||||
* function(error) { console.log('There was an error', error); }
|
||||
* );
|
||||
*
|
||||
* More examples are available in README.md
|
||||
*/
|
||||
(function($) {
|
||||
/**
|
||||
* @fn new
|
||||
* @memberof $.JsonRpcClient
|
||||
*
|
||||
* @param options An object stating the backends:
|
||||
* ajaxUrl A url (relative or absolute) to a http(s) backend.
|
||||
* socketUrl A url (relative of absolute) to a ws(s) backend.
|
||||
* onmessage A socket message handler for other messages (non-responses).
|
||||
* getSocket A function returning a WebSocket or null.
|
||||
* It must take an onmessage_cb and bind it to the onmessage event
|
||||
* (or chain it before/after some other onmessage handler).
|
||||
* Or, it could return null if no socket is available.
|
||||
* The returned instance must have readyState <= 1, and if less than 1,
|
||||
* react to onopen binding.
|
||||
*/
|
||||
$.JsonRpcClient = function(options) {
|
||||
var self = this;
|
||||
this.options = $.extend({
|
||||
ajaxUrl : null,
|
||||
socketUrl : null, ///< The ws-url for default getSocket.
|
||||
onmessage : null, ///< Other onmessage-handler.
|
||||
login : null, /// auth login
|
||||
passwd : null, /// auth passwd
|
||||
sessid : null,
|
||||
getSocket : function(onmessage_cb) { return self._getSocket(onmessage_cb); }
|
||||
}, options);
|
||||
|
||||
// Declare an instance version of the onmessage callback to wrap 'this'.
|
||||
this.wsOnMessage = function(event) { self._wsOnMessage(event); };
|
||||
};
|
||||
|
||||
/// Holding the WebSocket on default getsocket.
|
||||
$.JsonRpcClient.prototype._ws_socket = null;
|
||||
|
||||
/// Object <id>: { success_cb: cb, error_cb: cb }
|
||||
$.JsonRpcClient.prototype._ws_callbacks = {};
|
||||
|
||||
/// The next JSON-RPC request id.
|
||||
$.JsonRpcClient.prototype._current_id = 1;
|
||||
|
||||
/**
|
||||
* @fn call
|
||||
* @memberof $.JsonRpcClient
|
||||
*
|
||||
* @param method The method to run on JSON-RPC server.
|
||||
* @param params The params; an array or object.
|
||||
* @param success_cb A callback for successful request.
|
||||
* @param error_cb A callback for error.
|
||||
*/
|
||||
$.JsonRpcClient.prototype.call = function(method, params, success_cb, error_cb) {
|
||||
// Construct the JSON-RPC 2.0 request.
|
||||
|
||||
if (!params) {
|
||||
params = {};
|
||||
}
|
||||
|
||||
if (this.options.sessid) {
|
||||
params.sessid = this.options.sessid;
|
||||
}
|
||||
|
||||
var request = {
|
||||
jsonrpc : '2.0',
|
||||
method : method,
|
||||
params : params,
|
||||
id : this._current_id++ // Increase the id counter to match request/response
|
||||
};
|
||||
|
||||
if (!success_cb) {
|
||||
success_cb = function(e){console.log("Success: ", e);};
|
||||
}
|
||||
|
||||
if (!error_cb) {
|
||||
error_cb = function(e){console.log("Error: ", e);};
|
||||
}
|
||||
|
||||
// Try making a WebSocket call.
|
||||
var socket = this.options.getSocket(this.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
this._wsCall(socket, request, success_cb, error_cb);
|
||||
return;
|
||||
}
|
||||
|
||||
// No WebSocket, and no HTTP backend? This won't work.
|
||||
if (this.options.ajaxUrl === null) {
|
||||
throw "$.JsonRpcClient.call used with no websocket and no http endpoint.";
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : this.options.ajaxUrl,
|
||||
data : $.toJSON(request),
|
||||
dataType : 'json',
|
||||
cache : false,
|
||||
|
||||
success : function(data) {
|
||||
if ('error' in data) error_cb(data.error, this);
|
||||
success_cb(data.result, this);
|
||||
},
|
||||
|
||||
// JSON-RPC Server could return non-200 on error
|
||||
error : function(jqXHR, textStatus, errorThrown) {
|
||||
try {
|
||||
var response = $.parseJSON(jqXHR.responseText);
|
||||
if ('console' in window) console.log(response);
|
||||
error_cb(response.error, this);
|
||||
}
|
||||
catch (err) {
|
||||
// Perhaps the responseText wasn't really a jsonrpc-error.
|
||||
error_cb({ error: jqXHR.responseText }, this);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify sends a command to the server that won't need a response. In http, there is probably
|
||||
* an empty response - that will be dropped, but in ws there should be no response at all.
|
||||
*
|
||||
* This is very similar to call, but has no id and no handling of callbacks.
|
||||
*
|
||||
* @fn notify
|
||||
* @memberof $.JsonRpcClient
|
||||
*
|
||||
* @param method The method to run on JSON-RPC server.
|
||||
* @param params The params; an array or object.
|
||||
*/
|
||||
$.JsonRpcClient.prototype.notify = function(method, params) {
|
||||
// Construct the JSON-RPC 2.0 request.
|
||||
|
||||
if (this.options.sessid) {
|
||||
params.sessid = this.options.sessid;
|
||||
}
|
||||
|
||||
var request = {
|
||||
jsonrpc: '2.0',
|
||||
method: method,
|
||||
params: params
|
||||
};
|
||||
|
||||
// Try making a WebSocket call.
|
||||
var socket = this.options.getSocket(this.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
this._wsCall(socket, request);
|
||||
return;
|
||||
}
|
||||
|
||||
// No WebSocket, and no HTTP backend? This won't work.
|
||||
if (this.options.ajaxUrl === null) {
|
||||
throw "$.JsonRpcClient.notify used with no websocket and no http endpoint.";
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : this.options.ajaxUrl,
|
||||
data : $.toJSON(request),
|
||||
dataType : 'json',
|
||||
cache : false
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a batch-call by using a callback.
|
||||
*
|
||||
* The callback will get an object "batch" as only argument. On batch, you can call the methods
|
||||
* "call" and "notify" just as if it was a normal $.JsonRpcClient object, and all calls will be
|
||||
* sent as a batch call then the callback is done.
|
||||
*
|
||||
* @fn batch
|
||||
* @memberof $.JsonRpcClient
|
||||
*
|
||||
* @param callback The main function which will get a batch handler to run call and notify on.
|
||||
* @param all_done_cb A callback function to call after all results have been handled.
|
||||
* @param error_cb A callback function to call if there is an error from the server.
|
||||
* Note, that batch calls should always get an overall success, and the
|
||||
* only error
|
||||
*/
|
||||
$.JsonRpcClient.prototype.batch = function(callback, all_done_cb, error_cb) {
|
||||
var batch = new $.JsonRpcClient._batchObject(this, all_done_cb, error_cb);
|
||||
callback(batch);
|
||||
batch._execute();
|
||||
};
|
||||
|
||||
/**
|
||||
* The default getSocket handler.
|
||||
*
|
||||
* @param onmessage_cb The callback to be bound to onmessage events on the socket.
|
||||
*
|
||||
* @fn _getSocket
|
||||
* @memberof $.JsonRpcClient
|
||||
*/
|
||||
|
||||
$.JsonRpcClient.prototype.socketReady = function() {
|
||||
if (this._ws_socket === null || this._ws_socket.readyState > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$.JsonRpcClient.prototype.closeSocket = function() {
|
||||
if (self.socketReady()) {
|
||||
this._ws_socket.onclose = function (w) {console.log("Closing Socket")}
|
||||
this._ws_socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
$.JsonRpcClient.prototype.loginData = function(params) {
|
||||
self.options.login = params.login;
|
||||
self.options.passwd = params.passwd;
|
||||
}
|
||||
|
||||
$.JsonRpcClient.prototype.connectSocket = function(onmessage_cb) {
|
||||
var self = this;
|
||||
|
||||
if (!self.socketReady()) {
|
||||
self.authing = false;
|
||||
// No socket, or dying socket, let's get a new one.
|
||||
this._ws_socket = new WebSocket(this.options.socketUrl);
|
||||
|
||||
if (this._ws_socket) {
|
||||
// Set up onmessage handler.
|
||||
this._ws_socket.onmessage = onmessage_cb;
|
||||
this._ws_socket.onclose = function (w) {
|
||||
if (!self.ws_sleep) {
|
||||
self.ws_sleep = 2;
|
||||
}
|
||||
|
||||
self.ws_cnt = 0;
|
||||
|
||||
if (self.options.onWSClose) {
|
||||
self.options.onWSClose(self);
|
||||
}
|
||||
|
||||
console.error("Websocket Lost sleep: " + self.ws_sleep + "sec");
|
||||
|
||||
setTimeout(function() {
|
||||
console.log("Attempting Reconnection....");
|
||||
self.connectSocket(onmessage_cb);
|
||||
}, self.ws_sleep * 1000);
|
||||
|
||||
|
||||
if (++self.ws_cnt >= 150) {
|
||||
self.ws_sleep = 30;
|
||||
}
|
||||
}
|
||||
|
||||
// Set up sending of message for when the socket is open.
|
||||
this._ws_socket.onopen = function() {
|
||||
this.ws_sleep = 2;
|
||||
this.ws_cnt = 0;
|
||||
if (self.options.onWSConnect) {
|
||||
self.options.onWSConnect(self);
|
||||
}
|
||||
|
||||
var req;
|
||||
// Send the requests.
|
||||
while (req = $.JsonRpcClient.q.pop()) {
|
||||
self._ws_socket.send(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this._ws_socket ? true : false;
|
||||
}
|
||||
|
||||
$.JsonRpcClient.prototype._getSocket = function(onmessage_cb) {
|
||||
// If there is no ws url set, we don't have a socket.
|
||||
// Likewise, if there is no window.WebSocket.
|
||||
if (this.options.socketUrl === null || !("WebSocket" in window)) return null;
|
||||
|
||||
this.connectSocket(onmessage_cb);
|
||||
|
||||
return this._ws_socket;
|
||||
};
|
||||
|
||||
/**
|
||||
* Queue to save messages delivered when websocket is not ready
|
||||
*/
|
||||
$.JsonRpcClient.q = [];
|
||||
|
||||
/**
|
||||
* Internal handler to dispatch a JRON-RPC request through a websocket.
|
||||
*
|
||||
* @fn _wsCall
|
||||
* @memberof $.JsonRpcClient
|
||||
*/
|
||||
$.JsonRpcClient.prototype._wsCall = function(socket, request, success_cb, error_cb) {
|
||||
var request_json = $.toJSON(request);
|
||||
|
||||
if (socket.readyState < 1) {
|
||||
// The websocket is not open yet; we have to set sending of the message in onopen.
|
||||
self = this; // In closure below, this is set to the WebSocket. Use self instead.
|
||||
|
||||
$.JsonRpcClient.q.push(request_json);
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
// We have a socket and it should be ready to send on.
|
||||
socket.send(request_json);
|
||||
}
|
||||
|
||||
// Setup callbacks. If there is an id, this is a call and not a notify.
|
||||
if ('id' in request && typeof success_cb !== 'undefined') {
|
||||
this._ws_callbacks[request.id] = { request: request_json, request_obj: request, success_cb: success_cb, error_cb: error_cb };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal handler for the websocket messages. It determines if the message is a JSON-RPC
|
||||
* response, and if so, tries to couple it with a given callback. Otherwise, it falls back to
|
||||
* given external onmessage-handler, if any.
|
||||
*
|
||||
* @param event The websocket onmessage-event.
|
||||
*/
|
||||
$.JsonRpcClient.prototype._wsOnMessage = function(event) {
|
||||
// Check if this could be a JSON RPC message.
|
||||
var response;
|
||||
try {
|
||||
response = $.parseJSON(event.data);
|
||||
|
||||
/// @todo Make using the jsonrcp 2.0 check optional, to use this on JSON-RPC 1 backends.
|
||||
|
||||
if (typeof response === 'object'
|
||||
&& 'jsonrpc' in response
|
||||
&& response.jsonrpc === '2.0') {
|
||||
|
||||
/// @todo Handle bad response (without id).
|
||||
|
||||
// If this is an object with result, it is a response.
|
||||
if ('result' in response && this._ws_callbacks[response.id]) {
|
||||
// Get the success callback.
|
||||
var success_cb = this._ws_callbacks[response.id].success_cb;
|
||||
|
||||
/*
|
||||
// set the sessid if present
|
||||
if ('sessid' in response.result && !this.options.sessid || (this.options.sessid != response.result.sessid)) {
|
||||
this.options.sessid = response.result.sessid;
|
||||
if (this.options.sessid) {
|
||||
console.log("setting session UUID to: " + this.options.sessid);
|
||||
}
|
||||
}
|
||||
*/
|
||||
// Delete the callback from the storage.
|
||||
delete this._ws_callbacks[response.id];
|
||||
|
||||
// Run callback with result as parameter.
|
||||
success_cb(response.result, this);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is an object with error, it is an error response.
|
||||
else if ('error' in response && this._ws_callbacks[response.id]) {
|
||||
|
||||
// Get the error callback.
|
||||
var error_cb = this._ws_callbacks[response.id].error_cb;
|
||||
var orig_req = this._ws_callbacks[response.id].request;
|
||||
|
||||
// if this is an auth request, send the credentials and resend the failed request
|
||||
if (!self.authing && response.error.code == -32000 && self.options.login && self.options.passwd) {
|
||||
self.authing = true;
|
||||
|
||||
this.call("login", { login: self.options.login, passwd: self.options.passwd},
|
||||
this._ws_callbacks[response.id].request_obj.method == "login"
|
||||
?
|
||||
function(e) {
|
||||
self.authing = false;
|
||||
console.log("logged in");
|
||||
delete self._ws_callbacks[response.id];
|
||||
|
||||
if (self.options.onWSLogin) {
|
||||
self.options.onWSLogin(true, self);
|
||||
}
|
||||
}
|
||||
|
||||
:
|
||||
|
||||
function(e) {
|
||||
self.authing = false;
|
||||
console.log("logged in, resending request id: " + response.id);
|
||||
var socket = self.options.getSocket(self.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
socket.send(orig_req);
|
||||
}
|
||||
if (self.options.onWSLogin) {
|
||||
self.options.onWSLogin(true, self);
|
||||
}
|
||||
},
|
||||
|
||||
function(e) {
|
||||
console.log("error logging in, request id:", response.id);
|
||||
delete self._ws_callbacks[response.id];
|
||||
error_cb(response.error, this);
|
||||
if (self.options.onWSLogin) {
|
||||
self.options.onWSLogin(false, self);
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the callback from the storage.
|
||||
delete this._ws_callbacks[response.id];
|
||||
|
||||
// Run callback with the error object as parameter.
|
||||
error_cb(response.error, this);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
// Probably an error while parsing a non json-string as json. All real JSON-RPC cases are
|
||||
// handled above, and the fallback method is called below.
|
||||
console.log("ERROR: "+ err);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is not a JSON-RPC response. Call the fallback message handler, if given.
|
||||
if (typeof this.options.onmessage === 'function') {
|
||||
event.eventData = response;
|
||||
if (!event.eventData) {
|
||||
event.eventData = {};
|
||||
}
|
||||
|
||||
var reply = this.options.onmessage(event);
|
||||
|
||||
if (reply && typeof reply === "object" && event.eventData.id) {
|
||||
var msg = {
|
||||
jsonrpc: "2.0",
|
||||
id: event.eventData.id,
|
||||
result: reply
|
||||
};
|
||||
|
||||
var socket = self.options.getSocket(self.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
socket.send($.toJSON(msg));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************************************
|
||||
* Batch object with methods
|
||||
************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Handling object for batch calls.
|
||||
*/
|
||||
$.JsonRpcClient._batchObject = function(jsonrpcclient, all_done_cb, error_cb) {
|
||||
// Array of objects to hold the call and notify requests. Each objects will have the request
|
||||
// object, and unless it is a notify, success_cb and error_cb.
|
||||
this._requests = [];
|
||||
|
||||
this.jsonrpcclient = jsonrpcclient;
|
||||
this.all_done_cb = all_done_cb;
|
||||
this.error_cb = typeof error_cb === 'function' ? error_cb : function() {};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @sa $.JsonRpcClient.prototype.call
|
||||
*/
|
||||
$.JsonRpcClient._batchObject.prototype.call = function(method, params, success_cb, error_cb) {
|
||||
|
||||
if (!params) {
|
||||
params = {};
|
||||
}
|
||||
|
||||
if (this.options.sessid) {
|
||||
params.sessid = this.options.sessid;
|
||||
}
|
||||
|
||||
if (!success_cb) {
|
||||
success_cb = function(e){console.log("Success: ", e);};
|
||||
}
|
||||
|
||||
if (!error_cb) {
|
||||
error_cb = function(e){console.log("Error: ", e);};
|
||||
}
|
||||
|
||||
this._requests.push({
|
||||
request : {
|
||||
jsonrpc : '2.0',
|
||||
method : method,
|
||||
params : params,
|
||||
id : this.jsonrpcclient._current_id++ // Use the client's id series.
|
||||
},
|
||||
success_cb : success_cb,
|
||||
error_cb : error_cb
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @sa $.JsonRpcClient.prototype.notify
|
||||
*/
|
||||
$.JsonRpcClient._batchObject.prototype.notify = function(method, params) {
|
||||
if (this.options.sessid) {
|
||||
params.sessid = this.options.sessid;
|
||||
}
|
||||
|
||||
this._requests.push({
|
||||
request : {
|
||||
jsonrpc : '2.0',
|
||||
method : method,
|
||||
params : params
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes the batched up calls.
|
||||
*/
|
||||
$.JsonRpcClient._batchObject.prototype._execute = function() {
|
||||
var self = this;
|
||||
|
||||
if (this._requests.length === 0) return; // All done :P
|
||||
|
||||
// Collect all request data and sort handlers by request id.
|
||||
var batch_request = [];
|
||||
var handlers = {};
|
||||
|
||||
// If we have a WebSocket, just send the requests individually like normal calls.
|
||||
var socket = self.jsonrpcclient.options.getSocket(self.jsonrpcclient.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
for (var i = 0; i < this._requests.length; i++) {
|
||||
var call = this._requests[i];
|
||||
var success_cb = ('success_cb' in call) ? call.success_cb : undefined;
|
||||
var error_cb = ('error_cb' in call) ? call.error_cb : undefined;
|
||||
self.jsonrpcclient._wsCall(socket, call.request, success_cb, error_cb);
|
||||
}
|
||||
if (typeof all_done_cb === 'function') all_done_cb(result);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this._requests.length; i++) {
|
||||
var call = this._requests[i];
|
||||
batch_request.push(call.request);
|
||||
|
||||
// If the request has an id, it should handle returns (otherwise it's a notify).
|
||||
if ('id' in call.request) {
|
||||
handlers[call.request.id] = {
|
||||
success_cb : call.success_cb,
|
||||
error_cb : call.error_cb
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var success_cb = function(data) { self._batchCb(data, handlers, self.all_done_cb); };
|
||||
|
||||
// No WebSocket, and no HTTP backend? This won't work.
|
||||
if (self.jsonrpcclient.options.ajaxUrl === null) {
|
||||
throw "$.JsonRpcClient.batch used with no websocket and no http endpoint.";
|
||||
}
|
||||
|
||||
// Send request
|
||||
$.ajax({
|
||||
url : self.jsonrpcclient.options.ajaxUrl,
|
||||
data : $.toJSON(batch_request),
|
||||
dataType : 'json',
|
||||
cache : false,
|
||||
type : 'POST',
|
||||
|
||||
// Batch-requests should always return 200
|
||||
error : function(jqXHR, textStatus, errorThrown) {
|
||||
self.error_cb(jqXHR, textStatus, errorThrown);
|
||||
},
|
||||
success : success_cb
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal helper to match the result array from a batch call to their respective callbacks.
|
||||
*
|
||||
* @fn _batchCb
|
||||
* @memberof $.JsonRpcClient
|
||||
*/
|
||||
$.JsonRpcClient._batchObject.prototype._batchCb = function(result, handlers, all_done_cb) {
|
||||
for (var i = 0; i < result.length; i++) {
|
||||
var response = result[i];
|
||||
|
||||
// Handle error
|
||||
if ('error' in response) {
|
||||
if (response.id === null || !(response.id in handlers)) {
|
||||
// An error on a notify? Just log it to the console.
|
||||
if ('console' in window) console.log(response);
|
||||
}
|
||||
else handlers[response.id].error_cb(response.error, this);
|
||||
}
|
||||
else {
|
||||
// Here we should always have a correct id and no error.
|
||||
if (!(response.id in handlers) && 'console' in window) console.log(response);
|
||||
else handlers[response.id].success_cb(response.result, this);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof all_done_cb === 'function') all_done_cb(result);
|
||||
};
|
||||
|
||||
})(jQuery);
|