window.TY = window.TY || {};
(function (TY) {
TY.SERVER_URL= null;
TY.WS_URL=null;
TY.URL = {
initServerUrl: function (url) {
TY.SERVER_URL = url;
TY.WS_URL = url;
var urls = this.initReqUrl();
Object.assign( TY.URL,urls);
},
initReqUrl(){
return {
rtcStats: TY.SERVER_URL + "/ops/api/metrics", //设备注册
activate: TY.SERVER_URL + "/ops/api/license/activate", //设备注册
service: TY.SERVER_URL + "/ops/api/service", //设备注册
license: TY.SERVER_URL + "/ops/api/license", //设备注册
DeviceManager:{
addDevice: TY.SERVER_URL + "/user/device/add", //设备注册
deleteDevice: TY.SERVER_URL + "/device/del", //设备删除
modifyDevice: TY.SERVER_URL + "/device/upd", //设备信息修改包括回放密码
getDevicesByParams: TY.SERVER_URL + "/user/device", //设备list
deviceInfo:TY.SERVER_URL + "/device/info",
getScanUrl: TY.SERVER_URL + "/path", //获取服务器地址
getBindedScanUrl: TY.SERVER_URL + "/user/registered/end",//设备绑定二维码
getUnBindedDevice: TY.SERVER_URL + "/user/device/undistributed",//未绑定的设备列表
getUnAllocatedDevice: TY.SERVER_URL + "/user/device/undistributed/id",//未分配的设备列表
getAllocatedDevice: TY.SERVER_URL + "/user/device/allocated/id", //已分配的设备列表
bindDevice: TY.SERVER_URL + "/user/device/bound", //绑定设备
unBindDevice: TY.SERVER_URL + "/user/device/remove/bound", //解绑设备
allocateDevice: TY.SERVER_URL + "/user/device/shared/assignments/with/one/click", //设备分配 这个会删除你们共有而对方没有的
AllocateDevice: TY.SERVER_URL + "/user/device/shared", //设备分配 这个是添加方式
unAllocateDevice: TY.SERVER_URL + "/user/device/removePower", //移除全部分配设备
disableDevice: TY.SERVER_URL + "/device/disable", //设备禁用
transferDevice: TY.SERVER_URL + "/user/device/transfer", //设备调拔
loadBindUser: TY.SERVER_URL + "/user/department/all/user", //获取设备绑定界面的用户列表
},
deleteGroup:TY.SERVER_URL + "/user/community/del",
getBrocastList:TY.SERVER_URL + "/user/radio/query",
notGroupUsers: TY.SERVER_URL + "/menu/child",//未分配到群组的成员
loadMenu: TY.SERVER_URL + "/menu/child",//菜单
sos: TY.SERVER_URL + "/user/sos",
loadDepartmentCough: TY.SERVER_URL + "/user/department/cough",//单位信息
allDepartment: TY.SERVER_URL + "/user/department/all",//单位信息
settingDevice: TY.SERVER_URL +"/user/device/add",//单位用户信息
deleteUser:TY.SERVER_URL + "/user/del",
getUserInfo: TY.SERVER_URL + "/user/info",
expCsv:TY.SERVER_URL + "/user/exp_csv",
loadDepartentDevice:TY.SERVER_URL + "/user/department/device",
deleteDevices:TY.SERVER_URL + "/device/dels",
modifyPwd: TY.SERVER_URL +"/user/upd",
modifyGroup: TY.SERVER_URL + "/user/community/aver",
modifyGroupUsers: TY.SERVER_URL + "/user/community/aver",
deleteDepartment:TY.SERVER_URL + "/user/department/del",
addDepartment: TY.SERVER_URL +"/user/department/add",
modifyDepartment: TY.SERVER_URL +"/user/department/upd",
transferDepartment: TY.SERVER_URL +"/user/tool/department",
loadAllocatedDevice: TY.SERVER_URL +"/user/device/allocated/id",
loadUnAllocatedDevice:TY.SERVER_URL + "/user/device/undistributed/id",
updateAllocateDevices:TY.SERVER_URL + "/user/device/shared/assignments/with/one/click",
loadUnBindDevice: TY.SERVER_URL +"/user/device/undistributed",
loadGroupUser: TY.SERVER_URL +"/device/list",
loadUnGroupUser: TY.SERVER_URL +"/device/list/not",
loadGroupUsers:TY.SERVER_URL + "/user/community/personnel",
loadDeviceList: TY.SERVER_URL + "/user/device",
loadBindUser: TY.SERVER_URL + "/user/department/all/user",
loadUserList: TY.SERVER_URL +"/user/query",
bindScanUrl: TY.SERVER_URL +"/user/registered/end",
signPlaceAdd: TY.SERVER_URL + "/sign/add2edit",
loadGroupList: TY.SERVER_URL + "/user/community",
//deleteDepartment: TY.SERVER_URL+"/user/department/use"//单位用户信息
downLogo: TY.SERVER_URL + '/',
userInfo: TY.SERVER_URL + "/user/info",
loadAllUser: TY.SERVER_URL +"/user/department/all/user",
addUser: TY.SERVER_URL +"/user/add",//添加用戶
modifyUser: TY.SERVER_URL +"/user/upd",
login:TY.SERVER_URL + "/user/authorize",
startTalk:TY.SERVER_URL + "/intercom/apply",
stopTalk:TY.SERVER_URL + "/intercom/end",
createDispatch:TY.SERVER_URL + "/intercom/create/scheduling",
loadTalkDevice:TY.SERVER_URL + "/intercom/group/device",
loadConferenceDevice:TY.SERVER_URL + "/meeting/group/use",
loadTalkGroup:TY.SERVER_URL + "/intercom/group",
createConference:TY.SERVER_URL + "/meeting/create/token",
activeConference:TY.SERVER_URL + "/meeting/create/token",
removeConfMember:TY.SERVER_URL + "/meeting/remove",
inviteConfMember:TY.SERVER_URL + "/meeting/invite",
stopConference:TY.SERVER_URL + "/meeting/dissolution/msg", //解散会议
removeDispatchUser: TY.SERVER_URL + "/intercom/leave/use",//调度踢出调度对讲成员
addDispatchUser: TY.SERVER_URL + "/intercom/add",//添加调度对讲成员
leaveConference:TY.SERVER_URL + "/meeting/leave/msg", //解散会议
stopDispatch:TY.SERVER_URL + "/intercom/leave/msg",
loadDepartment:TY.SERVER_URL + "/user/department", //单位信息
loadUser:TY.SERVER_URL + "/user/department/use", //单位用户信息
removeGroupUser:TY.SERVER_URL + "/intercom/leave/use", //踢出对讲成员
addGroupUser:TY.SERVER_URL + "/intercom/add", //踢出对讲成员
joinConference:TY.SERVER_URL + "/meeting/join", //加入会议
outConference:TY.SERVER_URL + "/meeting/remove", //踢出会议
inviteConference:TY.SERVER_URL + "/meeting/invite", //邀请加入会议
createMoniter:TY.SERVER_URL + "/monitoring/create/token", //监控
addMoniter:TY.SERVER_URL + "/monitoring/join", //添加监控成员
removeMoniter:TY.SERVER_URL + "/monitoring/remove/msg", //移除监控成员
stopMoniter:TY.SERVER_URL + "/monitoring/leave/msg", //结束监控
loadConferenceGroup:TY.SERVER_URL + "/meeting/group", //会议组
changeTalkGroup:TY.SERVER_URL + "/intercom/switch",
unsubscribeGps:TY.SERVER_URL + "/user/gps/end", //取消订阅gps
subscribeGpsByDevice:TY.SERVER_URL + "/user/gps", //gps订阅
subscribeGpsByGroup:TY.SERVER_URL + "/user/gps/room", //gps订阅组
uploadPicture:TY.SERVER_URL + "/intercom/uploadPicture", //上传截屏图片
loadFile:TY.SERVER_URL + "/device/video/retrieve", //视频检索
loadTrackGps:TY.SERVER_URL + "/user/trajectory", //轨迹
brocastMsg:TY.SERVER_URL + "/user/radio", //根据地址获取用户
loadDevice:TY.SERVER_URL + "/user/device",
downloadFile:TY.SERVER_URL + "/download",
videoDown:TY.SERVER_URL + "/user/download/",
loadUsers:TY.SERVER_URL + "/user/query",
loadDevices:TY.SERVER_URL + "/user/department/device",
deleteFile:TY.SERVER_URL + "/device/remove/file",
uLoginLog:TY.SERVER_URL + "/user/loginLog",
importFile:TY.SERVER_URL + "/device/import/file",
loadRoles:TY.SERVER_URL + "/user/role/query",
setPermission:TY.SERVER_URL + "/user/role/pinless",
uploadImg:TY.SERVER_URL + "/user/upload",
beginUploadFile:TY.SERVER_URL + "/user/media/begin",
test:TY.SERVER_URL + "/intercom/switch",
querySystemConfig: TY.SERVER_URL + "/sys_config/query", //查询系统参数设置
setSystemConfig: TY.SERVER_URL + "/sys_config/set", //设置系统参数
uploadUpPackage: TY.SERVER_URL + "/exit", //上传平台升级包,进行升级,
getFencesByParams: TY.SERVER_URL +"/fence/list", //查询围栏列表
getFenceRecordsByParams: TY.SERVER_URL +"/fence/recordList", //查询围栏列表
setFence: TY.SERVER_URL + "/fence/add2edit", //设置围栏
getFenceInfo: TY.SERVER_URL + "/fence/detail", //围栏详情
deviceBindFence: TY.SERVER_URL + "/fence/bind", //设备绑定到围栏
deleteFence: TY.SERVER_URL + "/fence/delete", //删除围栏
getUnBindFences: TY.SERVER_URL + "/fence/unAllocateList", //未绑定围栏的设备列表
getBindFences: TY.SERVER_URL + "/fence/allocatedList", //已绑定围栏的设备列表
getSignPlacesByParams: TY.SERVER_URL +"/sign/list", //签到点列表
allSignPlaces: TY.SERVER_URL + "/sign/listAll", //全部签到点列表
signPlaceInfo: TY.SERVER_URL + "/sign/detail", //签到点详情
setSignPlace: TY.SERVER_URL + "/sign/add2edit", //设置签到点
deleteSignPlace: TY.SERVER_URL + "/sign/delete", //删除签到点
getWorkShiftsByParams: TY.SERVER_URL + "/banci/list", //签到班次列表
deviceBindWorkShift: TY.SERVER_URL + "/banci/bind", //设备绑定到签到班次
setWorkShift: TY.SERVER_URL + "/banci/add2edit", //设置签到班次
deleteWorkShift: TY.SERVER_URL + "/banci/delete", //删除签到班次
workShiftInfo: TY.SERVER_URL + "/banci/detail", //签到班次详情
unBindWorkShiftDevices: TY.SERVER_URL + "/banci/unAllocateList", //未绑定签到班次的设备列表
bindWorkShiftDevices: TY.SERVER_URL + "/banci/allocatedList", //已绑定签到班次的设备列表
getSignsByParams: TY.SERVER_URL + "/sign/records", //考勤记录
exportSigns: TY.SERVER_URL + "/sign/downRecord", //导出考勤记录
getChildRightsList: TY.SERVER_URL + "/menu", //获取下级权限列表
modifyRights: TY.SERVER_URL + "/menu/save", //添加&修改权限
getRightsInfo: TY.SERVER_URL + "/menu/detail", //获取权限详情
deleteRights: TY.SERVER_URL + "/menu/del", //删除权限
getSubRights: TY.SERVER_URL + "/menu/child", //获取所有下级的权限列表
getRoleList: TY.SERVER_URL + "/role_mgr", //获取角色列表
modifyRole: TY.SERVER_URL + "/role_mgr/save", //添加&修改角色,更改绑定的权限
getRoleInfo: TY.SERVER_URL + "/role_mgr/detail", //获取角色详情
deleteRole: TY.SERVER_URL + "/role_mgr/del", //删除角色,清除权限绑定关系
getDeviceLoginLogsByParams: TY.SERVER_URL + "/device/loginLog", //获取设备登录日志
getUserLoginLogsByParams: TY.SERVER_URL + "/user/loginLog", //获取用户登录日志
getUserOperateLogsByParams: TY.SERVER_URL + "/user/log", //获取操作日志
groupInfo: TY.SERVER_URL + "/user/community/info", //群组信息详情
deleteFace: TY.SERVER_URL + "/face/rm",//删除人脸库
getFacesByParams: TY.SERVER_URL + "/face/list",//人脸库列表
loadFaceImg: TY.SERVER_URL + "/face/down",//人脸图片下载
getResultsByParams: TY.SERVER_URL + "/face/hs_list",//识别结果列表
deleteResult: TY.SERVER_URL + "/face/hs_rm",//删除识别结果
getUserByUnit: TY.SERVER_URL + "/user/query",//获取部门下的人员列表
getUnitsByParams: TY.SERVER_URL + "/user/department",//部门列表
addUnit: TY.SERVER_URL + "/user/department/add",//添加部门
deleteUnit: TY.SERVER_URL + "/user/department/del",//删除部门
modifyUnit: TY.SERVER_URL + "/user/department/upd",//修改部门
modifyApp: TY.SERVER_URL + "/version/type_add2edit",//添加/修改App
getAppInfo: TY.SERVER_URL + "/version/type_detail",//App详情
deleteApp: TY.SERVER_URL + "/version/type_delete",//删除App
getAppList: TY.SERVER_URL + "/version/type_list",//App列表
modifyAppVersion: TY.SERVER_URL + "/version/add2edit",//添加/修改App版本
getAppVersionInfo: TY.SERVER_URL + "/version/detail",//App版本详情
deleteAppVersion: TY.SERVER_URL + "/version/delete",//删除App版本
getAppVersionList: TY.SERVER_URL + "/version/list",//App版本列表,
evidence: {
sortList: TY.SERVER_URL + '/evidence/sort',//文件类型
caseTopicList: TY.SERVER_URL + '/evidence/caseTopic',//案例专题列表
fileList: TY.SERVER_URL + '/evidence/file',//文件列表
deptList: TY.SERVER_URL + '/evidence/file/dept',//部门列表
devList: TY.SERVER_URL + '/evidence/file/dev',//文件列表
fileDetail: TY.SERVER_URL + '/evidence/file/detail',//文件信息
delFile: TY.SERVER_URL + '/evidence/file/del',//删除文件
saveFile: TY.SERVER_URL + '/evidence/file/save',//编辑文件信息
downFile: TY.SERVER_URL + '/evidence/file/down',//下载文件
thumb: TY.SERVER_URL + '/evidence/file/thumb', //下载图片
preFile: TY.SERVER_URL + '/evidence/file/preview',//预览文件
exportCsv: TY.SERVER_URL + '/evidence/file/exportCsv',//导出文件
openN: TY.SERVER_URL + '/evidence/file/openN',//记录播放数
uploadFile: TY.SERVER_URL + '/evidence/file/uploadFile',//上传文件
majorCount: TY.SERVER_URL + '/evidence/count/major',//文档重要级别统计
importCount: TY.SERVER_URL + '/evidence/count/import',//文档导入时段统计
personnelCount: TY.SERVER_URL + '/evidence/count/personnel',//人员资料统计
assessCount: TY.SERVER_URL + '/evidence/count/assess',//人员考核统计
}
}
},
};
TY.USER = {
CMD: { //指令码
INIT: 1,
INIT_RESP: 2,
msg: {
UPDATE_TOKEN: 0,
CONNECT_FAIL: 1,
ONLINE: 2,
OFFLINE: 3,
BROCAST: 4,
SUBSCRIBE_GPS: 5,
FILE_PROGRESS: 6,
FILE_UPLOAD: 9,
FACE_RECORD: 12,
SOS: 13,
RECORD_STATUS: 14,
SOMEONE_LOGIN: 15,
FILE_IMPORT: 16,
UPDATE_CERT:999
},
intercom: {
CHANGE_GROUP: 0,
ADD_MENBER: 1,
REMOVE_MEMBER: 2,
LEAVE_GROUP: 3,
TALKING: 4,
STOP_TALKING: 5,
ONLINE: 6,
OFFLINE: 7,
ADD_GROUP: 8,
DELE_GROUP: 9
},
meeting: {
JOIN_CONFERENCE: 0,
ADD_MEMBER: 1,
REMOVE_MEMBER: 2,
STOP_CONFERENCE: 3,
ONLINE: 4,
OFFLINE: 5
},
monitoring: {
ONLINE: 2,
OFFLINE: 3,
REQ_JOIN_CONFERENCE: 4,
LEAVE_CONFERENCE: 5,
JOIN_CONFERENCE: 6
}
},
setUser(token, uid) {//登录设置信息
this.token = token;
this.uid = uid;
this._login=true;
},
toCallback:{
// "record":
},
checkTokenInvalidEvent: null,
addCheckTokenInvalidEvent: function(resolve){
if(resolve) TY.USER.checkTokenInvalidEvent = resolve;
},
initSocket: function (resolve, reject) {//初始化socket
reject=reject?reject:resolve;
var socket = io.connect(TY.WS_URL, {
reconnection: true
});
socket.on('connect', function (data) {
socket.emit("init", {
"token": TY.USER.token
}, function (resp) {
if (resp.cmd == TY.RcCode.SOCKET_INIT_SUCCESS.code) {
console.log("==============消息通道已启动");
self._socket = socket;
resolve&&resolve();
var callback = TY.EventManager.callbacks[TY.EventManager.EVENTS.SOCKET_INIT];
callback&&callback(resp);
} else {
console.log("======消息通道启动失败=======", resp);
reject&&reject();
}
});
socket.on('intercom', function (data) {
if (!TY.talkManager) {
return;
}
var callback=TY.talkManager.callback;
console.log(new Date().toUTCString(), "======intercom*************=========", JSON.stringify(data));
switch (data.cmd) {
case TY.USER.CMD.intercom.TALKING:
if(data.uid==TY.USER.uid){
console.log("=====自己对讲");
return;
}
TY.talkManager.listenTalk(data.uid);
callback = callback?callback:TY.EventManager.callbacks[TY.EventManager.EVENTS.SOMEONE_TALKING];
console.log("=========有人对讲***********************************************************", data.uid);
callback&&callback(data);
break;
case TY.USER.CMD.intercom.STOP_TALKING:
console.log("=========有人结束对讲", data.uid);
callback = callback?callback:TY.EventManager.callbacks[TY.EventManager.EVENTS.SOMEONE_STOP_TALKING];
console.log("=========有人对讲***********************************************************", data.uid);
callback&&callback(data);
break;
case TY.USER.CMD.intercom.ADD_GROUP://添加组
TY.talkManager.callback&&TY.talkManager.callback(data);
break;
case TY.USER.CMD.intercom.DELE_GROUP://删除组
TY.talkManager.callback&&TY.talkManager.callback(data);
break;
case TY.USER.CMD.intercom.OFFLINE://下线
console.log("============对讲下线====================", data.uid);
break;
case TY.USER.CMD.intercom.ONLINE://上线
console.log("=============对讲上线===================", data.uid);
break;
case TY.USER.CMD.intercom.REMOVE_MEMBER:
break;
case TY.USER.CMD.intercom.CHANGE_GROUP://切换组
TY.talkManager.switchDispatchedGroup({id:data.id},function (resp) {
callback = callback?callback:TY.EventManager.callbacks[TY.EventManager.EVENTS.START_DISPATCHED];
console.log("=========被调度命令***********************************************************", data.uid);
callback&&callback(data);
});
break;
case TY.USER.CMD.intercom.LEAVE_GROUP://解散组
TY.talkManager.leaveGroup();
callback = callback?callback:TY.EventManager.callbacks[TY.EventManager.EVENTS.STOP_DISPATCHED];
console.log("=========解散命令***********************************************************", data.uid);
callback&&callback(data);
break;
default:
break;
}
TY.talkManager.callback&&TY.talkManager.callback(data);
});
socket.on('msg', function (data) {
console.log(new Date().toUTCString(), "===============================msg===================", data);
var callback;
switch (data.cmd) {
case TY.USER.CMD.msg.SOS://警报通知
callback = callback?callback:TY.EventManager.callbacks[TY.EventManager.EVENTS.SOS];
callback&&callback(data);
break;
case TY.USER.CMD.msg.FACE_RECORD://人脸识别通知
callback = callback?callback:TY.EventManager.callbacks[TY.EventManager.EVENTS.FACE_RECORD];
callback&&callback(data);
break;
case TY.USER.CMD.msg.ONLINE://警报通知
callback = callback?callback:TY.EventManager.callbacks[TY.EventManager.EVENTS.ONLINE];
console.log("=========有人上线***********************************************************", data.id);
callback&&callback(data);
break;
case TY.USER.CMD.msg.OFFLINE://警报通知
callback = callback?callback:TY.EventManager.callbacks[TY.EventManager.EVENTS.OFFLINE];
console.log("=========有人下线***********************************************************", data.id);
callback&&callback(data);
break;
case TY.USER.CMD.msg.SUBSCRIBE_GPS://gps实时通知
callback = callback?callback:TY.EventManager.callbacks[TY.EventManager.EVENTS.SUBSCRIBE_GPS];
callback&&callback(data);
break;
case TY.USER.CMD.msg.RECORD_STATUS://录像录音状态实时通知
var callback = TY.EventManager.callbacks[TY.EventManager.EVENTS.MEDIA];
callback&&callback(data);
break;
case TY.USER.CMD.msg.FILE_PROGRESS://文件下载进度
var callback = TY.EventManager.callbacks[TY.EventManager.EVENTS.FILE_PROGRESS];
callback&&callback(data);
break;
case TY.USER.CMD.msg.FILE_IMPORT://文件导入进度
var callback = TY.EventManager.callbacks[TY.EventManager.EVENTS.FILE_IMPORT];
callback&&callback(data);
break;
case TY.USER.CMD.msg.UPDATE_CERT://更换Https证书提醒
var callback = TY.EventManager.callbacks[TY.EventManager.EVENTS.UPDATE_CERT];
callback&&callback(data);
break;
case TY.USER.CMD.msg.SOMEONE_LOGIN://有人在抢登啦
callback = TY.EventManager.callbacks[TY.EventManager.EVENTS.SOMEONE_LOGIN];
callback&&callback(data);
break;
default:
break;
}
});
socket.on('pong', function(data){
var callback = TY.EventManager.callbacks[TY.EventManager.EVENTS.HEART];
callback&&callback(data);
});
socket.on('disconnect', function (resp) {
console.log(resp, "消息通道断开");
var callback = TY.EventManager.callbacks[TY.EventManager.EVENTS.DISCONNECT];
callback&&callback();
// this.disconnectCallBack&&disconnectCallBack();
});
});
},
disconnect(){//断开socket连接
if (self._socket) {
self._socket.disconnect();
self._socket = null;
L.Logger.info("通道断开连接");
}
this._login=false;
},
isLogin(){
return this._login;
},
_login:false,
_socket: null,
token: null,
uid: null
};
/**
* @description 登录的方法
* @param {String} params
*/
function test1(params){
}
/**
* 登录
* @function login -登录
* @param {String} params
* @param {Function} resolve -成功的回调
* @param {Function} reject -失败的回调
*/
TY.login = function (params, resolve, reject) {//登录
reject=reject?reject:resolve;
if(!params.serverUrl){
resolve&&reject(TY.RcCode.returnMsg(TY.RcCode.EMPTY_SERVER));
return;
}
if(TY.USER.isLogin()){//已登录
resolve&&reject(TY.RcCode.returnMsg(TY.RcCode.LOGINED));
return;
}
TY.URL.initServerUrl(params.serverUrl);
TY.reqPost(params, TY.URL.login, function (resp) {
TY.USER.setUser(resp.token, resp.id);//保存用户信息
resolve&&resolve(resp);
if (params.init) {
TY.USER.initSocket(function () {
resolve&&resolve(resp);
//成功
},reject);
}
}, reject);
};
/**
* 退出登录
* @typedef RcCodeMsg
* @property {Array.<code>} status -返回码
* @property {String} msg -返回的信息
*/
/**
* 退出
* @function logout -退出
* @return {Obj.<RcCodeMsg>}
*/
TY.logout = function () {
TY.USER.token = null;
TY.USER.disconnect();
return TY.RcCode.returnMsg(TY.RcCode.RC_CODE_S_OK);
};
TY.validation = function validation(options,fn1,fn2,fn) {
if (!options || typeof options != 'object') {
return TY.RcCode.PARAMETERS_ABNORMAL;
}else if(!TY.USER.token){
TY.USER.checkTokenInvalidEvent&&TY.USER.checkTokenInvalidEvent(data.status);
return TY.RcCode.EMPTY_TOKEN;
}
return 0;
};
TY.compositePut = function (url,options,fn,fn1,fn2){
if(TY.EventManager.callbacks[TY.EventManager.EVENTS.BEFORE_REQUEST]&&TY.EventManager.callbacks[TY.EventManager.EVENTS.BEFORE_REQUEST]()){
return;
}
var RcCode;
if(RcCode=TY.validation(options,fn1,fn2)){
fn2(TY.RcCode.returnMsg(RcCode));
return;
}
var msg;
if(fn && (msg = fn(options))){
if(typeof(msg)=='string' ){
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
}else{
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(msg));
}
return;
}
TY.reqPut(options, url, fn1, fn2);
};
TY.compositeGet = function (url,options,fn,fn1,fn2){
if(TY.EventManager.callbacks[TY.EventManager.EVENTS.BEFORE_REQUEST]&&TY.EventManager.callbacks[TY.EventManager.EVENTS.BEFORE_REQUEST]()){
return;
}
var RcCode;
if(RcCode=TY.validation(options,fn1,fn2)){
fn2(TY.RcCode.returnMsg(RcCode));
return;
}
var msg;
if(fn && (msg = fn(options))){
if(typeof(msg)=='string' ){
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
}else{
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(msg));
}
return;
}
TY.reqGet(options, url, fn1, fn2);
};
TY.compositePost = function (url,options,fn,fn1,fn2){
if(TY.EventManager.callbacks[TY.EventManager.EVENTS.BEFORE_REQUEST]&&TY.EventManager.callbacks[TY.EventManager.EVENTS.BEFORE_REQUEST]()){
return;
}
fn2=fn2?fn2:fn1;
if(TY.validation(options,fn1,fn2,fn)){
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL));
return;
}
var msg;
if(fn && (msg = fn(options))){
if(typeof(msg)== 'object'){
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(msg));
}else{
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
}
return;
}
TY.reqPost(options, url, fn1, fn2);
};
TY.compositeDown = function (url,options,fn,fn1,fn2,fn3){
if(TY.EventManager.callbacks[TY.EventManager.EVENTS.BEFORE_REQUEST]&&TY.EventManager.callbacks[TY.EventManager.EVENTS.BEFORE_REQUEST]()){
return;
}
fn2=fn2?fn2:fn1;
if(TY.validation(options,fn1,fn2,fn)){
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL));
return;
}
var msg;
if(fn && (msg = fn(options))){
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
return;
}
TY.reqDown(options, url, fn1, fn2, fn3);
};
TY.compositeUpload = function (url,options,fn,fn1,fn2,fn3){
if(TY.EventManager.callbacks[TY.EventManager.EVENTS.BEFORE_REQUEST]&&TY.EventManager.callbacks[TY.EventManager.EVENTS.BEFORE_REQUEST]()){
return;
}
if(TY.validation(options,fn1,fn2,fn)){
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL));
return;
}
var msg;
if(fn && (msg = fn(options))){
typeof fn2 == 'function'&&fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
return;
}
return TY.reqUpload(options, url, fn1, fn2, fn3);
};
TY.BroadcastManager=(function () {
/**
* @typedef params_sendMsg
* @property {Array} personnel -必选 广播人员id集合
* @property {String} msg -发送信息
*/
/**
* 发送广播消息
* @function sendMsg 发送广播消息
* @param {Array.<params_sendMsg>} params -广播人员id集合 / 发送内容
* @param {Function} resolve -成功的回调
* @param {Function} reject -失败的回调
*/
return {
getBrocastList(params,resolve, reject){
TY.compositePost(TY.URL.getBrocastList,params,null,resolve, reject);
},
sendMsg(params,resolve, reject){
TY.compositePost(TY.URL.brocastMsg,params,function (params) {
if(!params.personnel||params.personnel.length==0){
return "device id";
}
if(!params.msg){
return "msg";
}
},resolve, reject);
}
};
})();
TY.RTCManager=function(){
var methods={
//根据条件获取群组列表
getService(resolve, reject){
TY.compositeGet(TY.URL.service,{},null,resolve, reject);
},
getLicense(resolve, reject){
TY.compositeGet(TY.URL.license,{},null,resolve, reject);
},
setService(params,resolve, reject){
TY.compositePut(TY.URL.service,params,null,resolve, reject);
},
setLicense(params,resolve, reject){
TY.compositePut(TY.URL.license,params,null,resolve, reject);
},
setActivationKey(params,resolve, reject){
TY.compositePut(TY.URL.activate,params,null,resolve, reject);
},
activateOnline(params,resolve, reject){
TY.compositePut(TY.URL.activate,params,null,resolve, reject);
},
rtcStats(resolve, reject){
TY.compositeGet(TY.URL.rtcStats,{},null,resolve, reject);
},
};
return methods;
}();
TY.MoniterManager=function(){//监控类
var OPEN=1;
var CLOSE=0;
var RECORD={PICTURE:0,VIDEO:1,AUDIO:2};
var user2StreamMap={};//所有rtc room里的用户
var clients={};//房间id==>conference
var userMap={};//监控的用户
var recordFiles={};
var stid2uid={};
var methods= {
userMap:userMap,
//设置监控中,设备的媒体状态
/**
* @typedef Data
* @property {Number} recordAudio -本地录音,0关闭,1开启
* @property {Number} recordVideo -本地录像0关闭,1开启
* @property {Number} serverRecordAudio -远程录音,0关闭,1开启
* @property {Number} serverRecordAudio -远程录像,0关闭,1开启
*/
/**
* 设置监控中,设备的媒体状态
* @function setMediaStatus 设置监控中,设备的媒体状态
* @param {string} uid 人员id
* @param {Array.<Data>} data
*/
setMediaStatus:function(uid,data){
if (!userMap[uid]) {
return;
}
if(data.recordAudio){
userMap[uid].deviceRecordAudio=data.recordAudio;
}
if(data.recordVideo){
userMap[uid].deviceRecordVideo=data.recordVideo;
}
if(data.serverRecordAudio){
userMap[uid].serverRecordAudio=data.serverRecordAudio;
}
if(data.serverRecordVideo){
userMap[uid].serverRecordVideo=data.serverRecordVideo;
}
},
reEnterMoniter() {
for (var room in clients) {
clients[room].leave();
self._socket.emit("fill", room, function (token) {
console.log("重新获取的房间token", token);
var uids=[];
for (var uid in userMap) {
uids.push(parseInt(uid));
}
methods.changeRoom({id:uids,token:token});
});
}
},
saveRecordFile(id, file, fileName, type, name, time,rid){
recordFiles[id+"_"+type] = {id: id, file: file, fileName: fileName, type: type, name: name, time: time,rid:rid};
},
removeRecordFile(id,type){
delete recordFiles[id+"_"+type];
},
rtspPublish(params){
var externalStream;
var conference = Object.values(clients)[0];
params.conference =conference;
var options ={
url: params.url,
video: true,
audio: true
};
//如果无声摄像头,则禁用声音
if (params.isExternal == 1) {
options ={
url: params.url,
video: true,
audio: false
};
}
console.log(params.isExternal,"===================发布类型",options);
PureRTC.ExternalStream.create(options, function (err, stream) {
if (err) {
return console.log('create ExternalStream failed:', err);
}
externalStream = stream;
conference.publish(externalStream, {}, function(st) {
// st.attr("uid",params.id);
stid2uid[st.id()]=parseInt(params.id);
L.Logger.info('stream published:', st.id());
}, function(err) {
L.Logger.error('publish failed:', err);
});
});
},
startRtspMoniter(uids){
for (let i = 0; i < uids.length; i++) {
var user = userMap[uids[i]];
if (user.url) {
// user.url="rtsp://192.168.1.201:554/user=admin&password=admin123&channel=channel1&stream=0.sdp?";
methods.rtspPublish(user);
}
}
},
//开始监控
//{params={id:1,el:''}; "personnel":[553]}==》{id:1,el:'moniter'}
/**
* 返回状态码code
* @typedef code
* @property {int} 0 -初始化成功
* @property {int} 200 -请求成功
* @property {int} 202 -存在绑定关系,操作失败
* @property {int} 203 -名称不能重复
* @property {int} 204 -已存在, 不能重复
* @property {int} 205 -围栏绑定了设备, 请解绑后删除
* @property {int} 206 -班次绑定了设备,请解绑后删除
* @property {int} 207 -该角色已经被用户绑定,请解绑后删除
* @property {int} 208 -群主绑定了设备,请解绑后删除
* @property {int} 209 -该部门下有人员, 请删除后再次操作
* @property {int} 210 -人员编号冲突
* @property {int} 211 -该设备识别号已注册
* @property {int} 212 -app下已经发布了版本, 请删除后再次操作
* @property {int} 300 -token为空,可能未登陆
* @property {int} 213 -参数不能包括特殊字符
* @property {int} 401 -token无效
* @property {int} 406 -设备不在线
* @property {int} 407 -创建调度的人在调度
* @property {int} 460 -服务器地址为空
* @property {int} 461 -参数不能为空
* @property {int} 462 -参数异常
* @property {int} 463 -已经登陆过
* @property {int} 464 -不能删除设备文件
* @property {int} 465 麦克风不能用
* @property {int} 466 -RTC异常
* @property {int} 467 -已经有人说话
* @property {int} 468 -该设备不在监控房间
* @property {int} 469 -该设备不是被监控的状态
* @property {int} 470 -已经邀请改设备进入监控
* @property {int} 471 -结束失败 ,录制时间太短
* @property {int} 500 -系统错误
* @property {int} 410 -账号密码错误
* @property {int} 411 -用户不存在
* @property {int} 403 -没有权限
* @property {int} 999 -其他异常
*/
/**
* @typedef params_startMoniter
* @property {Number} id -监控成员id
* @property {String} el -渲染的div id
*/
/**
* 实时视音频数据统计
* @callback Function_stats
* @example
* [{
"type": "VideoBWE",
"id": "",
"stats": {
"available_send_bandwidth": "0",
"available_receive_bandwidth": "762808",
"transmit_bitrate": "0",
"retransmit_bitrate": "0"
}
}, {
"type": "ssrc_audio_recv",
"id": "ssrc_3672166965_recv",
"stats": {
"bytes_rcvd": "44753",
"delay_estimated_ms": "190",
"packets_rcvd": "449",
"packets_lost": "0",
"codec_name": "opus"
}
}, {
"type": "ssrc_video_recv",
"id": "ssrc_3867188859_recv",
"stats": {
"bytes_rcvd": "721034",//收到字节大小
"packets_rcvd": "576",
"packets_lost": "0",//丢失字节大小
"firs_sent": "0",
"nacks_sent": "1",
"plis_sent": "0",
"frame_width": "1280",//分辨率宽
"frame_height": "720",//分辨高
"framerate_rcvd": "21",//收到的帧率
"framerate_output": "21",//帧率输出
"current_delay_ms": "92",
"codec_name": "H264"
}
}]
*/
/**
* 回调返回的参数
* @callback function
* @property {Array.<code>} status -返回码
* @property {String} msg -返回的信息
*/
/**
* 开启监控
* @function startMoniter 开启监控
* @param {Obj.<params_startMoniter>} params -B必选 传入的参数
* @param {function} resolve -必选 成功回调
* @param {function} reject -不是必选 失败回调
* @param {Function_stats} statCallback -不是必选 实时视音频数据统计
*/
startMoniter(params,resolve, reject,statCallback){//开始监控
var options = params;
userMap[options.id] = options;
if(!params.el){
return "div id";
}
params={'personnel':[params.id]};
TY.compositePost(TY.URL.createMoniter,params,function (params) {
if(!params.personnel||params.personnel.length==0){
return "device id";
}
},function (resp) {//如果未进入则进入监控房间
TY.UserManager.getUserInfo({id:options.id},function (resp) {
userMap[options.id].name = resp.user.name;
});
userMap[options.id].resolve=resolve;
userMap[options.id].statCallback=statCallback;
if(resp.array&&resp.array.length>0){
var data = resp.array[0];
if(!clients[data.room]){//已在监控房间,则不再次进入
methods.changeRoom(data,resolve);
return;
}
}
userMap[options.id].conference = clients[data.room];
var inter = setInterval(function () {
if (userMap[options.id].conference.readySub) {
window.clearInterval(inter);
methods.startRtspMoniter([options.id]);
methods.subscribe(options.id);
}
},10);
}, reject);
},
//todo
//批量监控
/**
* @function startMoniters 开始批量监控
* @param {obj.<params>} params 参数
* @param {Function} resolve -成功的回调
* @param {Function} reject -失败的回调
*/
startMoniters(params,resolve, reject){//开始批量监控
TY.compositePost(TY.URL.removeMoniter,params,function (params) {
if(!params.personnel||params.personnel.length==0){
return "device id";
}
},function (resp) {//如果未进入则进入监控房间
resolve&&resolve(resp);
}, reject);
},
//{id:1}
/**
* 结束单个监控
* @function stopMoniter 结束单个监控
* @param {number} id -必选 结束监控成员id
* @param {function} resolve -成功回调
* @param {function} reject -失败回调
* @param {Function_stats} statCallback 实时视音频数据统计
*/
stopMoniter(params,resolve, reject){//关闭一个监控
var id = params.id;
params={'personnel':[params.id]};
TY.compositePost(TY.URL.removeMoniter,params,function (params) {
if(!params.personnel||params.personnel.length==0){
return "device id";
}
},function (resp) {//如果未进入则进入监控房间
console.log("============================取消订阅",id);
methods.unsubscribe(id,resolve, reject);
}, reject);
},
//关闭所有的监控
/**
* 结束全部监控
* @function stopAllMoniter 结束全部监控
* @param {Function} resolve -成功的回调
* @param {Function} reject -失败的回调
*/
stopAllMoniter(resolve, reject){
methods.uploadRecordFiles();
var result = TY.RcCode.returnMsg(TY.RcCode.RC_CODE_S_OK);
result.uids=window.Object.keys(userMap);
for (let i = 0; i <result.uids.length ; i++) {
methods.stopMoniter({id:result.uids[i]});
}
resolve&&resolve(result);
methods.leaveRoom();
},
leaveRoom(){
for (var room in clients) {
clients[room]& clients[room].leave();
}
clients={};
},
/**
* @param {*} params
* @param {Function} resolve
* @param {Function} reject
*/
changeRoom(params,resolve,reject) {
var token = params.token;
var moniterUids=params.id;
var conference = PureRTC.ConferenceClient.create({});
clients[params.room] =conference;
conference.join(token, function (resp) {
console.log("======================加入监控成功",conference.conferenceId);
Object.keys(conference.remoteStreams).forEach(function (key) {
var s = conference.remoteStreams[key];
console.log(s.id());
var uid = parseInt(s.attributes().uid);
if (uid) {
console.log(uid, "============提前进来" + s.id());
user2StreamMap[uid] = s;
if (moniterUids.indexOf(uid) != -1) {
console.log("============人家早早就进来了" + s.id());
//开始监控
userMap[uid].conference=conference;
methods.subscribe(uid);
}
}
});
conference.readySub=true;
conference.streams = resp.streams;
methods.startRtspMoniter(moniterUids);
}, function (err) {
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.RTC_ABNOMAL));
L.Logger.error('server connection failed:', err);
});
conference.on('stream-added', function (event) {
if (event.target.myId != conference.myId) {
return;
}
var stream = event.stream;
console.log(stream.attributes().uid, "============刚刚有人来" + event.stream.id());
L.Logger.info('stream added:', stream.id());
var uid = parseInt(stream.attributes().uid);
//开始监控
if (uid) {
console.log("==============有人被监控啦======" + stream.attributes().uid);
//开始监控
user2StreamMap[uid] = stream;
if (userMap[uid]) {
console.log(userMap[uid].statsTime);
if (userMap[uid].statsTime) {
window.clearInterval(userMap[uid].statsTime);
}
userMap[uid].conference=conference;
methods.subscribe(uid);
}
}else{
uid = stid2uid[stream.id()];
user2StreamMap[uid] = stream;
methods.subscribe(uid);
}
});
conference.on('stream-removed', function (event) {
if (event.target.myId != conference.myId) {
return;
}
var stream = event.stream;
console.log("========================================有人离开啦" + stream.attributes().uid);
var uid = parseInt(stream.attributes().uid);
let user = userMap[uid];
if(user){
user.serverRecordVideo=CLOSE;//离开设置本地录像状态为关闭
}
methods.uploadRecordFile(recordFiles[uid+"_"+RECORD.AUDIO]);
methods.uploadRecordFile(recordFiles[uid+"_"+RECORD.VIDEO]);
var callback = TY.EventManager.callbacks[TY.EventManager.EVENTS.MEDIA];
callback&&callback({id:uid,serverRecordAudio:CLOSE,serverRecordVideo:CLOSE});
delete user2StreamMap[uid];
});
return conference;
},
/**
* 订阅
* @param {String} uid
*/
subscribe(uid) {
if (!uid) {
return;
}
var self = this;
var stream = user2StreamMap[uid];
if (!stream) {
return;
}
var user = userMap[uid];
var showEl = document.getElementById(userMap[uid].el);
var childs = showEl.childNodes;
for (let i = 0; i < childs.length; i++) {
showEl.removeChild(childs[i]);
}
console.log(stream.id(), user.conference.myId);
user.conference.subscribe(stream, function () {
console.log("=========订阅===================================================",userMap[uid].el);
self.getStats(uid);
stream.show(userMap[uid].el);
user.stream = stream;
user.resolve&&user.resolve(TY.RcCode.returnMsg(TY.RcCode.RC_CODE_S_OK));
})
},
unsubscribe(uid,resolve, reject) {
var user = userMap[uid];
if(!user){
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.NOT_SUBSCRIBE_DEVICE));
return;
}
if (user.stream) {
stid2uid[user.stream.id()]&&delete stid2uid[user.stream.id()];
user.conference.unsubscribe(user.stream, function () {
console.log("=========取消订阅成功");
});
}
resolve&&resolve(TY.RcCode.returnMsg(TY.RcCode.RC_CODE_S_OK));
window.clearInterval(user.statsTime);
delete userMap[uid];
},
getStats(uid) {//定时渲染帧率
// console.log("==========又开始订阅数据信息啦===================**************************************************========");
var user = userMap[uid];
var statsTime = setInterval(function () {
user.conference.getConnectionStats(user.stream, function (stats) {
// console.log("================数据信息",stats);
user.statCallback&&user.statCallback(stats);
}, function (err) {
L.Logger.error('Get statistic information failed:', err);
}
);
}, 1000);
user.statsTime=statsTime;
},
uploadRecordFiles() {//退出后,上传未主动上传的服务器音视频
if (recordFiles&&window.Object.keys(recordFiles).length>0) {
$.each(recordFiles, function (index, file) {
methods.uploadRecordFile(file);
});
recordFiles = {};
}
},
uploadRecordFile(file){
if (!file) {
return;
}
setTimeout(function () {
methods.uploadFile(parseInt(file.id), "", file.fileName, file.type, file.name, file.time, function () {
});
}, 1000);
},
//远程录音
/**
* @typedef server_params
* @property {Number} uid -监控成员id
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject - 可不选 失败的回调
*/
/**
* 服务器录音状态切换
* @function serverRecordAudio 服务器录音状态切换
* @param {Obj.<server_params>} uid -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject - 可不选 失败的回调
*/
serverRecordAudio(uid,resolve, reject){
var user = userMap[uid];
if (user.serverRecordAudio != OPEN) {//
methods.startServerRecordAudio(uid,resolve, reject);
}else{
methods.stopServerRecordAudio(uid,resolve, reject)
}
},
//开始服务器录像
startServerRecordAudio(uid,resolve, reject){
var user = userMap[uid];
var options =
{
audioStreamId: user.stream.id(),
audioCodec: 'aac'
};
user.conference.startRecorder(options,
function (resp) {
methods.beginUploadFile(uid, resp.recorderId + ".mp4", RECORD.AUDIO, user.name, Date.now());
methods.saveRecordFile(uid, "", resp.recorderId + ".mp4", RECORD.AUDIO, user.name, Date.now(),resp.recorderId);
user.serverRecordAudio=OPEN;
user.time = Date.now();
resolve&&resolve(methods.returnMsg(user,TY.RcCode.RC_CODE_S_OK));
L.Logger.info('开始录像: ', resp);
},
function (err) {
reject&&reject(user);
}
);
},
//结束服务器录像
stopServerRecordAudio:function (uid, resolve, reject) {
var user = userMap[uid];
if(Date.now()-user.time<5000){
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.RECORD_LESS));
return;
}
user.conference.stopRecorder({recorderId: recordFiles[uid+"_"+RECORD.AUDIO].rid}, function (file, v) {
methods.uploadFile(uid,"",recordFiles[uid+"_"+RECORD.AUDIO].rid+".mp4",RECORD.AUDIO,user.name,recordFiles[uid+"_"+RECORD.AUDIO].time,function () {
user.serverRecordAudio=CLOSE;
resolve&&resolve(methods.returnMsg(user,TY.RcCode.RC_CODE_S_OK));
},function (resp) {
reject&&reject(methods.returnMsg(user,TY.RcCode.getRcCode(resp.status)));
});
L.Logger.info('结束录像: ', file);
}, function (err) {
//vue.alert.error("设备"+name+"结束录像失败");
var result = TY.RcCode.returnMsg(TY.RcCode.OTHER_ABNORMAL);
result.user = user;
reject&&reject(result);
// vue.alert.error("设备" + name + "结束录像失败");
L.Logger.error('Media recorder cannot stop with failure: ', err, user.rid);
}
);
},
returnMsg(user,code){
var result = TY.RcCode.returnMsg(code);
result.user = user;
return result;
},
//远程录音
/**
* 服务器录像状态切换
* @function serverRecordVideo 服务器录像状态切换
* @param {Obj.<server_params>} uid -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject - 可不选 失败的回调
*/
serverRecordVideo(uid,resolve, reject){
var user = userMap[uid];
if (user.serverRecordVideo != OPEN) {//
methods.startServerRecordVideo(uid,resolve, reject);
}else{
methods.stopServerRecordVideo(uid,resolve, reject)
}
},
//开始服务器录像
startServerRecordVideo(uid,resolve, reject){
var user = userMap[uid];
var options =
{
videoStreamId: user.stream.id(),
audioStreamId: user.stream.id(),
videoCodec: 'h264',
audioCodec: 'aac'
};
var suffix=".mp4";
//无声摄像头没有没有音频
if (user.isExternal==1) {
options =
{
videoStreamId: user.stream.id(),
videoCodec: 'h264',
};
suffix=".mkv";
}
user.conference.startRecorder(options,
function (resp) {
console.log(resp.recorderId);
methods.beginUploadFile(uid,resp.recorderId +suffix, RECORD.VIDEO, user.name, Date.now());
methods.saveRecordFile(uid, "", resp.recorderId + suffix, RECORD.VIDEO, user.name, Date.now(),resp.recorderId);
user.time=Date.now();
user.serverRecordVideo=OPEN;
resolve&&resolve(methods.returnMsg(user,TY.RcCode.RC_CODE_S_OK));
L.Logger.info('开始录像: ', resp);
},
function (err) {
console.log(err);
reject&&reject(user);
}
);
},
//结束服务器录像
stopServerRecordVideo:function (uid, resolve, reject) {
var user = userMap[uid];
if(Date.now()-user.time<3000){
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.RECORD_LESS));
return;
}
var suffix=".mp4";
//无声摄像,格式为mkv
if(user.isExternal==1){
suffix=".mkv";
}
console.log( recordFiles[uid+"_"+RECORD.VIDEO].rid);
user.conference.stopRecorder({recorderId: recordFiles[uid+"_"+RECORD.VIDEO].rid}, function (file, v) {
methods.uploadFile(uid,"",recordFiles[uid+"_"+RECORD.VIDEO].rid+suffix,RECORD.VIDEO,user.name,recordFiles[uid+"_"+RECORD.VIDEO].time,function () {
user.serverRecordVideo=CLOSE;
resolve&&resolve(methods.returnMsg(user,TY.RcCode.RC_CODE_S_OK));
},function (resp) {
reject&&reject(methods.returnMsg(user,TY.RcCode.getRcCode(resp.status)));
});
L.Logger.info('结束录像: ', file);
}, function (err) {
//vue.alert.error("设备"+name+"结束录像失败");
var result = TY.RcCode.returnMsg(TY.RcCode.OTHER_ABNORMAL);
result.user = user;
reject&&reject(result);
// vue.alert.error("设备" + name + "结束录像失败");
L.Logger.error('Media recorder cannot stop with failure: ', err,user.rid);
}
);
},
beginUploadFile(id, fileName, type, name, startTime) {//开始录音/录像,保存到服务器
TY.compositePost(TY.URL.beginUploadFile, {
"id": id,
"name": fileName,
"type": type,
"begintime": startTime,
});
},
/**
* 服务器抓拍
* @function screenshot 服务器抓拍
* @param el
* @param {Obj.<server_params>} uid -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject - 可不选 失败的回调
*/
screenshot (uid,resolve, reject) {
var user = userMap[uid];
var el=document.getElementById("stream"+user.stream.id());
var canvas = document.createElement("canvas");
var video = el;
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
var canvasCtx = canvas.getContext("2d");
canvasCtx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, video.videoWidth, video.videoHeight);
var dataUrl = canvas.toDataURL("image/png");
var fileName = ".jpg";
// var time = moment().format("YYYY-MM-DD HH:mm:ss");
methods.uploadFile(uid, dataUrl, fileName, RECORD.PICTURE, user.name, Date.now(),function () {
resolve&&resolve(methods.returnMsg(user,TY.RcCode.RC_CODE_S_OK));
},function (resp) {
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.getRcCode(resp.status)));
});
},
uploadFile (uid, file, fileName, type, name, startTime, callback,reject) {
TY.compositePost(TY.URL.uploadImg, {
"id": uid,
"file": file,
"name": fileName,
"type": type,
"begintime": startTime,
"endtime": Date.now()
}, null,function (resp, success) {
methods.removeRecordFile(uid,type);
callback&&callback();
},reject);
},
//设备录音
/**
* serverRecordAudio|serverRecordVideo(本地录音/录像状态切换,本地拍照)
* 输入参数
* @typedef device_params
* @property {Number} id -必选 监控成员id
* @property {Function} resolve - 必选成功的回调
* @property {Function} reject - 可不选 失败的回调
*/
/**
* 远程录音状态切换
* @function deviceRecordAudio 远程录音状态切换
* @param {Obj.<device_params>} options -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject - 可不选 失败的回调
*/
deviceRecordAudio(options,resolve, reject){
reject=reject?reject:resolve;
var user = userMap[options.id];
TY.DeviceManager.remoteControl({id:[options.id],recordAudio:user.deviceRecordAudio!=OPEN?OPEN:CLOSE},function (resp) {
user.deviceRecordAudio =user.deviceRecordAudio==OPEN?CLOSE:OPEN;
resolve&&resolve(methods.returnMsg(user,TY.RcCode.RC_CODE_S_OK));
},function (resp) {
reject&&reject(methods.returnMsg(user,TY.RcCode.OTHER_ABNORMAL));
});
},
//设备录像
/**
* 远程录像状态切换
* @function deviceRecordVideo 远程录像状态切换
* @param {Obj.<device_params>} options -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject - 可不选 失败的回调
*/
deviceRecordVideo(options,resolve, reject){
reject=reject?reject:resolve;
var user = userMap[options.id];
TY.DeviceManager.remoteControl({id:[options.id],recordVideo:user.deviceRecordVideo!=OPEN?OPEN:CLOSE},function () {
user.deviceRecordVideo =user.deviceRecordVideo==OPEN?CLOSE:OPEN;
resolve&&resolve(methods.returnMsg(user,TY.RcCode.RC_CODE_S_OK));
},function (resp) {
resp.user = user;
reject&&reject(resp);
});
},
//设备抓拍
/**
* 远程录音抓拍
* @function devicePicture 远程录音抓拍
* @param {Obj.<device_params>} options -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject - 可不选 失败的回调
*/
devicePicture(options,resolve, reject){
reject=reject?reject:resolve;
var user = userMap[options.id];
TY.DeviceManager.remoteControl({id:[options.id],capture:OPEN},function (resp) {
console.log(resp);
resolve&&resolve(methods.returnMsg(user,TY.RcCode.RC_CODE_S_OK));
},function (resp) {
console.log(resp);
reject&&reject(methods.returnMsg(user,TY.RcCode.OTHER_ABNORMAL));
});
}
};
return methods;
};
TY.TalkManager=function(){//对讲类
var el="voice_talk";//对讲渲染的div id
var body = document.getElementsByTagName("body");
var div = document.createElement("div");
div.style.display='none';
div.setAttribute("id",el);
body[0].appendChild(div);
var curGid=null;//当前组
var lastGid=null;
var createOptions = {
video: false,
audio: true
};
var dispatched=false;//被调度
var dispatcher=false;//主动调度
var talking=false;//是否讲话中
var talkingUid=null;//讲话者id
var init=false;
var conference=null;
var groupList=[];
var groupsObj ={};
var methods= {
/**
* 获取当前对讲人id
* @function getTalkingUid 获取当前对讲人id
* @return {Number} -获取当前对讲人id
*/
getTalkingUid(){//获取当前对讲人id
return talkingUid;
},
/**
* 是否是对讲中
* @function isTalking 是否是对讲中
* @return {Boolean} -是否是对讲中
*/
isTalking(){//是否是对讲中
return talking;
},
/**
* 是否被调度
* @function isDispached 是否被调度
* @return {Boolean} -是否被调度 true被调度, false没有被调度
*/
isDispached(){//是否被调度
return dispatched;
},
/**
* 上一个群组的id
* @function getLastGid 上一个群组id
* @return {Boolean} -上一个群组的id
*/
getLastGid () {//上一个群组id
return lastGid;
},
/**
* 获取当前群组id
* @function getCurGid 获取当前群组id
* @return {Boolean} -获取当前群组id
*/
getCurGid () {//获取当前群组id
return curGid;
},
/**
* 是否正在调度别人
* @function isDispatcher 是否正在调度别人
* @return {Boolean} -是否正在调度别人
*/
isDispatcher(){//是否正在调度别人
return dispatcher;
},
getGroupInfo(params){//获取群组信息
return groupsObj[params.id];
},
getGroupList(){
return groupList;
},
/**
* 查询群组参数
* @typedef params
* @property {Number} pageSize -每页条数 必选
* @property {Number} pageNumber -当前页数 必选
* @property {string} groupName -群主名称 可不选
* @property {Number} type -0普通群组 可不选
* @property {string} name -群主名称 可不选
*/
/**
* 群组列表集合
* @typedef groups
* @property {int} id -返回码
* @property {string} name -返回信息
* @property {int} unallocate -1为未分配成员集合(不能切换到这个组,00为普通群组,可以切换到这个组)
* @property {int} type -群组类型(0为普通群组)
*/
/**
* @callback Function_getGroups
* @param {int} status -返回码
* @param {string} msg -返回信息
* @param {obj.<groups>} -群组列表集合
*/
/**
* 获取群组列表
* @function getGroups -获取群组列表
* @param {Function_getGroups} resolve -成功回调
* @param {function} reject -失败回调
*/
getGroups (params,resolve, reject) {
var main = this;
TY.compositeGet(TY.URL.loadTalkGroup,params,null,function (resp) {
var isDispatch=true;
groupsObj={};
if(resp.intercom.length>1){
lastGid = resp.intercom[1].id;
for(var index in resp.intercom){//当前组
var group = resp.intercom[index];
groupsObj[group.id]=group;
if(group.id==resp.id){
isDispatch=false;
// break;
}
}
}
if(isDispatch&&resp.id!=0){//调度状态
dispatched=true;
if (resp.intercom.length > 1) {//调度的话,当前组为普通组
lastGid=resp.intercom[1].id;
}
}
curGid=resp.id;
if(params.immediateEnterGroup){
main.switchGroup({id:curGid});
}
groupList=resp.intercom;
resolve({status:TY.RcCode.RC_CODE_S_OK.code,groups:resp.intercom});
}, reject);
return methods;
},
//取消调度 {id:talkManager.getCurGid(),type:TY.GroupManager.TYPE.DISPATCH}
/**
* 取消调度的参数
* @typedef params_inactiveDispatchGroup
* @property {int} id -必选 对讲组id
*/
/**
* 取消调度
* @function inactiveDispatchGroup 结束调度
* @param {Obj.<params_inactiveDispatchGroup>} params 对讲组的id
* @param {function} resolve -成功回调
* @param {function} reject -失败回调
*/
inactiveDispatchGroup(params,resolve, reject){
if (params) {
params.type = TY.GroupManager.TYPE.DISPATCH;
}
TY.GroupManager.deleteGroup(params,function(resp){
curGid = null;
dispatcher=false;
resolve&&resolve(TY.RcCode.returnMsg(TY.RcCode.RC_CODE_S_OK));
},function(resp){
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.getRcCode(resp.status)));
});
},
//{"type":"dispatch","name":"调度",'personnel':[1]}
//激活调度
/**
* @typedef params_activeDispatchGroup
* @property {array} personnel -调度成员id集合
*/
/**
* 激活调度
* @function activeDispatchGroup 激活调度
* @param {Obj.<params_activeDispatchGroup>} params 对讲组的id
* @param {function} resolve -成功回调
* @param {function} reject -失败回调
*/
activeDispatchGroup (params,resolve, reject) {//激活调度组
if (params) {
params.type = TY.GroupManager.TYPE.DISPATCH;
params.name="调度";
}
TY.GroupManager.addGroup(params,function(res){
methods.joinRoom(res.token, function(resp){
dispatcher=true;
curGid = res.id;
resp.id = res.id;
resolve&&resolve(resp);
},reject);
},function(resp){
reject&&reject(resp);
});
},
//{id:id}
switchDispatchedGroup(params,resolve, reject){//切换到被调度组
if (!dispatcher) {//非主动调度
lastGid=curGid;
}
dispatched=true;
methods.switchGroup(params,resolve, reject);
},
//切换群组
/**
* 切换群组
* @typedef params_switchGroup
* @property {Number} id -必选 群组id
*/
/**
* @typedef RcCodeMsg
* @property {array.<code>} status -返回状态码
* @property {string} msg -返回信息
*/
/**
* @typedef rights
* @property {string} title -标题名称
* @property {string} url -权限路径
* @property {int} pid -父级权限id
* @property {int} id -权限id
*/
/**
* @typedef switchGroup_user
* @property {int} id -用户id
* @property {string} name -用户名称
* @property {string} rtcHost -视频通讯连接地址
* @property {Obj.<rights>} right -当前用户的权限集合
* @property {Obj.<RcCodeMsg>} status -返回状态
*/
/**
* @callback Function_resolveObj
* @param {int.<code>} status 返回码
* @param {string} msg 返回信息
* @param {obj.<switchGroup_user>} user 用户信息
*/
/**
* 切换群主
* @function switchGroup 切换群组
* @param {Obj.<params_switchGroup>} params -群组的id
* @param {Function_resolveObj} resolve -成功回调
* @param {function} reject -失败回调
*/
switchGroup(params,resolve, reject) {//切换组
TY.compositePost(TY.URL.changeTalkGroup,{id:params.id},function (params) {
if(!params.id){
return "group id";
}
},function (resp) {
methods.joinRoom(resp.token, function(resp){
curGid = params.id;
if(!dispatched&&!dispatcher&&curGid){//普通组的话,则上一个组
lastGid=curGid;
}
dispatcher=false;
resolve&&resolve(resp);
},reject);
}, reject);
},
//对讲状态切换
/**
* 对讲状态切换
* @function changeTalkStatus 切换对讲状态(对讲申请/对讲结束)
* @param {function} resolve -成功回调
* @param {function} reject -失败回调
*/
changeTalkStatus(resolve, reject){
if (talking) {
methods.stopTalk({id:curGid},resolve, reject);
}else{
methods.startTalk({id:curGid},resolve, reject);
}
},
startTalk(params,resolve, reject){
reject=reject?reject:resolve;
TY.compositePost(TY.URL.startTalk,params,function (params) {
if(!params.id){
return "group id";
}
},function (resp) {
methods.publish(resolve, reject);
}, reject);
},
stopTalk(params,resolve, reject){
reject=reject?reject:resolve;
TY.compositePost(TY.URL.stopTalk,params,function (params) {
if(!params.id){
return "group id";
}
},function (resp) {
methods.unPublish(resolve, reject);
}, reject);
},
joinRoom(token,resolve, reject){//获得服务器许可后,进入对讲组房间内
this.leaveRoom();
conference = PureRTC.ConferenceClient.create({});
conference.streamMap={};
conference.join(token, function (resp) {
console.log("======================加入組成功",conference.conferenceId);
PureRTC.LocalStream.create(
createOptions, function (err, stream) {
if (err) {
console.log("麦克风不可用");
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.MICROPHONE_LIMIT));
return;
}
conference.localStream = stream;
conference.localStream.attr("uid", TY.USER.uid);
//保存已经在线的用户信息
Object.keys(conference.remoteStreams).forEach(function (key) {
var s = conference.remoteStreams[key];
console.log('============================有人进来啦',s.attributes().uid);
conference.streamMap[parseInt(s.attributes().uid)]= s;
});
}
)
;
conference.streams = resp.streams;
resolve(TY.RcCode.returnMsg(TY.RcCode.RC_CODE_S_OK));
}, function (err) {
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.RTC_ABNOMAL));
L.Logger.error('server connection failed:', err);
});
conference.on('stream-added', function (event) {
if (event.target.myId != conference.myId) {
return;
}
var stream = event.stream;
L.Logger.info('stream added:', stream.id());
var fromMe = false;
for (var i in conference.localStreams) {
if (conference.localStreams.hasOwnProperty(i)) {
if (conference.localStreams[i].id() === stream.id()) {
fromMe = true;
init=true;
break;
}
}
}
if (fromMe) {
L.Logger.info('stream', stream.id(),
'is from me; will not be subscribed.');
return;
} else {
console.log('============================有人进来啦',stream.attributes().uid, talkingUid);
conference.streamMap[parseInt(stream.attributes().uid)]= stream;
if (stream.attributes().uid == talkingUid) {
methods.subscribe(stream);
}
}
});
conference.on('stream-removed', function (event, cid) {
if (event.target.myId != conference.myId) {
return;
}
var stream = event.stream;
delete conference.streamMap[parseInt(stream.attributes().uid)];
});
conference.on('user-joined', function (event, cid) {
if (event.target.myId != conference.myId) {
return;
}
L.Logger.info('user joined:', event.user);
});
},
publish:function(resolve,reject){//发布自身的流
if (!conference||!conference.localStream) {
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.RTC_ABNOMAL));
return;
}
console.log("========准备发布====",conference.localStream.id());
conference.publish(conference.localStream, {}, function (st) {
talking=true;
talkingUid=st.attributes().uid;
resolve(TY.RcCode.returnMsg(TY.RcCode.RC_CODE_S_OK));
console.log("===============================================发布成功==" + st.id());
}, function (err) {
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.RTC_ABNOMAL));
L.Logger.error('publish failed:', err);
});
},
unPublish:function(resolve,reject){//取消发布自身的流
console.log("========准备取消====",conference.localStream.id());
conference.unpublish(conference.localStream, function () {
talking=false;
resolve(TY.RcCode.returnMsg(TY.RcCode.RC_CODE_S_OK));
console.log("===============================================取消发布成功==");
}, function (err) {
L.Logger.error('unpublish failed:', err);
reject(TY.RcCode.returnMsg(TY.RcCode.RTC_ABNOMAL));
});
talkingUid=null;
},
listenTalk:function(uid){//听取声音
talkingUid = uid;
var stream = conference.streamMap[uid];
if(stream){
methods.subscribe(stream);
}
},
subscribe:function(stream){//订阅语音流,听声音
var options = {video: false, audio: true};
conference.subscribe(stream, options,function () {
stream.show(el);
console.log("==========================================订阅成功",stream.id());
}, function (err) {
console.log(err);
});
talkingUid=null;
},
leaveGroup(){
curGid=null;
dispatched=false;
methods.leaveRoom();
},
leaveRoom:function(){
if (conference&&conference.myId) {
conference.leave();
}
},
callback:null,//对象事件注册
addListEvent(event){
methods.callback=event;
},
};
TY.talkManager = methods;
return methods;
};
TY.UserManager={
//根据条件获取群组列表
//{"pageSize":10,"name":"","polNo":"","pageNumber":1}
/**
* 人员信息 / 修改人员信息
* @typedef User
* @property {Number} roleld -必选 角色id
* @property {String} departmentName -必选 部门名称
* @property {Number} departmentId - 不是必选 部门id
* @property {Number} id - 不是必选 人员id
* @property {String} password -不是必选 密码
* @property {String} name -不是必选 人员名称
* @property {String} account -必选 人员编号(也是登录账号)
* @property {String} describe -不是必选 描述
* @property {String} phone -不是必选 手机号码
* @property {String} email -不是必选 邮箱
*/
/**
* @callback Function_obj_result_getUserByParams
* @param {Number} status -必选 200成功,其他失败
* @param {obj.<User>} rows -用户信息 人脸库信息
* @param {Number} total -必选 总条数
*/
/**
* @typedef params_getUserByParams
* @property {Number} pageSize -必选 每页条数
* @property {Number} pageNumber -必选 当前页数(从1开始)
* @property {string} polNo -不是必选 人员编号
* @property {string} name -不是必选 人员名称
*/
/**
* 获取用户列表
* @function getUserByParams 获取用户列表
* @param {Obj.<params_getUserByParams>} params -必选
* @param {Function_obj_result_getUserByParams} resolve -成功的回调
* @param {Function} reject -失败的回调
*/
getUserByParams(params,resolve, reject){ //获取用户列表
TY.compositePost(TY.URL.loadAllUser,params,null,resolve, reject);
},
//添加用户
// { roleId: "", departmentName: "", departmentId: null, id: null, name: "", account: "", describe: "", phone: '', email: ''}
/**
* 人员添加
* @typedef params_addUser
* @property {Number} id -必选 群组id
* @property {array} personnel -必选 人员列表
* @property {Number} type - 不是必选 0普通组,2领导组
*/
/**
* 人员添加
* @function addUser -添加群组人员
* @param {Obj.<params_addUser>} params -必选
* @param {Function} resolve --必选 成功的回调
* @param {Function} reject - -可不选 失败的回调
*/
addUser(params,resolve, reject){
TY.compositePost(TY.URL.addUser,params,function (params) {
if(!params.departmentId){
return "departmentId";
}
if(!params.account){
return "account";
}
if(!params.name){
return "name";
}
},resolve, reject);
},
/**
* 人员修改 查询用户参数
* @function modifyUser -人员修改
* @param {Obj.<User>} params -必选
* @param {Function} resolve -成功的回调
* @param {Function} reject -失败的回调
*/
modifyUser(params,resolve, reject){
TY.compositePost(TY.URL.modifyUser,params,function (params) {
if(!params.id){
return "user id";
}
},resolve, reject);
},
//删除用户
/**
* 人员删除
* @typedef params_deleteUser -人员删除
* @property {Number} id -必选 群组id
* @property {array} personnel -必选 人员列表
* @property {Number} type - 不是必选 0普通组,2领导组
*/
/**
* @function deleteUser -移除群组成员
* @param {Obj.<params_deleteUser>} params -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject - 可不选 失败的回调
*/
deleteUser(params,resolve, reject){
TY.compositePost(TY.URL.deleteUser,params,function (params) {
if(!params.id){
return "id";
}
},resolve, reject);
},
//获取用户信息
/**
* 人员添加
* @typedef params_getUserInfo
* @property {Number} roleld -必选 角色id
* @property {String} departmentName -必选 部门名称
* @property {Number} departmentId - 不是必选 部门id
* @property {String} name -不是必选 人员名称
* @property {String} account -必选 人员编号(也是登录账号
* @property {String} describe -不是必选 描述
* @property {String} phone -不是必选 手机号码
* @property {String} email -不是必选 邮箱
*/
/**
* @callback Function_User -人员信息
* @property {Number} roleld -必选 角色id
* @property {String} departmentName -必选 部门名称
* @property {Number} departmentId - 不是必选 部门id
* @property {String} name -不是必选 人员名称
* @property {String} account -必选 人员编号(也是登录账号
* @property {String} describe -不是必选 描述
* @property {String} phone -不是必选 手机号码
* @property {String} email -不是必选 邮箱
*/
/**
* 人员添加 查询用户参数
* @function getUserInfo -获取人员信息
* @param {number} id -必选 人员id
* @param {Function_User} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
getUserInfo(params,resolve, reject){
TY.compositePost(TY.URL.getUserInfo,params,function (params) {
if(!params.id){
return "id";
}
},resolve, reject);
},
//转移部门
/**
* params_transferUnit 转移部门的参数
* @typedef params_transferUnit
* @property {Number} id -必选 部门id
* @property {array} personnel -必选 转移id列表。如[3,4,5]
*/
/**
* 转移部门
* @function transferUnit -转移部门
* @param {Obj.<params_transferUnit>} params -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
transferUnit(params,resolve, reject){
TY.compositePost(TY.URL.transferDepartment,params,function (params) {
if(!params.personnel||params.personnel.length==0){
return reject(TY.RcCode.returnMsg(TY.RcCode.EMPTY_PROPS,"users"));
}
if(!params.id){
return "departmentId";
}
},resolve, reject);
},
//修改密码
modifyPwd(params,resolve, reject){
TY.compositePost(TY.URL.modifyPwd,params,function (params) {
if(!params.id){
return "user id";
}
},resolve, reject);
}
};
//文件检索管理类
TY.FileManager={
TYPE:{PLATFORM:0,DEVICE:1,DOCUMENT:2},
//根据条件获取群组列表
//{"pageSize":10,"name":"","type":"","pageNumber":1,"begintime',endtime:'',personnel:[1,2]}
//todo personnel 数组不好
/**
* 获取文件参数
* @typedef params_getFilesByParams
* @property {Number} pageSize -必选 每页条数
* @property {Number} pageNumber -必选 当前页码 (从1开始)
* @property {Array} personnel -必选 人员id;列表
* @property {Number} storageType -必选 存储平台(0平台,1设备,2我的文档)
* @property {String} type -必选 文件类型(0视频,1图片,2音频,,3设备日志,
* @property {String} begintime -不是必选 开始时间
* @property {String} endtime -不是必选 结束时间
*/
/**
* 获取文件列表
* @function transferUnit -获取文件列表
* @param {Obj.<params_getFilesByParams>} params -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
getFilesByParams(params,resolve, reject){
reject=reject?reject:resolve;
TY.compositePost(TY.URL.loadFile,params,function (params) {
if(!params.personnel){
return "device id";
}
params.type=params.type?params.type:9;
params.personnel=[params.personnel];
},resolve, reject);
},
//删除文件
// { {id: 1 array: [{id: 2, storageType: row.storageType, type: row.type}]}
/**
* file
* @typedef file
* @property {Number} id -必选 人员id
* @property {Number} storageType -必选 存储平台(0平台,1设备,2我的文档)
* @property {Number} type -必选 文件类型(0视频,1图片,2音频,3设备日志,9全部)
*/
/**
* 删除文件参数
* @typedef params_deleteFile
* @property {Number} id - 必选 人员id
* @property {Array.<file>} array -必选 删除文件集合
*/
/**
* 删除文件
* @function deleteFile -删除文件
* @param {array.<params_deleteFile>} params -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
deleteFile(params,resolve, reject){
TY.compositePost(TY.URL.deleteFile,params,function (params) {
// if(!params.id){
// return "device id";
// }
if(!params.array||params.array.length==0){
return "files";
}
for(var file in params.array){
if(file.storageType==TY.FileManager.TYPE.DEVICE){
return TY.RcCode.DEVICE_FILE_DELETE_LIMIT;
}
}
},resolve, reject);
},
//{"id":1281,"did":535,"type":2,"storageType":0}
/**
* 下载文件 输入参数
* @typedef params_downloadFile
* @property {Number} id - 必选 人员id
* @property {Number} storageType -必选 存储平台(0平台,1设备,2我的文档
* @property {Number} type -必选 文件类型(0视频,1图片,2音频,3设备日志,9全部)
* @property {Number} did - 必选 人员id
*/
/**
* 下载文件
* @function downloadFile -下载文件
* @param {array.<params_downloadFile>} params -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
downloadFile(params,resolve, reject){
// // {"id":null,"array":[{"id":1282,"storageType":0,"type":0}]}
// var options={};
// options.id = params.puid;
// options.array = [{id:params.id,storageType:params.storageType,type:params.type}];
// params=options;
// var paramscompositeDown = {"id":1281,"did":535,"type":2,"storageType":0};
TY.compositeDown(TY.URL.downloadFile,params,function (params) {
if(!params.id){
return "file id";
}
if(!params.did){
return "device id";
}
if(params.storageType==null){
return "storageType id";
}
// if(!params.array||params.array.length==0){
// return "files";
// }
},resolve, reject);
},
/**
* impFileObj
* @typedef impFileObj
* @property {Number} id - 文件id
* @property {Number} type -文件类型(0视频,1图片,2音频,3设备日志)
*/
/**
* impFile
* @typedef impFile
* @property {Number} type - 必选 1日志文件,0其他文件
* @property {Array.<impFileObj>} files -批量导入文件集合
*/
/**
* 导入文件参数
* @typedef params_importFile
* @property {Number} id - 必选 人员id
* @property {Array.<impFile>} files -批量导入文件集合
*/
/**
* 导入文件
* @function importFile -导入文件
* @param {array.<params_importFile>} options -必选
* @param {Function} fn1 - 必选 成功的回调
* @param {Function} fn2 -可不选 失败的回调
*/
importFile: function (options, fn1, fn2) {
TY.compositePost(TY.URL.importFile, options, (options) => {
if(!options.id){
return 'id';
}
if(!options.files){
return 'files';
}
if(options.files && typeof options.files == 'string'){
options.files = JSON.parse(options.files);
}
}, fn1, fn2);
}
};
//群组类
TY.GroupManager={
TYPE:{NORMAL:0,LEADER:1,DISPATCH:2},//普通组,领导组,调度组
//添加群组
/**
* 人员添加
* @typedef params_addGroup_Or_ModifyGroup -添加或修改群组参数
* @property {String} id - 可不选 群组id(空为添加,否则为修改)
* @property {String} name -可不选 群组名称
* @property {Number} type -必选 0是普通组
* @property {String} describe -必选 描述
*/
/**
* 群组添加
* @function addGroup -群组添加
* @param {array.<params_addGroup_Or_ModifyGroup>} params -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
addGroup(params,resolve, reject){
var url =TY.URL.modifyGroup,params;
if(params.type==TY.GroupManager.TYPE.DISPATCH){
url= TY.URL.createDispatch;
params.personnel=params.personnel&¶ms.personnel.length>0?params.personnel:[TY.USER.uid];
}
TY.compositePost(url,params,function (params) {
if(params.type==null){
return "group type";
}
if(!params.name){
return "name";
}
},resolve, reject);
},
//删除群组
/**
* 删除群组
* @typedef params_deleteGroup
* @property {Number} id -必选 群主id
*/
/**
* 删除群组
* @function deleteGroup -删除群组
* @param {array.<params_deleteGroup>} params -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
deleteGroup(params,resolve, reject){
var url =TY.URL.deleteGroup,params;
if(params.type==TY.GroupManager.TYPE.DISPATCH){
url= TY.URL.stopDispatch;
}
TY.compositePost(url,params,function (params) {
if(!params.id){
return "group id";
}
},resolve, reject);
},
//修改群组信息
/**
* 修改群组信息
* @function modifyGroup -修改群组信息
* @param {array.<params_addGroup_Or_ModifyGroup>} params -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
modifyGroup(params,resolve, reject){
TY.compositePost(TY.URL.modifyGroup,params,function (params) {
if(!params.id){
return "group id";
}
if(params.type==null){
return "group type";
}
if(!params.name){
return "name";
}
},resolve, reject);
},
//群组信息
/**
* @callback Function_group_obj
* @param {Number} id -必选 群组id
* @param {String} name -可不选 群组名称
* @param {Number} type -可不选 0普通组
* @param {Number} leaderld -可不选 创建人员id
* @param {Number} leader -可不选 创建人员名称
* @param {String} time -可不选 创建时间
* @param {String} desc -可不选 描述
* @param {Number} status - 可不选 200成功,具体查看TY.RcCode
*/
/**
* 获取群组信息
* @function getGroupInfo -获取群组信息
* @param {Number} id -必选 群组信息
* @param {Function_group_obj} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
getGroupInfo(params,resolve, reject){
TY.compositePost(TY.URL.groupInfo,params,function (params) {
if(!params.id){
return "group id";
}
},resolve, reject);
},
// (群组列表)
/**
* 群组信息
* @typedef Group 群组信息
* @property {Number} id - 必选 人员id
* @property {String} name - 不是必选 群组名称
* @property {Number} type - 不是必选 0普通组
* @property {Number} leaderId - 不是必选 创建人员id
* @property {Number} leader - 不是必选 创建人员名称
* @property {String} time - 不是必选 创建时间
* @property {String} desc - 不是必选 描述
*/
/**
* @callback Function_obj_group_list
* @param {Number} status - 必选 200成功,其他失败
* @param {Obj.<Group>} rows -必选 群组列表数据
* @param {Number} total -必选 总条数
*/
/**
* 查询群组列表
* @function getGroupsByParams -查询群组列表
* @param {array.<params>} params -必选
* @param {Function_obj_group_list} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
getGroupsByParams(params,resolve, reject){
TY.compositePost(TY.URL.loadGroupList,params,null,resolve, reject);
},
//分配群组成员
/**
* 分配群组参数
* @typedef params_allocateGroup
* @property {Number} id - 必选 人员id
* @property {Array} devicelds - 不是必选 人员id列表(疑问)
* @property {Number} type - 不是必选 0普通组
*/
/**
* 分配群组成员
* @function allocateGroup -分配群组成员
* @param {array.<params_allocateGroup>} params -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
allocateGroup(params,resolve, reject){
TY.compositePost(TY.URL.loadGroupUsers,params,function (params) {
if(!params.id){
return "group id";
}
},resolve, reject);
},
//添加群组成员
addUser(params,resolve, reject){
var url =TY.URL.removeGroupUser;
if(params.type==TY.GroupManager.TYPE.DISPATCH){
url= TY.URL.addDispatchUser;
}
TY.compositePost(url,params,function (params) {
if(!params.personnel||params.personnel.length==0){
return "users";
}
if(!params.id){
return "group id";
}
},resolve, reject);
},
//移除群组成员
deleteUser(params,resolve, reject){
var url =TY.URL.removeGroupUser;
if(params.type==TY.GroupManager.TYPE.DISPATCH){
url= TY.URL.removeDispatchUser;
}
TY.compositePost(url,params,function (params) {
if(!params.personnel||params.personnel.length==0){
return "users";
}
if(!params.id){
return "group id";
}
if(params.type==TY.GroupManager.TYPE.DISPATCH){
params.room = params.id;
delete params['id'];
}
},resolve, reject);
},
//群组成员列表
/**
* 群组成员列表 返回值:组内群组成员列表
* @function getAllocateUsersByParams -群组成员列表
* @param {Number} id -必选 群组id
* @param {Function_obj_groupUser} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
getAllocateUsersByParams(params,resolve, reject){
TY.compositePost(TY.URL.loadGroupUser,params,function (params) {
if(!params.id){
return "group id";
}
},resolve, reject);
},
//组内未分配到的群组成员列表
/**
* members -组内成员列表
* @typedef Members
* @property {String} name - 所绑定的人员名称
* @property {String} puid -设备编号
* @property {Number} id -人员id
*/
/**
* 分配群组参数
* @callback Function_obj_groupUser
* @param {Number} status -200成功,具体查看TY.RcCode
* @param {Obj.<Members>} members -组内成员列表
*/
/**
* 组内未分配到的群组成员列表
* @function getUnAllocateUsers -组内未分配到的群组成员列表
* @param {Number} id -必选 群组id
* @param {Function_obj_groupUser} resolve - 必选 成功的回调
* @param {Function} reject -可不选 失败的回调
*/
getUnAllocateUsers(params,resolve, reject){
TY.compositePost(TY.URL.loadUnGroupUser,params,function (params) {
if(!params.id){
return "group id";
}
},resolve, reject);
}
};
TY.EventManager={
EVENTS:{
SOS:'sos',
SUBSCRIBE_GPS:'gps',
BEFORE_REQUEST:'beforeRequest',
HEART:"heart",
MEDIA:'mediaStatus',
UPDATE_CERT: 'updateCert',
FILE_PROGRESS: 'downProgress',
FILE_IMPORT: 'importProgress',
SOMEONE_LOGIN: 'someoneLogin',
SOMEONE_TALKING:"talked",//某人在对讲
SOMEONE_STOP_TALKING:"stopTalked",//某人在对讲
START_DISPATCHED:"dispatched",//某人在对讲
STOP_DISPATCHED:"stopDispatched",//某人在对讲
SOCKET_INIT:"socketInit",//socket初始化
DISCONNECT:"disconnect",//某人在对讲
OFFLINE:"offline",
ONLINE:"online",
},
callbacks:{},
addEventListener(event,callback){
this.callbacks[event] = callback;
},
removeEventListener(event){
delete this.callbacks[event];
},
},
TY.DeviceManager = (function () {
var validation = function (options,fn1,fn2) {
if (!options || typeof options != 'object') {
return 1;
} else if (fn1 && typeof fn1 != 'function') {
return 2;
} else if (fn2 && typeof fn2 != 'function') {
return 3;
}
return 0;
};
var composite_post = function (options,url,fn1,fn2,fn){
if(validation(options,fn1,fn2)){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL));
}
var msg;
if(fn && (msg = fn(options))){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
}
TY.reqPost(options, url, fn1, fn2);
};
var composite_get = function (options,url,fn1,fn2,fn){
if(validation(options,fn1,fn2)){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL));
}
var msg;
if(fn && (msg = fn(options))){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
}
TY.reqGet(options, url, fn1, fn2);
};
function send(id,comm,fn){
var socket = self._socket;
socket.emit("config", {
personnel: [id],
command: comm
}, fn);
}
var composite_socket = function (options,data,fn1,fn2,fn){
if(validation(options,fn1,fn2)){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL));
return command;
}
var msg;
if(fn && (msg = fn(options))){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
return command;
}
send(options.id,data,function(r){
if(r == false){
fn2&&fn2(TY.RcCode.returnMsg({code:204,msg:"设备不在线"}));
}else if(r == 1){
fn1&&fn1(TY.RcCode.returnMsg({code:200,msg:"操作成功"}));
}else if(r == 2){
fn2&&fn2(TY.RcCode.returnMsg(TY.RcCode.RECORD_LESS));
}else if(r == 0){
fn1&&fn1(TY.RcCode.returnMsg({code:500,msg:"操作失败"}));
}else if(r == -1){
fn1&&fn1(TY.RcCode.returnMsg({code:204,msg:"警号已存在"}));
}else if(typeof (r)=='object'){
var result = TY.RcCode.returnMsg({code:200,msg:"操作成功"});
result.data = r;
fn1&&fn1(result);
}else{
fn1&&fn1({code:200,data:r});
}
});
};
var command = {
callback:null,
addListEvent(event){//添加通知事件事件
command.callback=event;
},
/**
* 获取设备信息 返回参数
* @typedef getDeviceInfo -获取设备信息
* @property {String} DevNo -设备编号
* @property {String} name - 名称
* @property {int} type - 设备类型
* @property {String} desc -备注
* @property {int} imei -唯一imei
* @property {String} MAC -网卡地址
* @example
* {
DevNo:'dv_2312', //设备编号
name :'小明', //名称
type :1, //设备类型
desc :'前台摄像头', //备注
imei :589523658521662, //唯一imei
MAC :'7c-E9-f5-56-48-95' //网卡地址
}
*/
/**
* 获取设备信息 输入参数
* @function getDeviceInfo -获取设备信息
* @param {int} id -必选 设备id
* @example
* {id:1, 设备id };
* @param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @param {obj.<getDeviceInfo>} ... -返回参数
*/
getDeviceInfo(options,fn1,fn2){
TY.compositePost(TY.URL.DeviceManager.deviceInfo,options,function (params) {
if(!params.id){
return "device id";
}
},fn1, fn2);
},
/**
* options 修改设备
* @typedef options_modifyDevice
* @property {int} id -设备id
* @property {String} name -设备信息
* @property {int} type -设备类型
* @property {String} model -形容
* @property {String} adress -设备地址
* @property {String} desc -备注
* @example
* {
id:1, //设备id
name:'小明', //设备名称
type:1, //设备类型
model:'T5', //形容
adress:'xxx', //设备地址
desc:'备注' //备注
}
*/
/**
* @callback Function__obj
* @param {array.<code>} status -返回码
* @param {string} msg -返回信息
* @param {object} ... 其他(不一定有)
*/
/**
* 修改设备 输入参数
* @function modifyDevice -修改设备
* @param {Obj.<options_modifyDevice>} options
* @param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @return {Obj.<code>} status - 返回参数 返回码
* {
"status": 200
}
*/
modifyDevice(options,fn1,fn2){
TY.compositePost(TY.URL.DeviceManager.modifyDevice,options,function (params) {
if(!params.id){
return "device id";
}
if(options.type==0&&(!options.imei || options.imei.length != 15)){
return 'imei not found';
}
},fn1, fn2);
},
/**
* options 设备注册
* @typedef options_addDevice
* @property {int} imei -唯一imei
* @property {String} password -密码
* @property {String} desc -备注
* @property {String} adress -地址
* @property {int} type -设备类型
* @property {String} name -设备名称
* @property {String} model -设备描述
* @example
* {
imei:589658446554, //唯一imei
password:'123', //密码
desc:'备注', //备注
address:'23132', //地址
type:1, //设备类型
name:'设备1', //设备名称
model:'T5' //设备描述
}
*/
/**
* 设备注册 输入参数
* @function addDevice -设备注册 输入参数
* @param {Obj.<options_addDevice>} options
* @param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @return {Obj.<code>} status - 返回参数 返回码
* {
"status": 200
}
*/
addDevice:function(options,fn1,fn2){//设备注册
composite_post(options,TY.URL.DeviceManager.addDevice,fn1,fn2,function(options){
if(options.type==0&&(!options.imei || options.imei.length != 15)){
return 'imei not found';
}
if(!options.password){
return 'password not found';
}
return 0;
});
return command;
},
/**
* 设备删除 输入参数
* @function deleteDevice -设备删除
* @param {int} options -id设备id
* @example
* {
id:1, //设备id
}
* @param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @return {Obj.<code>} status - 返回参数 返回码
* {
"status": 200
}
*/
deleteDevice:function(options,fn1,fn2){//设备删除
composite_post(options,TY.URL.DeviceManager.deleteDevice,fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
return 0;
});
return command;
},
/**
* options 条件搜索设备列表
* line,puid,name,enalbe,sortable它们是and的关系,如果不传表示不加
* @typedef options_getDevicesByParams
* @property {int} line -在线状态
* @property {String} puid -设备编号
* @property {String} name -设备名称
* @property {int} enable -是否启用
* @property {int} sortable -0平台/1设备/2本地
* @property {int} pageSize -页面大小
* @property {int} pageNumber -当前页
* @property {String} deviceName -名称
* @example
* {
line:1, //在线状态
puid:'pu_2312', //设备编号
name:'小明', //设备名称
enable:1, //是否启用
sortable:1, //0 平台/ 1 设备/ 2 本地
pageSize:10, //页面大小
pageNumber:1, //当前页
deviceName:'设备2' //名称
}
*/
/**
* 返回的参数
* @typedef getDevicesByParams
* @property {int} total -数据总条数
* @property {object} rows -数据对象
* @property {int} rows.pid -设备id
* @property {int} rows.id -用户id
* @property {String} rows.name -用户名称
* @property {String} rows.userSn -用户编号
* @property {int} rows.type -设备类型
* @property {String} rows.puid -设备编号
* @property {String} rows.status -设备状态 0离线,1在线,2禁止
* @property {String} rows.deviceName -设备名称
* @property {String} rows.SN -设备产品序号
* @property {String} rows.MAC -网卡地址
* @property {String} rows.language -语言
* @property {String} rows.line 在线状态
* @property {String} rows.groupName -当前组名称
* @property {String} rows.departmentId -部门id
* @property {String} rows.departmentName -部门名称
* @property {String} rows.password -设备密码
* @property {String} rows.longitude -当前Ing
* @property {String} rows.latitude -当前lat
* @example
* {
rows:[
{
pid:1, //设备id
id:1, //用户主键id
name:'小明', //用户名称
userSn:'oopac', //用户编号
type:1, //设备类型
puid:'pu_75682', //设备编号
status:1, //设备状态 0 离线,1 在线,2 禁止
deviceName:'设备1', //设备名称
SN:'adlliw', //设备产品序号
MAC:'7c-E9-f5-56-48-95', //网卡地址
language:'cn', //语言
line:1, //在线状态
groupName:'对讲1', //当前组名称
departmentId:1, //部门id
departmentName:'主部门', //部门名称
password:'111111', //设备密码
longitude:153.202301, //当前lng
latitude:23.165247 //当前lat
}
],
total:230
}
*/
/**
* 条件搜索设备列表 输入参数
* @function getDevicesByParams -条件搜索设备列表 输入参数
* @param {Obj.<options_getDevicesByParams>} options -id设备id
* @param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @param {Obj.<getDevicesByParams>} -返回参数
*/
getDevicesByParams:function(options,fn1,fn2){//条件搜索设备列表 options=>line,puid,name,pageSize,pageNumber,enable,sortable
composite_post(options,TY.URL.DeviceManager.getDevicesByParams,fn1,fn2);
return command;
},
/**
* 获取设备扫码换服务器IP地址 返回值
* @typedef getScanUrl 返回值
* @property {int.<code>} status -在线状态
* @property {String} msg -返回信息
* @property {object} ... -其他(不一定有)
*/
/**
* 获取设备扫码换服务器IP地址
* @function getScanUrl - 获取设备扫码换服务器IP地址
* @param options -参数无
* @param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @param {Obj.<getScanUrl>} -返回参数
*/
getScanUrl:function(options,fn1,fn2){//获取设备扫码换服务器IP地址
if(typeof options == 'function' && !fn2){ fn2 = fn1;fn1 = options;options = {}}
composite_get(options,TY.URL.DeviceManager.getScanUrl,fn1,fn2);
return command;
},
/**
* 获取绑定设备的二维码地址 返回参数
* @typedef getBindedScanUrl 返回值
* @property {String} url -二维码地址
* @example
* {
url:'https://baidu:3005' //二维码地址
}
*/
/**
* 获取绑定设备的二维码地址 输入参数
* @function getBindedScanUrl - 获取绑定设备的二维码地址
* @param {int} id -设备id
* @example
* {
id:1 //设备id
}
* @param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @param {Obj.<getBindedScanUrl>} ... -返回参数
*/
getBindedScanUrl:function(options,fn1,fn2){//获取绑定设备的二维码地址
composite_post(options,TY.URL.DeviceManager.getBindedScanUrl,fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
return 0;
});
return command;
},
/**
* 未绑定设备列表
* @typedef options_loadBindUser
* @property {int} id -设备id
* @property {String} PolNo -人员编号
* @property {String} DevNo -设备编号
* @property {int} line -在线状态
* @property {int} bound -绑定状态
* @property {String} name -人员名称
* @property {int} pageSize -行数
* @property {int} pageNumber -页数
* @property {String} sortable -排序(参数列值)
* @example
* {
id:1, //设备id
PolNo:'pu_2312', //人员编号
DevNo:'au_2312', //设备编号
line:1, //在线状态
bound:1, //绑定状态
name:'小明', //人员名称
pageSize:10, //行数
pageNumber:1, //页数
sortable:'name' //按照名称排序
}
*/
/**
* 未绑定设备列表 返回值
* @typedef loadBindUser 返回值
* @property {int} id -人员id
* @property {String} polNo -人员编号
* @property {String} devNo -设备编号
* @property {String} phone -电话
* @property {String} email -邮箱
* @property {int} departmentId -部门id
* @property {String} department -部门名称
* @property {int} admin -是否管理
* @property {int} line -在线状态
* @property {String} name -人员名称
* @property {String} sn -产品序列
* @property {int} roleld -角色id
* @property {String} roleName -角色名称
* @example
* [
{
id:1, //人员id
devNo:'pu_1654656', //设备编号
polNo:'ac1cq12', //人员编号
phone:15612354892, //电话
email:'192.365@163.com', //邮箱
departmentId:1, //部门id
department:'主部门', //部门名称
admin:1, //是否管理
line:1, //在线状态
name:'小明', //人员名称
sn:'121213328345655', //人员编号
roleId:1, //角色id
roleName:'调度员' //角色名称
},
...
]
*/
/**
* 未绑定设备列表 输入参数
* @function loadBindUser - 未绑定设备列表
* @param {Obj.<options_loadBindUser>} options -输入参数
* @param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @param {Obj.<loadBindUser>} ... -返回参数
*/
loadBindUser:function(options,fn1,fn2){//未绑定设备列表 =>name: "",pageNumber: 1,pageSize: 10,puid: ""
composite_post(options,TY.URL.DeviceManager.loadBindUser,fn1,fn2);
return command;
},
/**
* 未绑定设备列表 返回值
* @typedef getUnBindedDevice 返回值
* @property {String} name -名称
* @property {int} id -用户id
* @property {String} puid -设备编号
* @example
* {
name:'pu_设备1', //名称
id:1,
puid:'pu_2312'
}
*/
/**
* 未绑定设备列表 输入参数
* @function getUnBindedDevice - 未绑定设备列表
* @param {String} name -设备名称
* @param {String} puid -设备编号
* @param {int} pageSize -行数
* @param {int} pageNumber 页数
* @param {String} sortable -排序(参数返回的列名)
* @param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @example
* {
name:'pu_设备1', //设备名称
puid:'pu_2312', //设备编号
pageSize:1, //行数
pageNumber:1, //页数
sortable:'name' //排序
}
* @param {Obj.<getUnBindedDevice>} ... -返回参数
*/
getUnBindedDevice:function(options,fn1,fn2){//未绑定设备列表 =>name: "",pageNumber: 1,pageSize: 10,puid: ""
composite_post(options,TY.URL.DeviceManager.getUnBindedDevice,fn1,fn2);
return command;
},
/**
* 未分配的设备列表 返回参数
* @typedef getUnAllocatedDevice
* @property {String} name -人员名称
* @property {int} id -用户id
* @example
* {
name:'小明', //人员名称
id:1, //设备id
}
*/
/**
* 未分配的设备列表 输入参数
* @function getUnAllocatedDevice - 未分配的设备列表
* @param {int} id -设备id
* @example
* {
id:1 //设备id
}
*@param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @param {Obj.<getUnAllocatedDevice>} ... -返回参数
*/
getUnAllocatedDevice:function(options,fn1,fn2){//未分配的设备列表 => id: 411
composite_post(options,TY.URL.DeviceManager.getUnAllocatedDevice,fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
return 0;
});
return command;
},
/**
* 已分配的设备列表 返回参数
* @typedef getAllocatedDevice 返回参数
* @property {String} name -人员名称
* @property {int} id -人员id
* @example
* {
name:'小明', //人员名称
id:1, //设备id
}
*/
/**
* 已分配的设备列表 输入参数
* @function getAllocatedDevice - 已分配的设备列表
* @param {int} options -设备id
* @example
* {
id:1 //设备id
}
* @param {Function__obj} resolve -成功的回调
* @param {Function_RcCode} reject -失败的回调
* @param {Obj.<getAllocatedDevice>} ... -返回参数
*/
getAllocatedDevice:function(options,fn1,fn2){//已分配的设备列表 => id: 411
composite_post(options,TY.URL.DeviceManager.getAllocatedDevice,fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
return 0;
});
return command;
},
/**
* 绑定设备 返回参数
* @typedef bindDevice 返回参数
* @property {int.<code>} status -返回码
*/
/**
* 绑定设备
* @typedef options_bindDevice
* @property {int} deviceld -待解绑的用户id
* @property {int} userid -待绑定的用户id
* @example
* {
deviceId:1, //待解绑的用户id
userId:1 //待绑定的用户id
}
*/
/**
* 绑定设备 输入参数
* @function bindDevice - 绑定设备
* @param {obj.<options_bindDevice>} options -设备id
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<bindDevice>} ... -返回参数
*/
bindDevice:function(options,fn1,fn2){//绑定设备 => deviceId: 568,userId: 569
composite_post(options,TY.URL.DeviceManager.bindDevice,fn1,fn2,function(options){
if(!options.deviceId){
return 'deviceId not found';
}
if(!options.userId){
return 'userId not found';
}
return 0;
});
return command;
},
/**
* @typedef Function_RcCode
* @property {} SOCKET_INIT_SUCCESS -{code:0,msg:"初始化成功"}, //socket初始化成功
* @property {} RC_CODE_S_OK -{code:200,msg:"请求成功"}, //请求成功
* @property {} ACTION_BY_BIND -{code:202,msg:"存在绑定关系,操作失败"},
* @property {} GROUP_NAME_SINGLE -{code:203,msg:"名称不能重复"},
* @property {} USER_EXIST -{code:204,msg:"已存在,不能重复"}, //请求成功
* @property {} FENCE_BY_BIND -{code:205,msg:"围栏绑定了设备,请解绑后删除"},
* @property {} WORK_SHFIT_BY_BIND -{code:206,msg:"班次绑定了设备,请解绑后删除"},
* @property {} ROLE_BY_BIND -{code:207,msg:"该角色已被用户绑定,请解绑后删除"},
* @property {} GROUP_BY_BIND -{code:208,msg:"群组绑定了设备,请解绑后删除"},
* @property {} DEPT_BY_BIND -{code:209,msg:"该部门下有人员,请删除后再次操作"},
* @property {} USER_CODE_EXIST -{code:210,msg:"人员编号冲突"},
* @property {} DEV_CODE_EXIST -{code:211,msg:"该设备识别号已注册"},
* @property {} APP_VERSION_EXIST -{code:212,msg:"app下已经发布了版本,请删除后再次操作"},
* @property {} EMPTY_TOKEN -{code:300,msg:"token为空,可能未登录"}, //请求头没有token
* @property {} SPECIAL_PARAMS -{code:213,msg:"参数不能包括特殊字符"}, //请求头没有token
* @property {} INVALID_TOKEN -{code:401,msg:"token无效"}, //请求头没有token
* @property {} DEV_OUTLINE -{code:406,msg:"设备不在线"},
* @property {} DISPATCHER_DISPATCHER -{code:407,msg:"创建调度的人在调度"},
* @property {} EMPTY_SERVER -{code:460,msg:"服务器地址为空"}, //请求头没有token
* @property {} EMPTY_PROPS -{code:461,msg:"参数不能为空"}, //请求头没有token
* @property {} PARAMETERS_ABNORMAL -{code:462,msg:"参数异常"},//参数异常
* @property {} LOGINED -{code:463,msg:"已经登录过"}, //请求头没有token
* @property {} DEVICE_FILE_DELETE_LIMIT -{code:463,msg:"不能删除设备文件"}, //请求头没有token
* @property {} MICROPHONE_LIMIT -{code:465,msg:"麦克风不可用"}, //请求头没有token
* @property {} RTC_ABNOMAL -{code:466,msg:"RTC异常"}, //请求头没有token
* @property {} OTHER_TALK -{code:467,msg:"已有人在说话"}, //请求头没有tokenNOT_SUBSCRIBE_DEVICE
* @property {} NOT_STREAM -{code:468,msg:"该设备不在监控房间"}, //请求头没有token
* @property {} NOT_SUBSCRIBE_DEVICE -{code:469,msg:"该设备不是被监控状态"}, //请求头没有token
* @property {} DEVICE_MONITER_EXIST -{code:470,msg:"已经邀请该设备进入监控"}, //请求头没有token
* @property {} RECORD_LESS -{code:471,msg:"结束失败,录制时间太短"}, //请求头没有token
* @property {} SYSTEM_ERROR -{code:500,msg:"系统错误"},
* @property {} LOGIN_FAIL -{code:410,msg:"账号密码错误"},//登录失败==》账号密码错误
* @property {} LOGIN_USER_FAIL -{code:411,msg:"用户不存在"},//登录失败==》用户不存在
* @property {} LIMIT_AUTHORITY -{code:403,msg:"没有权限"}, //权限不够,
* @property {} OTHER_ABNORMAL -{code:999,msg:"其他异常"}//参数
*/
/**
* 成功的回调函数 返回的参数
* @callback Function_obj
* @param {array.<code>} status -返回码
* @param {String} msg -返回信息
* @param {Obj.<groups>} groups -返回列表集合说
*/
/**
* 绑定设备 返回参数
* @typedef unBindDevice 返回参数
* @property {int.<code>} status -返回码
* @example
* {
"status": 200
}
*/
/**
* 解绑设备 输入参数
* @function unBindDevice - 解绑设备
* @param {int} options -待解绑的用户id
* @example
* {
userId:1 //待绑定的用户id
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<unBindDevice>} ... -返回参数
*/
unBindDevice:function(options,fn1,fn2){//解绑设备
composite_post(options,TY.URL.DeviceManager.unBindDevice,fn1,fn2,function(options){
if(!options.userId){
return 'userId not found';
}
return 0;
});
return command;
},
/**
* 设备分配 返回参数
* @typedef allocateDevice 返回参数
* @property {int.<code>} status -返回码
* @example
* {
"status": 200
}
*/
/**
* 设备分配 输入参数
* @function allocateDevice - 设备分配
* @param {int} id -待移除的用户id
* @param {Array} devicelds -移除的设备集合
* @example
* {
userId:1, //待移除的用户id
deviceIds:[1,2,3,4,5] //移除的设备集合
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<allocateDevice>} ... 返回参数
*/
allocateDevice:function(options,fn1,fn2){//设备分配
composite_post(options,TY.URL.DeviceManager.allocateDevice,fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
if(!options.deviceIds){
return 'deviceIds not found';
}
return 0;
});
return command;
},
AllocateDevice:function(options,fn1,fn2){//添加分配设备
composite_post(options,TY.URL.DeviceManager.AllocateDevice,fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
if(!options.deviceIds){
return 'deviceIds not found';
}
return 0;
});
return command;
},
unAllocateDevice:function(options,fn1,fn2){//移除分配设备
composite_post(options,TY.URL.DeviceManager.unAllocateDevice,fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
if(!options.targetId){
return 'targetId not found';
}
return 0;
});
return command;
},
/**
* 设备停用 返回参数
* @typedef disableDevice 返回参数
* @property {int.<code>} status -返回码
* @example
* {
"status": 200
}
*/
/**
* 设备停用 输入参数
* @function disableDevice - 设备停用
* @param {int} id -待禁用的设备id
* @example
* {
id:1 //待禁用的设备id
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<allocateDevice>} ... 返回参数
*/
disableDevice:function(options,fn1,fn2){//设备停用
composite_post(options,TY.URL.DeviceManager.disableDevice,fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
options.status = 2;
return 0;
});
return command;
},
/**
* 设备启用 返回参数
* @typedef enableDevice 返回参数
* @property {int.<code>} status -返回码
* @example
* {
"status": 200
}
*/
/**
* 设备启用 输入参数
* @function enableDevice - 设备启用
* @param {int} id -待启用的设备id
* @example
* {
id:1 //待启用的设备id
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<enableDevice>} ... 返回参数
*/
enableDevice:function(options,fn1,fn2){//设备启用
composite_post(options,TY.URL.DeviceManager.disableDevice,fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
options.status = 1;
return 0;
});
return command;
},
/**
* 设备调拨 返回参数
* @typedef transferDevice 返回参数
* @property {int.<code>} status -返回码
* @example
* {
"status": 200
}
*/
/**
* 设备调拨 输入参数
* @function transferDevice - 设备调拨
* @param {int} sourceld -待转移的设备id
* @param {int} targetld -待接受的用户id
* @param {int[]} devicelds -设备id集合
* @example
* {
sourceId:1, //待转移的用户id
targetId:1, //待接受的用户id
deviceIds:[1,2,3] //设备集合
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<transferDevice>} ... 返回参数
*/
transferDevice:function(options,fn1,fn2){//设备调拔
composite_post(options,TY.URL.DeviceManager.transferDevice,fn1,fn2,function(options){
if(!options.sourceId){
return 'sourceId not found';
}
if(!options.targetId){
return 'targetId not found';
}
if(!options.deviceIds){
return 'deviceIds not found';
}
if(!Array.isArray(options.deviceIds)){
return 'deviceIds not array';
}
return 0;
});
return command;
},
/**
* 轨迹 返回参数
* @typedef loadTrackGps 返回参数
* @property {object[]} user --用户轨迹集合
* @property {object[]} trajectorys -轨迹文件集合
* @example
* {
users:'[
[
{
uid:1, //用户id
lat:23.562688 //纬度
lng:127.1513513 //经度
time:'2019-05-52 23:50:59' //时间
},
{
uid:1, //用户id
lat:23.562688 //纬度
lng:127.1513513 //经度
time:'2019-05-52 23:51:20' //时间
}
],
[
{...},
{...}
]
],
trajectorys:[
{
id:1, //文件id
name:'2312', //人员名称
begintime:'2018-05-06 20:60:30', //开始时间
endtime:'2018-05-07 20:60:30', //结束时间
url:'http://xxx/xxx/a.aac', //地址
duration:200.3, //视音频文件
}
],
}
*/
/**
* 轨迹 输入参数
* @function loadTrackGps - 轨迹
* @param {int[]} personnel -用户id的集合
* @param {String} begin -待接受的用户id
* @param {String} end -设备id集合
* @param {int} distance -地图距离比例
* @example
* {
personnel:[1,2,3,5], //人员id集合
begin:'2015-06-15 06:50:20', //待转移的用户id
end:'2016-05-23 20:50:30', //待接受的用户id
distance:500 //设备集合
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<loadTrackGps>} ... 返回参数
*/
loadTrackGps:function(options,fn1,fn2) { //轨迹
composite_post(options, TY.URL.loadTrackGps, fn1, fn2, function (options) {
if (!options.personnel) {
return 'personnel not found';
}
if (!options.begin) {
return 'begin not found';
}
if (!options.end) {
return 'end not found';
}
});
return command;
},
/**
* 获取设备音视频状态 返回参数
* @typedef getMediaInfo 返回参数
* @property {int} id -人员id
* @property {int} recordAudio -录音状态
* @property {int} recordVideo -录像状态
* @example
* {
id:1, //人员id
recordAudio:1, //录音状态
recordVideo:0 //录像状态
}
*/
/**
* 获取设备音视频状态 输入参数
* @function getMediaInfo - 获取设备音视频状态
* @param {int} id -用户id
* @example
* {
id:212
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<getMediaInfo>} ... 返回参数
*/
getMediaInfo:function(options,fn1,fn2){//获取设备音视频状态
composite_socket(options,{
cmd:9
},fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
});
return command;
},
/**
* 修改设备密码 返回参数
* @typedef modifyDevicePwd 返回参数
* @property {int.<code>} status -返回码
* @example
* {
"status": 200
}
*/
/**
* 修改设备密码 输入参数
* @function modifyDevicePwd - 修改设备密码
* @param {int} id -用户id
* @param {String} password -密码
* @example
* {
id:212, //用户id
password:'2312' //密码
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<modifyDevicePwd>} ... 返回参数
*/
modifyDevicePwd:function(options,fn1,fn2){//修改设备密码
options.id=[options.id];
composite_socket(options,{
cmd:18,
setting:options
},fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
if(!options.password){
return 'password not found';
}
});
return command;
},
remoteControl:function(options,fn1,fn2){ //向设备发送远程命令
composite_socket(options,{
cmd:7,
setting:options
},fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
});
return command;
},
/**
* 设置设备本地配置 返回参数
* @typedef setLocal 返回参数
* @property {int.<code>} status -返回码
* @example
* {
"status": 200
}
*/
/**
* 设置设备本地配置
* @typedef options_setLocal -设置设备本地配置
* @property {int} id -人员id
* @property {int} autoInfrared -红外线自动
* @property {String} login -登录的ip和端口号
* @property {bool} autoLogin -自动登录
* @property {int} bluetooth 蓝牙开启
* @property {String} capture -自动抓拍名称
* @property {int} fileName -录像文件名称(0默认文件, 1设备识别号)
* @property {int} language -语言(0中文,1英文)
* @property {int} wifi -wifi开启
* @property {int} autoRecord -自动录像
* @property {int} loopSave -循环存储
* @property {int} screen -屏幕常亮
* @property {int} volume -音量大小
* @property {int} baseGps -基站定位
* @property {int} gps 开启定位
* @property {int} lte -4g开启
* @property {int} launch 开机自启动开启
* @property {int} onlineTime -设备在线时间,单位小时
* @property {int} time -0不同步 ,1同步
* @example
* {
id:1 //人员id
autoInfrared: 480, //红外线自动
login: "", //登录的ip和端口号
autoLogin: true, //自动登录
bluetooth: 0, //自动抓拍名称
capture: "", //自动抓拍名称
fileName: 0, //录像文件名称(0默认文件,1设备识别号)
language: 0, //语言(0中文,1英文)
wifi: 1, //wifi开启
loopSave: 0, //循环存储
screen: 1, //屏幕常亮
volume: 100, //音量大小
autoRecord: 0, //自动录像
baseGps: 1, //基站定位
gps: 1, //定位开启
lte: 1, //4g开启
launch: 1, //开机自启动开启
onlineTime:10, //设备在线时间,单位小时
time:0 //0不同步,1同步
}
*/
/**
* 设置设备本地配置 输入参数
* @function setLocal - 设置设备本地配置
* @param {obj.<options_setLocal>} options -参数
* @param {Function_obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<setLocal>} ... 返回参数
*/
setLocal:function(options,fn1,fn2){ //设置设备本地配置
composite_socket(options,{
cmd:0,
setting:options.local
},fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
});
return command;
},
/**
* 获取设备本地配置 返回参数
* @typedef getLocal -获取设备本地配置
* @property {int} autoInfrared -红外线自动
* @property {String} login -登录的ip和端口号
* @property {bool} autoLogin -自动登录
* @property {int} bluetooth 蓝牙开启
* @property {String} capture -自动抓拍名称
* @property {int} fileName -录像文件名称(0默认文件, 1设备识别号)
* @property {int} language -语言(0中文,1英文)
* @property {int} wifi -wifi开启
* @property {int} autoRecord -自动录像
* @property {int} loopSave -循环存储
* @property {int} screen -屏幕常亮
* @property {int} volume -音量大小
* @property {int} baseGps -基站定位
* @property {int} gps 开启定位
* @property {int} lte -4g开启
* @property {int} launch 开机自启动开启
* @property {int} onlineTime -设备在线时间,单位小时
* @property {int} time -0不同步 ,1同步
* @example
* {
autoInfrared: 480, //红外线自动
login: "", //登录的ip和端口号
autoLogin: true, //自动登录
bluetooth: 0, //自动抓拍名称
capture: "", //自动抓拍名称
fileName: 0, //录像文件名称(0默认文件,1设备识别号)
language: 0, //语言(0中文,1英文)
wifi: 1, //wifi开启
loopSave: 0, //循环存储
screen: 1, //屏幕常亮
volume: 100, //音量大小
autoRecord: 0, //自动录像
baseGps: 1, //基站定位
gps: 1, //定位开启
lte: 1, //4g开启
launch: 1, //开机自启动开启
onlineTime:10, //设备在线时间,单位小时
time:0 //0不同步,1同步
}
*/
/**
* 获取设备本地配置 输入参数
* @function getLocal - 获取设备本地配置
* @param {int} id -人员id
* @example
* {
id:1 //人员id
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<getLocal>} ... 返回参数
*/
getLocal:function(options,fn1,fn2){ //获取设备本地配置
composite_socket(options,{
cmd:15
},fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
});
return command;
},
/**
* 设置设备媒体配置 返回参数
* @typedef setMedia 返回参数
* @property {int.<code>} status -返回码
* @example
* {
"status": 200
}
*/
/**
* 设置设备媒体配置
* @typedef options_setMedia -设置设备媒体配置
* @property {int} id -人员id
* @property {int} resolution -分辨率(480为720x480,720为1280x720,1080为1920x1080)
* @property {bool} time -是否显示时间(true显示,false不显示)
* @property {int} text -文本信息(空串表示不显示文本信息)
* @property {int} positionType -位置显示类型(0位置,1地址)
* @property {int} position -叠加位置(0左上,1左下,2右上,3右下,4四周)
* @property {int} prerecordDelay -预录时间长度(单位秒)
* @property {int} delayRecordDelay -延录时间长度(单位秒)
* @property {int} recordDelay -录像时间长度(单位分钟)
* @example
* {
id:1, //人员id
time: false, //是否显示时间(true显示,false不显示)
text: "", //文本信息(空串表示不显示文本信息)
resolution: 480, //分辨率(480为720x480,720为1280x720,1080为1920x1080)
positionType: 1, //位置显示类型(0位置,1地址)
position: 1, //叠加位置(0左上,1左下,2右上,3右下,4四周)
prerecordDelay: 16, //预录时间长度(单位秒)
delayRecordDelay: 16,//延录时间长度(单位秒)
recordDelay: 16 //录像时间长度(单位分钟)
}
*/
/**
* 设置设备媒体配置 输入参数
* @function setMedia - 设置设备媒体配置
* @param {obj.<options_setMedia>} options -参数
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<setMedia>} ... 返回参数
*/
setMedia:function(options,fn1,fn2){ //设置设备媒体配置
composite_socket(options,{
cmd:2,
setting:options.videoAudio
},fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
});
return command;
},
/**
* 获取设备媒体配置 返回参数
* @typedef getMedia 获取设备媒体配置 返回参数
* @property {int} resolution -分辨率(480为720x480,720为1280x720,1080为1920x1080)
* @property {bool} time -是否显示时间(true显示,false不显示)
* @property {int} text -文本信息(空串表示不显示文本信息)
* @property {int} positionType -位置显示类型(0位置,1地址)
* @property {int} position -叠加位置(0左上,1左下,2右上,3右下,4四周)
* @property {int} prerecordDelay -预录时间长度(单位秒)
* @property {int} delayRecordDelay -延录时间长度(单位秒)
* @property {int} recordDelay -录像时间长度(单位分钟)
*/
/**
* 获取设备媒体配置 输入参数
* @function getMedia - 获取设备媒体配置
* @param {int} id -人员id
* @example
* {
id:1 //人员id
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<getMedia>} ... 返回参数
*/
getMedia:function(options,fn1,fn2){ //获取设备媒体配置
composite_socket(options,{
cmd:17
},fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
});
return command;
},
/**
* 设置设备拍照配置 返回参数
* @typedef setPicture 返回参数
* @property {int.<code>} status -返回码
* @example
* {
"status": 200
}
*/
/**
* 设置设备拍照配置 输入参数
* @function setPicture - 设置设备拍照配置
* @param {int} id -人员id
* @param {int} resolution -分辨率(13为48002700,18为56323168,23为64003600,34为78084392)
* @param {int} shots -连拍张数
* @example
* {
id:1, //人员id
resolution: 13, //分辨率(13为4800*2700,18为5632*3168,23为6400*3600,34为7808*4392)
shots: 16 //连拍张数
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<setPicture>} ... 返回参数
*/
setPicture:function(options,fn1,fn2){ //设置设备拍照配置
composite_socket(options,{
cmd:1,
setting:options.picture
},fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
});
return command;
},
/**
* 获取设备拍照配置 返回参数
* @typedef getPicture 返回参数
* @property {int} resolution -分辨率(13为48002700,18为56323168,23为64003600,34为78084392)
* @property {int} shots -连拍张数
* @example
* {
resolution: 13, //分辨率(13为4800*2700,18为5632*3168,23为6400*3600,34为7808*4392)
shots: 16 //连拍张数
}
*/
/**
* 获取设备拍照配置 输入参数
* @function getPicture - 获取设备拍照配置
* @param {int} id -人员id
* @example
* {
id:1 //人员id
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<getPicture>} ... 返回参数
*/
getPicture:function(options,fn1,fn2){ //获取设备拍照配置
composite_socket(options,{
cmd:16
},fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
});
return command;
},
/**
* 获取设备剩余空间 返回参数
* @typedef getSpace 返回参数
* @property {int} space -内存大小(单位m)
* @example
* {
space:100 //内存大小(单位m)
}
*/
/**
* 获取设备剩余空间 输入参数
* @function getSpace - 获取设备剩余空间
* @param {int} id -人员id
* @example
* {
id:1 //人员id
}
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
* @param {Obj.<getSpace>} ... 返回参数
*/
getSpace:function(options,fn1,fn2){ //获取设备剩余空间
composite_socket(options,{
cmd:8
},fn1,fn2,function(options){
if(!options.id){
return 'id not found';
}
});
return command;
},
/**
* 获取部门和设备成员 输入参数
* @function loadDepartentDevice - 获取部门和设备成员
* @param {Object} options -参数
* @param {Function__obj} resolve -成功的回调
* @param {obj.<Function_RcCode>} reject -失败的回调
*/
loadDepartentDevice: function (options, fn1, fn2) {//获取部门和设备成员
TY.compositePost(TY.URL.loadDepartentDevice, options, null, fn1, fn2);
return command;
}
};
return command;
})();
TY.SystemSetManager = (function () {
var command = {
/**
* 获取系统参数
* logo路径需要添加服务端地址前缀
* 输入参数 params传空{}
* @function querySystemConfig 获取系统参数
* @param {Obj} params -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
*/
querySystemConfig: function (options, fn1, fn2) {//查询系统参数设置
TY.reqPost(options, TY.URL.querySystemConfig, (result) => {
//转化数据
let fmtResult = {
status: result.status,
data: {
subsystemLogo: result.data.logo,
loginLogo: result.data.logoTop,
loginPoster: result.data.logoLeft,
systemNameLogo: result.data.logoRightCenter,
copyright: result.data.copyright,
title: result.data.title,
scheduleNameZh: result.data.zhName_schedule,
scheduleNameEn: result.data.enName_schedule,
managerNameZh: result.data.zhName_manager,
managerNameEn: result.data.enName_manager,
platformFileIsShare: result.data.fileIsShare,
shareFileSavePath: result.data.fileShareAfterIsRemove,
deletePlatformFileForSharing: result.data.fileShareSavePath,
}
};
if (fn1) fn1(fmtResult);
}, fn2);
return command;
},
/**
* @typedef params_setSystemConfig
* @property {file} logo -不是必需 子系统LOGO
* @property {file} logoTop -不是必需 登录页LOGO
* @property {file} logoLeft -不是必需 登录页海报
* @property {file} logoRightCenter -不是必需 登录页平台名称
* @property {String} copyright -不是必需 版权信息
* @property {String} zhName_schedule -不是必需 指控子系统中文标题
* @property {String} enName_schedule -不是必需 指控子系统英文标题
* @property {String} zhName_manager -不是必需 管理子系统中文标题
* @property {String} enName_manager -不是必需 管理子系统英文标题
* @property {String} fileIsShare -不是必需 文件共享 0:否 1:是
* @property {String} fileShareAfterIsRemove -不是必需 文件共享后立即删除 0:否 1:是
* @property {String} fileShareSavePath -不是必需 共享文件存储位置
*/
/**
* 设置系统参数
* 修改参数会先清除全部参数再重新添加,所以需要保持不变的参数也需要提交
* 如果不修改已经上传的file, 传现有文件路径即可
* @function setSystemConfig 设置系统参数
* @param {Obj.<params_setSystemConfig>} params -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
*/
setSystemConfig: function (options, fn1, fn2, fn3) {//设置系统参数
//转化数据
let fmtOptions = {
logo: options.subsystemLogo,
logoTop: options.loginLogo,
logoLeft: options.loginPoster,
title: options.title,
logoRightCenter: options.systemNameLogo,
copyright: options.copyright,
zhName_schedule: options.scheduleNameZh,
enName_schedule: options.scheduleNameEn,
zhName_manager: options.managerNameZh,
enName_manager: options.managerNameEn,
fileIsShare: options.platformFileIsShare,
fileShareAfterIsRemove: options.shareFileSavePath,
fileShareSavePath: options.deletePlatformFileForSharing,
};
TY.compositeUpload(TY.URL.setSystemConfig, fmtOptions, null, fn1, fn2, fn3);
return command;
},
/**
* @typedef params_uploadUpPackage
* @property {file} file -上传压缩包文件流(当前只支持zip格式压缩包) file(文件流)
*/
/**
* 上传升级安装包
* @function uploadUpPackage 上传升级安装包
* @param {Obj.<params_uploadUpPackage>} params 参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
*/
uploadUpPackage: function (options, fn1, fn2, fn3) {//上传平台升级包,进行升级
TY.compositeUpload(TY.URL.uploadUpPackage, options, null, fn1, fn2, fn3);
return command;
}
};
return command;
})();
TY.FenceManager = (function () {
var command = {
/**
* 查询围栏列表 参数
* @typedef params_getFencesByParams
* @property {int} type -围栏类型
* @property {String} name -围栏名称
* @property {String} dept_name -部门名称
* @property {int} pageNumber -当前页
* @property {int} pageSize -行数
* @property {int} sortable -排序的列值
* @example
* {
type:1, //围栏类型
name:'午饭集合', //围栏名称
dept_name:'研发部', //部门名称
pageNumber:1, //当前页
pageSize:10, //行数
sortable:'name' //排序的列值
}
*/
/**
* rows返回值
* @typedef getFencesByParams 返回值
* @property {array.<code>} type -返回码
* @property {String} msg -返回信息
* @property {int} total -总数
* @property {object[]} rows -数据集合
* @example
* {
rows:[
{
id:1, //主键id
coordinates:
[
{
"lat": 22.56636466490955,
"lng": 113.94664075447638
},
...
], //坐标集合
name:'麦克', //围栏名称
type:1, //围栏类型
create_time:'2015-05-05 20:00:10', //创建时间
update_time:'2015-06-05 20:00:10', //修改时间
dept_name:'主部门' //部门
}
],
total:1,
}
*/
/**
* 查询围栏列表
* @function getFencesByParams 查询围栏列表
* @param {Obj.<params_getFencesByParams>} params 参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {Obj.<getFencesByParams>} ... row返回值
*/
getFencesByParams: function (options, fn1, fn2) {//查询围栏列表
TY.reqPost(options, TY.URL.getFencesByParams, fn1, fn2);
return command;
},
getFenceRecordsByParams: function (options, fn1, fn2) {//查询围栏列表
TY.reqPost(options, TY.URL.getFenceRecordsByParams, fn1, fn2);
return command;
},
/**
* object返回值
* @typedef getFenceInfo 围栏详情 返回值
* @property {array.<code>} type -返回码
* @property {String} msg -返回信息
* @property {object} ... -其他
* @example
* {
id:1, //主键id
coordinates:
[
{
"lat": 22.56636466490955,
"lng": 113.94664075447638
},
...
], //坐标集合
name:'集合', //围栏名称
type:1, //围栏类型
opid:2312, //发起人id
}
*/
/**
* 围栏详情
* @function getFenceInfo 围栏详情
* @param {int} id -围栏id
* @example
* {
id:1, //围栏id
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {Obj.<getFenceInfo>} ... object返回值
*/
getFenceInfo: function (options, fn1, fn2) {//围栏详情
TY.compositePost(TY.URL.getFenceInfo, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 状态返回
* @typedef State_return 状态返回
* @property {array.<code>} int -返回码
* @property {String} msg -返回信息
*/
/**
* 设置围栏 参数
* @typedef param_setFence 设置围栏 返回值
* @property {String} coordinates -围栏详情
* @property {String} name -围栏名称
* @property {int} type -围栏类型
* @example
* {
coordinates:
[
{
"lat": 22.56636466490955,
"lng": 113.94664075447638
},
...
], //坐标集合
name:'集合', //围栏名称
type:1 //围栏类型 1圆形 2多边形
}
*/
/**
* 设置围栏
* @function setFence 设置围栏
* @param {obj.<param_setFence>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {Obj.<State_return>} ... 状态返回
*/
setFence: function (options, fn1, fn2) {//设置围栏
TY.compositePost(TY.URL.setFence, options, (options) => {
if(!options.name){
return 'name';
}
if(!options.type){
return 'type';
}
if(!options.coordinates){
return 'coordinates';
}
}, fn1, fn2);
return command;
},
/**
* 删除围栏
* @function deleteFence 删除围栏
* @param {int} id -围栏id
* @example
* {
id:1, //围栏id
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {Obj.<State_return>} ... 状态返回
*/
deleteFence: function (options, fn1, fn2) {//删除围栏
TY.compositePost(TY.URL.deleteFence, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 设别绑定到围栏
* @function deviceBindFence 设别绑定到围栏
* @param {int} fid -围栏id
* @param {int[]} uids -人员id集合
* @example
* {
fid:1, //围栏id
uids:[1,2,3] //人员id集合
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {Obj.<State_return>} ... 状态返回
*/
deviceBindFence: function (options, fn1, fn2) {//设备绑定到围栏
TY.compositePost(TY.URL.deviceBindFence, options, (options) => {
if(!options.fid){
return 'fid';
}
}, fn1, fn2);
return command;
},
/**
* 未绑定围栏的设备列表 返回值
* @typedef datas_getUnBindFences 返回值
* @property {array.<code>} status -返回码
* @property {String} msg -返回信息
* @property {object[]} datas -其他
* @example
* {
datas:[
{
uid:1, //人员id
name:'小明', //人员名称
user_sn:'sn_23121', //人员编号
isBound:1, //是否绑定 0:未绑定 1:已绑定 2:被他人绑定
puinfo_id:1, //设备id
}
]
}
*/
/**
* 未绑定围栏的设备列表
* @function getUnBindFences 未绑定围栏的设备列表
* @param {int} fid -围栏id
* @example
* {
fid:1, //围栏id
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {Obj.<datas_getUnBindFences>} ... 状态返回
*/
getUnBindFences: function (options, fn1, fn2) {//未绑定围栏的设备列表
TY.reqPost(options, TY.URL.getUnBindFences, fn1, fn2);
return command;
},
/**
* 已绑定围栏的设备列表 返回值
* @typedef datas_getBindFences 返回值
* @property {array.<code>} status -返回码
* @property {String} msg -返回信息
* @property {object[]} datas -其他
* @example
* {
status: 200,
datas:
[
{
uid:1, //用户id
name:'小明' //用户名称
},
{
...
}
]
}
*/
/**
* 已绑定围栏的设备列表
* @function getBindFences 已绑定围栏的设备列表
* @param {int} fid -围栏id
* @example
* {
fid:1, //围栏id
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {Obj.<datas_getBindFences>} ... 状态返回
*/
getBindFences: function (options, fn1, fn2) {//已绑定围栏的设备列表
TY.compositePost(TY.URL.getBindFences, options, (options) => {
if(!options.fid){
return 'fid';
}
}, fn1, fn2);
return command;
}
};
return command;
})();
TY.SignManager = (function () {
var command = {
/**
* rows参数说明
* @typedef rows rows参数说明
* @property {int} bid -班次id
* @property {String} name -班次名称
* @property {string} time -创建时间
* @property {string} dept_name -所属部门名称
* @example
* {
"total": 3,
"rows": [{
"bid": 14,
"name": "东方半岛",
“dept_name”: "所属部门名称",
"time": "2018-09-03 01:25:27",
}, {
"bid": 15,
"name": "景芬路口",
"time": "2018-08-29 05:01:38",
}, {
"bid": 17,
"name": "俄文",
"time": "2018-09-03 01:28:06",
}],
status:200
}
*/
/**
* 返回参数
* @typedef getWorkShiftsByParams 返回参数
* @property {Array.<code>} status -返回状态码
* @property {String} msg -返回信息
* @property {int} total -数据总条数
* @property {obj.<rows>} ... 其他
*/
/**
* RcCodeMsg 返回状态
* @typedef Function_RcCodeMsg
* @property {Array.<code>} status -返回状态码
* @property {String} msg -返回信息
*/
/**
* 根据参数查询签到班次列表
* @typedef params_getWorkShiftsByParams 请求参数
* @property {String} name -不是必选 班次名称,支持模糊查询
* @property {String} dept_name -不是必选 部门名称,支持模糊查询
* @property {int} pageNumber -页码
* @property {int} pageSize -每页数据数量
* @property {object} sortable -排序对象
* @example
* {
name:1, //班次名称
dept_name:'一级部门', //部门名称
pageNumber:1, //页码
pageSize:10, //每页数据数量
sortable:{
prop: 'name',//排序字段
order: 'desc'//排序方式desc或者asc
},
}
*/
/**
* 根据参数查询签到班次列表 检索参数 返回值:签到班次列表
* @function getWorkShiftsByParams 根据参数查询签到班次列表
* @param {obj.<params_getWorkShiftsByParams>} params -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getWorkShiftsByParams>} ... 返回参数
*/
getWorkShiftsByParams: function (options, fn1, fn2) {//签到班次列表
TY.reqPost(options, TY.URL.getWorkShiftsByParams, fn1, fn2);
return command;
},
/**
* 签到班次详情 bid 返回值:班次详情
* @function workShiftInfo 签到班次
* @param {int} bid -必选 班次id
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @example 返回示例
* {
name: 谷歌, //string 班次名称
bid: 18, //int 班次id
signtimesList:[{ //object 班次需要签到的时间段列表
"beginTime": -25200000, //int 开始时间
"endTime": -21600000, //int 结束时间
"week": 1, //int 星期
"name": "早", //string 时间段名称
"stid": "1", //int 时间段标识(早班,午班等标识)
"spid": "23", //int 关联签到点id
"place": "东方半岛" //string 关联签到点名称
}, {
"beginTime": -25200000,
"endTime": -21600000,
"week": 2,
"name": "早",
"stid": "1",
"spid": "23",
"place": "东方半岛"
}, {
"beginTime": -25200000,
"endTime": -21600000,
"week": 3,
"name": "早",
"stid": "1",
"spid": "23",
"place": "东方半岛"
},
...
]
}
*/
workShiftInfo: function (options, fn1, fn2) {//签到班次详情
TY.compositePost(TY.URL.workShiftInfo, options, (options) => {
if(!options.bid){
return 'bid';
}
}, fn1, fn2);
return command;
},
/**
* 添加修改班次 班次参数
* @function setWorkShift 添加,修改班次
* 不传bid为创建 , 传bid为修改
* @param {obj | {}} options -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @example 请求示例 不传bid为创建 , 传bid为修改
* {
name: 谷歌, //string 班次名称
bid: 18, //int 班次id
signtimesList:[{ //object 班次需要签到的时间段列表
"beginTime": -25200000, //int 开始时间
"endTime": -21600000, //int 结束时间
"week": 1, //int 星期
"name": "早", //string 时间段名称
"stid": "1", //int 时间段标识(早班,午班等标识)
"spid": "23", //int 关联签到点id
"place": "东方半岛" //string 关联签到点名称
}, {
"beginTime": -25200000,
"endTime": -21600000,
"week": 2,
"name": "早",
"stid": "1",
"spid": "23",
"place": "东方半岛"
},
...
]
}
*@param {int.<code>} ... -status返回码
*/
setWorkShift: function (options, fn1, fn2) {//设置签到班次
if(options.signtimesList && typeof options.signtimesList == 'string'){
options.signtimesList = JSON.parse(options.signtimesList);
}
TY.compositePost(TY.URL.setWorkShift, options, (options) => {
if(!options.name){
return 'name';
}
}, fn1, fn2);
return command;
},
/**
* 删除签到班次 请求参数
* @typedef params_deviceBindWorkShift
* @property {Array} uids -必选 绑定的设备用户id列表
* @property {int} bid -必选 班次id
*/
/**
* 删除签到班次
* @function deleteWorkShift 删除签到班次
* @param {int} id -必选 班次id
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<code>} ... -status返回码
* @example 返回示例 { status:200 //状态码 }
*/
deleteWorkShift: function (options, fn1, fn2) {//删除签到班次
TY.compositePost(TY.URL.deleteWorkShift, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 设备绑定到签到班次 请求参数
* @typedef params_deviceBindWorkShift
* @property {Array} uids -必选 绑定的设备用户id列表
* @property {int} bid -必选 班次id
* @example 请求参数示例
* {
uids: [610,16],
bid: 14
}
*/
/**
* 设备绑定到签到班次
* @function deviceBindWorkShift 设备绑定到签到班次
* @param {obj.<params_deviceBindWorkShift>} options -必选
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<code>} ... -status返回码
* @example 返回示例 { status:200 //状态码 }
*/
deviceBindWorkShift: function (options, fn1, fn2) {//设备绑定到签到班次
TY.compositePost(TY.URL.deviceBindWorkShift, options, (options) => {
if(!options.bid){
return 'bid';
}
}, fn1, fn2);
return command;
},
/**
* 查询未分配签到班次的设备列表 bid 返回值:用户列表
* @function unBindWorkShiftDevices 查询未分配签到班次的设备列表
* @param {int} bid -必选 班次id
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<code>} ... -status返回码
* @example 返回参数示例
* {
"status": 200, //int 返回码
"datas": [ //array 设备用户列表
{
"uid": 404, //int 用户id
"name": "Wilson233", //string 用户名称
"isBound": 2 //int 0:未被分配 1:已分配 2:被其他班次分配
}
]
}
*/
unBindWorkShiftDevices: function (options, fn1, fn2) {//未绑定签到班次的设备列表
TY.reqPost(options, TY.URL.unBindWorkShiftDevices, fn1, fn2);
return command;
},
/**
* 查询分配签到班次的设备列表 bid 返回值:用户列表
* @function bindWorkShiftDevices 查询分配签到班次的设备列表
* @param {int} bid -必选 班次id
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<code>} ... -status返回码
* @example 返回参数示例
* {
"status": 200, //int 返回码
"datas": [ //array 设备用户列表
{
"uid": 404, //int 用户id
"name": "Wilson233", //string 用户名称
}
]
}
*/
bindWorkShiftDevices: function (options, fn1, fn2) {//绑定签到班次的设备列表
TY.compositePost(TY.URL.bindWorkShiftDevices, options, null, fn1, fn2);
return command;
},
/**
* 设备绑定到签到班次 请求参数
* @typedef params_getSignPlacesByParams 参数
* @property {integer} pageNUmber -必选 页码1
* @property {integer} pageSize -必选 一条页数10
* @property {string} name -不是必选 签到点名称,支持模糊查询
* @property {string} dept_name -不是必选 部门名称,支持模糊查询
* @property {object} sortable -不是必选 排序对象 sortable
* @example 排序对象
* {
prop: 'xx', //排序字段
order:'desc', //排序方式 desc 或者 asc
}
*/
/**
* 查询签到点列表 检索参数 返回值:签到点列表
* @function getSignPlacesByParams 查询签到点列表
* @param {obj.<params_getSignPlacesByParams>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<code>} ... -status返回码
* @example 返回参数示例
*{
"rows": [ //array 分页数据集合
{
"spid": 4, //number 签到点id
"lat": 12, //number 经度
"lng": 23, //number 纬度
"scope": 34, //number 签到范围
"name": "55", //number 签到点名称
"create_time": "1538039462726", //number 签到点创建时间
"update_time": "1538046507447", //number 签到点修改时间
"dept_name": "主部门"
}
],
"total": 3, //number 分页总数量
"status": 200 //number 返回码
}
*/
getSignPlacesByParams: function (options, fn1, fn2) {//签到点列表
TY.reqPost(options, TY.URL.getSignPlacesByParams, fn1, fn2);
return command;
},
/**
* 设备绑定到签到班次 请求参数
* @typedef params_allSignPlaces 参数
* @property {string} name -不是必选 签到点名称,模糊查询
* @property {string} bid -必选
*/
/**
* 查询全部签到点列表 返回值:签到点列表
* @function allSignPlaces 查询全部签到点列表
* @param {obj.<params_allSignPlaces>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<code>} ... -status返回码
* @example 返回参数示例
*{
"rows": [{ //array 数据集合
"spid": 4, //number 签到点id
"lat": 12, //number 经度
"lng": 23, //number 纬度
"scope": 34, //number 签到范围
"name": "55", //number 签到点名称
"create_time": "1538039462726", //number 签到点创建时间
"update_time": "1538046507447" //number 签到点修改时间
}],
"status": 200 //number 返回码
}
*/
allSignPlaces: function (options, fn1, fn2) {//全部签到点列表
TY.reqPost(options, TY.URL.allSignPlaces, fn1, fn2);
return command;
},
/**
* 查询签到点详情 spid 返回值:签到点详情
* @function signPlaceInfo 查询签到点详情
* @param {string} spid -必需 签到点id
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<code>} ... -status返回码
* @example 返回参数示例
*{
"spid": 1, //number 签到点id
"name": "康和盛大厦1", //string 签到点名称
"lat": 22.565752936830386, //number 经度
"lng": 113.94782850347625, //number 纬度
"scope": 200, //number 签到范围
"status": 200 //number 返回码
}
*/
signPlaceInfo: function (options, fn1, fn2) {//签到点详情
TY.compositePost(TY.URL.signPlaceInfo, options, (options) => {
if(!options.spid){
return 'spid';
}
}, fn1, fn2);
return command;
},
/**
* 设置签到点 请求参数
* @typedef params_setSignPlace
* @property {string} lat -必选 经度 示例:113.15454645
* @property {string} Ing -必选 纬度 示例:23.155115616
* @property {integer} scope -必选 签到范围 示例:200
* @property {string} name -必选 签到点名称 示例:狼山路中
* @property {integer} spid -不是必须 签到点id,为空则添加 , 有id则修改
*/
/**
* 设置签到点 签到点参数 返回值:返回码
* @function setSignPlace 添加,修改签到点
* @param {obj.<params_setSignPlace>} options -必需
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<code>} ... -status返回码
*/
setSignPlace: function (options, fn1, fn2) {//设置签到点
TY.compositePost(TY.URL.setSignPlace, options, (options) => {
if(!options.name){
return 'name';
}
if(!options.lat || !options.lng){
return 'position';
}
}, fn1, fn2);
return command;
},
/**
* 删除签到点 id 返回值:返回码
* @function deleteSignPlace 删除签到点
* @param {integer} id -必需 签到点id
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<code>} ... -status返回码
*/
deleteSignPlace: function (options, fn1, fn2) {//删除签到点
TY.compositePost(TY.URL.deleteSignPlace, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* @typedef getSignsByParams_rows
* @property {array} rows -分页数据集合
* @property {number} rows.sid -考勤id
* @property {number} rows.uid 设备ID
* @property {number} rows.stid -签到时间段标识
* @property {number} rows.date -打卡的日期时间戳,具体变现为那一天的0点
* @property {number} rows.opid -所属部门id
* @property {string} rows.time -打卡时间(如果没有打卡,则为空)
* @property {string} rows.uname -设备名称
* @property {number} rows.mark -是否打卡(0未打卡,1正常)
* @property {string} rows.banciName -班次名称
* @property {string} rows.timesName -时间段名称
* @property {string} rows.period -正常打卡时间范围(start--end)
* @property {number} total -分页总数量
* @property {array.<code>} status -返回码
* @example 返回示例
* {
"rows": [ //array 分页数据集合
{
"sid": 2, //number 考勤id
"uid": 1, //number 设备id
"stid": 1, //number 签到时间段标识
"date": 1538064000000, //number 打卡的日期时间戳,具体变现为那一天的0点
"time": null, //number 所属部门id
"uname": "小明", //string 打卡时间 (如果没有打开,则为空)
"mark": "0", //number 是否打卡(0未打卡,1正常)
"banciName": "谷歌", //string 班次名称
"timesName": "早", //string 时间段名称
"period": "-25200000—-21600000",
"dept_name": "主部门"
}
],
"total": 2, //number 分页总数量
"status": 200 //number 返回码
}
*/
/**
* 查询考勤记录列表 参数
* @typedef param_getSignsByParams
* @property {integer} pageNumber -必需 分页页码 eg:1
* @property {integer} pageSize -必需 分页条数 eg:10
* @property {integer} quick -不是必需 1为快速搜索 , 0为实时搜索
* @property {string} name -不是必需 设备关联的人员名称
* @property {string} mark -打卡状态(0未打卡,1正常)
* @property {string} beginDate -不是必需 开始时间 eg:2018-08-23 19:16:50
* @property {string} endDate -不是必需 结束时间 eg:2018-08-23 19:16:50
*/
/**
* 查询考勤记录列表 检索参数 返回值:考勤记录列表
* @function getSignsByParams 查询考勤记录列表
* @param {obj.<param_getSignsByParams>} options -必需
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<getSignsByParams_rows>} ... -返回参数
*
*/
getSignsByParams: function (options, fn1, fn2) {//考勤记录
if(options.beginDate && typeof options.beginDate == 'string'){
options.beginDate = new Date(options.beginDate).getTime();
}
if(options.endDate && typeof options.endDate == 'string'){
options.endDate = new Date(options.endDate).getTime();
}
TY.reqPost(options, TY.URL.getSignsByParams, fn1, fn2);
return command;
},
/**
* 导出考勤记录 参数
* @typedef param_exportSigns
* @property {string} name -不是必需 设备关联的人员名称
* @property {string} mark -不是必需打卡状态(0未打卡,1正常)
* @property {string} beginDate -不是必需 开始时间 eg:2018-08-23 19:16:50
* @property {string} endDate -不是必需 结束时间 eg:2018-08-23 19:16:50
* @property {object} sortable -不是必选 排序对象 sortable
* @example 排序对象
* {
prop: 'xx', //排序字段
order:'desc', //排序方式 desc 或者 asc
}
*/
/**
* 导出考勤记录 检索参数 返回值:文件流
* @function exportSigns 导出考勤记录
* @param {obj.<param_exportSigns>} options -必需
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {int.<code>} ... -status返回码
*/
exportSigns: function (options, fn1) {//导出考勤记录
TY.reqDown(options, TY.URL.exportSigns, fn1);
return command;
}
};
return command;
})();
TY.PermissionManager = (function () {
var command = {
/**
* 添加&修改权限 返回值 操作状态
* @typedef modifyRights
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
*/
/**
* 添加&修改权限 参数
* @typedef param_modifyRights
* @property {int} rights_type -权限类型
* @property {string} rights_url -地址路径
* @property {string} rights_desc -备注
* @property {string} menu_title -标题
* @property {string} parent_rights -上级权限id
* @property {int} rights_id -主键id(修改时使用)
* @example
* {
rights_type:1,
rights_url:'/a/c',
rights_desc:'添加',
menu_title:'人员添加',
parent_rights:1,
rights_id:2,
}
*/
/**
*添加&修改权限
* @function modifyRights 添加&修改权限
* @param {obj.<param_modifyRights>} options -必需
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<modifyRights>} ... -操作状态
*/
modifyRights: function (options, fn1, fn2) {//添加&修改权限
TY.compositePost(TY.URL.modifyRights, options, (options) => {
if(!options.menu_title){
return 'menu_title';
}
if(!options.rights_url){
return 'rights_url';
}
if(!options.rights_type){
return 'rights_type';
}
}, fn1, fn2);
return command;
},
/**
* 获得权限详情 权限信息
* @typedef getRightsInfo
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @example
* {
rights_id:2, //主键id
rights_type:'1', //权限类型
rights_url:'/a/b', //权限地址
rights_desc:'查询列表', //权限备注
menu_title:'查询', //权限标题
parent_rights:1, //上级主键
parent_title:'检索模块' //上级标题
}
*/
/**
*获得权限详情
* @function getRightsInfo 获得权限详情
* @param {int} id -主键id
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getRightsInfo>} ... -权限信息
*/
getRightsInfo: function (options, fn1, fn2) {//获取权限详情
TY.compositePost(TY.URL.getRightsInfo, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 删除权限 返回值 操作状态
* @typedef deleteRights
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
*/
/**
*删除权限
* @function deleteRights 删除权限
* @param {int} ids -主键id集合
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<deleteRights>} ... -操作状态
* @example
* {
ids:[1,2,3] //主键id集合
}
*/
deleteRights: function (options, fn1, fn2) {//删除权限
TY.compositePost(TY.URL.deleteRights, options, (options) => {
if(!options.ids){
return 'ids';
}
if(options.ids && typeof options.ids == 'string'){
options.ids = JSON.parse(options.ids);
}
}, fn1, fn2);
return command;
},
/**
* 获取所有权限列表 返回值 权限列表
* @typedef getRightsList
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @property {object[]} data -权限集合
* @example {
data:[ //权限集合
{
id:2, //主键id
title:'按钮', //权限标题
pid:1, //父级id
rights_type:1, //权限类型
url:'/a/c', //权限地址
have:20 //角色关联id
}
]
}
*/
/**
*获取所有权限列表
* @function getRightsList 获取所有权限列表
* @param {int} id -主键id
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getRightsList>} ... -权限列表
* @example
* {
id:1 //主键id
}
*/
getRightsList: function (options, fn1, fn2) {//获取所有权限列表
options = options || {}, options.id = 0;
TY.compositePost(TY.URL.getSubRights, options, null, fn1, fn2);
return command;
},
/**
* 获得下级权限列表 返回值 操权限列表
* @typedef getChildRightsList
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @example
* {
title:'', //权限标题
rights_type:1, //权限类型
url:'/a/b', //权限地址
id:1, //主键id
pid:2, //上级id
sort:1, //上级id,无显示自己
child_num:5, //下级权限的数目
desc:'' //备注
}
*/
/**
* 获得下级权限列表 参数
* @typedef param_getChildRightsList
* @property {int} id -主键id
* @property {string} name -权限名称(模糊匹配)
* @property {int} type -权限类型
* @example
* {
id:3,
name:'删除',
type:1
}
*/
/**
*获得下级权限列表
* @function getChildRightsList 获得下级权限列表
* @param {obj.<param_getChildRightsList>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getChildRightsList>} ... -权限列表
*/
getChildRightsList: function (options, fn1, fn2) {//获取下级权限列表
TY.compositePost(TY.URL.getChildRightsList, options, null, fn1, fn2);
return command;
},
/**
* 获得所有下级权限
* @typedef getSubRights
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @property {object[]} data -权限集合
* @example
* {
data:[ //权限集合
{
id:2, //主键id
title:'按钮', //权限标题
pid:1, //父级id
rights_type:1, //权限类型
url:'/a/c', //权限地址
have:20 //角色关联id
}
]
}
*/
/**
*获得所有下级权限
* @function getSubRights 获得所有下级权限
* @param {int} id -主键id
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getSubRights>} ... -权限列表
*/
getSubRights: function (options, fn1, fn2) {//获取所有下级权限
TY.compositePost(TY.URL.getSubRights, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 添加或修改角色,更改绑定的权限 返回值 操作状态
* @typedef modifyRole
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
*/
/**
* 添加或修改角色,更改绑定的权限
* @typedef param_modifyRole
* @property {string} role_name -角色名称
* @property {string} role_desc -备注
* @property {string} role_id -角色id(修改时使用)
* @example
* {
role_name:'添加', //角色名称
role_desc:'人员', //备注
role_id:1 //角色id (修改时使用)
}
*/
/**
*添加或修改角色,更改绑定的权限
* @function modifyRole 添加&修改角色,更改绑定的权限
* @param {obj.<param_modifyRole>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<modifyRole>} ... -权限列表
*/
modifyRole: function (options, fn1, fn2) {//添加&修改角色,更改绑定的权限
TY.compositePost(TY.URL.modifyRole, options, (options) => {
if(!options.role_name){
return 'role_name';
}
if(options.role_rights && typeof options.role_rights == 'string'){
options.role_rights = JSON.parse(options.role_rights);
}
}, fn1, fn2);
return command;
},
/**
* 获取角色详情
* @typedef getRoleInfo
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @example
* {
data:{
role:[
{
role_id:1, //角色主键
role_name:'管理', //角色名称
role_desc:'备注', //备注
def:0
}
],
roleRights:[
{
id:2, //关联表主键
title:'', //权限标题
pid:3, //父级外键
rights_type:1, //权限类型
url:'/a/b', //地址
have:2 //上级id
}
]
}
}
*/
/**
*获取角色详情
* @function getRoleInfo 获取角色详情
* @param {int} id -角色id
* @example
* {
id:1 //角色id
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getRoleInfo>} ... -权限列表
*/
getRoleInfo: function (options, fn1, fn2) {//获取角色详情
TY.compositePost(TY.URL.getRoleInfo, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 删除角色,清除权限绑定关系
* @typedef deleteRole
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
*/
/**
*删除角色,清除权限绑定关系
* @function deleteRole 删除角色,清除权限绑定关系
* @param {int[]} ids -id集合
* @example
* {
ids:[1,2,3] //id集合
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<deleteRole>} ... -权限列表
*/
deleteRole: function (options, fn1, fn2) {//删除角色,清除权限绑定关系
TY.compositePost(TY.URL.deleteRole, options, (options) => {
if(!options.ids){
return 'ids';
}
if(options.ids && typeof options.ids == 'string'){
options.ids = JSON.parse(options.ids);
}
}, fn1, fn2);
return command;
},
/**
* 获取角色列表
* @typedef getRoleList
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @example
* {
role_id:1, //主键id
role_name:'管理员', //角色名称
role_desc:'备注', //备注
}
*/
/**
*获取角色列表
* @function getRoleList 获取角色列表
* @param {} option -参数无
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getRoleList>} ... -权限列表
*/
getRoleList: function (options, fn1, fn2) {//获取角色列表
TY.reqPost(options, TY.URL.getRoleList, fn1, fn2);
return command;
}
};
return command;
})();
TY.LogManager = (function () {
var command = {
/**
* 用户登录日志
* @typedef getUserLoginLogsByParams
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @example
* {
puid:'abc', //人员编号
name:'', //人员名称
ip:'192.168.xxx', //登录ip
port:8080, //登录端口
onlineTime:'2018-05-06 20:03:06', //上线时间
offlineTime:'2018-05-06 20:03:06', //下线时间
curTime:2032, //本次时长
totalTime:5622556 //总共时长
}
*/
/**
* 用户登录日志
* @typedef param_getUserLoginLogsByParams
* @property {string} beginDate -开始时间
* @property {string} endDate -结束时间
* @property {string} name -人员名称
* @property {int} pageSize -行数
* @property {int} pageNumber -当前页
* @property {object} sortable -排序(参照返回列)
* @example
* {
{
beginDate:'2018-02-01 20:30:56',//开始时间
endDate:'2018-02-02 20:30:56', //结束时间
name:'', //人员名称
pageSize:10, //行数
pageNumber:1, //页数
sortable:{
prop:'', //排序列(根据返回列)
order:'desc' //排序方式
}
}
}
*/
/**
*用户登录日志
* @function getUserLoginLogsByParams 用户登录日志
* @param {obj.<param_getUserLoginLogsByParams>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getUserLoginLogsByParams>} ... -result返回值
*/
getUserLoginLogsByParams: function (options, fn1, fn2) {//用户登录日志
TY.reqPost(options, TY.URL.getUserLoginLogsByParams, fn1, fn2);
return command;
},
/**
* 设备登录日志
* @typedef getDeviceLoginLogsByParams
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @example
* {
puid:'abc', //设备编号
name:'', //人员名称
ip:'192.168.xxx', //登录ip
port:8080, //登录端口
onlineTime:'2018-05-06 20:03:06', //上线时间
offlineTime:'2018-05-06 20:03:06', //下线时间
curTime:2032, //本次时长
totalTime:5622556 //总共时长
}
*/
/**
* 设备登录日志
* @typedef param_getDeviceLoginLogsByParams
* @property {string} puid 设备编号
* @property {string} beginDate -开始时间
* @property {string} endDate -结束时间
* @property {string} name -人员名称
* @property {int} pageSize -行数
* @property {int} pageNumber -当前页
* @property {object} sortable -排序(参照返回列)
* @example
* {
{
puid:'', //设备编号
beginDate:'2018-02-01 20:30:56',//开始时间
endDate:'2018-02-02 20:30:56', //结束时间
name:'', //人员名称
pageSize:10, //行数
pageNumber:1, //页数
sortable:{
prop:'', //排序列(根据返回列)
order:'desc' //排序方式
}
}
}
*/
/**
*设备登录日志
* @function getDeviceLoginLogsByParams 设备登录日志
* @param {obj.<param_getDeviceLoginLogsByParams>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getDeviceLoginLogsByParams>} ... -result返回值
*/
getDeviceLoginLogsByParams: function (options, fn1, fn2) {//设备登录日志
TY.reqPost(options, TY.URL.getDeviceLoginLogsByParams, fn1, fn2);
return command;
},
/**
* 操作日志
* @typedef getUserOperateLogsByParams
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @example
* {
name:'小明' //人员名称
time:'2018-06-09 20:20:23' //操作时间
operate:'查询' //操作内容
result:1 //操作结果
description:'aa' //备注
}
*/
/**
* 操作日志
* @typedef param_getUserOperateLogsByParams
* @property {string} beginDate -开始时间
* @property {string} endDate -结束时间
* @property {string} name -人员名称
* @property {int} pageSize -行数
* @property {int} pageNumber -当前页
* @property {object} sortable -排序(参照返回列)
* @property {string} operate -操作内容(模糊匹配)
* @example
* {
{
beginDate:'2018-02-01 20:30:56',//开始时间
endDate:'2018-02-02 20:30:56', //结束时间
name:'', //人员名称
pageSize:10, //行数
pageNumber:1, //页数
operate:'拍照', //操作内容(模糊匹配)
sortable:{
prop:'', //排序列(根据返回列)
order:'desc' //排序方式
}
}
}
*/
/**
*操作日志
* @function getUserOperateLogsByParams 操作日志
* @param {obj.<param_getUserOperateLogsByParams>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getUserOperateLogsByParams>} ... -result返回值
*/
getUserOperateLogsByParams: function (options, fn1, fn2) {//操作日志
TY.reqPost(options, TY.URL.getUserOperateLogsByParams, fn1, fn2);
return command;
},
/**
* 设备操作日志文件检索
* @typedef getDeviceOperateLogsByParams
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @example
* {
id:1, //文件id
name:'', //文件名称
startTime:'', //开始时间(视频)
endTime:'', //结束时间(视频)
size:1313254, //文件大小
duration:200, //文件长度
type:1, //文件类型
desc:'aa' //文件备注
storageType:1 //存储类型 (平台/设备/本地)
}
*/
/**
* 设备操作日志文件检索
* @typedef param_getDeviceOperateLogsByParams
* @property {int[]} personnel -人员id集合
* @property {int} storageType -平台类型
* @example
* {
personnel;[
1,2,3
],
storageType:1
}
*/
/**
*设备操作日志文件检索
* @function getDeviceOperateLogsByParams 设备操作日志文件检索
* @param {obj.<param_getDeviceOperateLogsByParams>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<getDeviceOperateLogsByParams>} ... -result返回值
*/
getDeviceOperateLogsByParams: function (options, fn1, fn2) {//设备操作日志
options.type = 3;
TY.compositePost(TY.URL.loadFile, options, (options) => {
if(!options.personnel){
return 'personnel';
}
if(!options.storageType){
return 'storageType';
}
if(options.personnel && typeof options.personnel == 'string'){
options.personnel = JSON.parse(options.personnel);
}
}, fn1, fn2);
return command;
},
/**
* 导入设备日志
* @typedef importDeviceOperateLog
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
*/
/**
* 导入设备日志
* @typedef param_importDeviceOperateLog
* @property {int} id -人员id
* @property {int[]} array -文件id集合
* @property {int} type -文件类型
* @example
* {
id:1, //人员id
array:[1,2,3], //文件id集合
type:1 //文件类型
}
*/
/**
*导入设备日志
* @function importDeviceOperateLog 导入设备日志
* @param {obj.<param_importDeviceOperateLog>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<importDeviceOperateLog>} ... -状态返回
*/
importDeviceOperateLog: function (options, fn1, fn2) {//导入设备日志
TY.compositePost(TY.URL.importFile, options, (options) => {
if(!options.id){
return 'id';
}
if(!options.files){
return 'files';
}
if(options.files && typeof options.files == 'string'){
options.files = JSON.parse(options.files);
}
}, fn1, fn2);
return command;
},
getDeviceLogByParams:function(options, fn1, fn2){
options.type=3;
TY.FileManager.getFilesByParams(options, fn1, fn2);
},
/**
* 下载设备日志
* @typedef downloadDeviceOperateLog
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
*/
/**
* 下载设备日志
* @typedef param_downloadDeviceOperateLog
* @property {int} id -人员id
* @property {int} storageType -存储类型
* @property {int} type -文件类型
* @property {int} did -人员id
* @example
* {
id:1, //文件id
storageType:1, //储存类型
type:2, //文件类型
did:2312 //人员id
}
*/
/**
*下载设备日志
* @function downloadDeviceOperateLog 下载设备日志
* @param {obj.<param_downloadDeviceOperateLog>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<downloadDeviceOperateLog>} ... -状态返回
*/
downloadDeviceOperateLog: function (options, fn1, fn2) {//下载设备日志
options.type = 3;
TY.compositeDown(TY.URL.downloadFile, options, (options) => {
if(!options.did){
return 'did';
}
if(!options.id && options.id != 0){
return 'id';
}
if(!options.storageType && options.storageType != 0){
return 'storageType';
}
}, fn1, fn2);
return command;
},
/**
* 删除设备日志
* @typedef deleteDeviceOperateLog
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
*/
/**
* 删除设备日志
* @typedef param_deleteDeviceOperateLog
* @property {int} id -人员id
* @property {int[]} array -文件id集合
* @example
* {
id:1, //人员id
array:[1,2,3] //文件id集合
}
*/
/**
*删除设备日志
* @function deleteDeviceOperateLog 删除设备日志
* @param {obj.<param_deleteDeviceOperateLog>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function_RcCodeMsg} reject -不是必选 失败的回调
* @param {obj.<deleteDeviceOperateLog>} ... -状态返回
*/
deleteDeviceOperateLog: function (options, fn1, fn2) {//删除设备日志
TY.compositePost(TY.URL.deleteFile, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
}
};
return command;
})();
TY.AI = (function () {
var command = {
/**
* rect[]矩形区域
* @typedef rect -矩形区域
* @property {Number} x -必选 x坐标
* @property {Number} y -必选 y坐标
* @property {Number} h -必选 高度
* @property {Number} w -必选 宽度
* @property {Number} id -必选 200成功,其他失败
* @property {strint} name -必选 识别到的人员名称
*/
/**
* Result 人脸记录
* @typedef Result -人脸记录
* @property {Number} id -必选 人脸记录id
* @property {float} lat -必选 纬度
* @property {float} lng -必选 经度
* @property {String} time -必选 上传时间(格式yyyy-MM-dd hh:mm:ss)
* @property {String} name -必选 上传人员名称
* @property {Number} devNo -必选 设备编号
* @property {Number} polNo -必选 人员便号
* @property {Number} img -必选 图片id
* @property {Array.<rect>} rect -必选 总条数
*/
/**
* @callback Function_obj_result
* @param {Number} status -必选 200成功,其他失败
* @param {Array.<Result>} rows -必选 人脸库信息
* @param {Number} total -必选 总条数
*/
/**
* @typedef params_getResultsByParams
* @property {Number} pageSize -是
* @property {Number} pageNumber -是
* @property {String} begintime -是
* @property {String} endtime -是
* @property {Number} name -是
* @property {Number} devNo -是
* @property {Number} polNo -是
*/
/**
* 查询识别记录列表
* @function getResultsByParams 查询识别记录列表
* @param {Obj.<params_getResultsByParams>} params -必选 没有返回值
* @param {Function_obj_result} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
*/
getResultsByParams: function (options, fn1, fn2) {//识别结果列表
TY.reqPost(options, TY.URL.getResultsByParams, fn1, fn2);
return command;
},
/**
* 批量删除识别记录
* @typedef obj_result
* @property {Array} ids -是
*/
/**
* 批量删除识别记录
* @function deleteResult 批量删除识别记录
* @param {Array.<obj_result>} params -必选 没有返回值
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
*/
deleteResult: function (options, fn1, fn2) {//删除人脸记录
TY.compositePost(TY.URL.deleteResult, options, (options) => {
if(!options.id){
return 'id';
}
options.ids = [parseInt(options.id)];
}, fn1, fn2);
return command;
},
deleteResults: function (options, fn1, fn2) {//批量删除人脸记录
TY.compositePost(TY.URL.deleteResult, options, (options) => {
if(!options.ids){
return 'ids';
}
if(options.ids && typeof options.ids == 'string'){
options.ids = JSON.parse(options.ids);
}
}, fn1, fn2);
return command;
},
/**
* @typedef Face
* @property {Nnmber} id -必选 人脸库id
* @property {String} uname -必选 识别到的名称
* @property {String} time -必选 上传时间(格式 yyyy-MM-dd hh:mm:ss)
* @property {String} name -必选 上传人员名称
* @property {Nnmber} polNo -必选 上传人员编号
* @property {Nnmber} imgs -必选 人脸库图片id集合
*/
/**
* @callback Function_obj_faces
* @param {Number} status -必选 200成功,其他失败
* @param {Obj.<Face>} rows -必选 人脸库信息
* @param {Number} total -必选 总条数
*/
/**
* 查询人脸库列表
* @typedef obj_getFacesByParams
* @property {Nnmber} pageSize -是
* @property {Nnmber} pageNumber -是
* @property {Nnmber} begintime -是
* @property {Nnmber} endtime -是
* @property {Nnmber} name -是
* @property {Nnmber} devNo -是
* @property {Nnmber} polNo -是
*/
/**
* 查询人脸库列表
* @function getFacesByParams 查询人脸库列表
* @param {Obj.<obj_getFacesByParams>} options -必选 没有返回值
* @param {Function_obj_faces} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
*/
getFacesByParams: function (options, fn1, fn2) {//人脸库列表
TY.reqPost(options, TY.URL.getFacesByParams, fn1, fn2);
return command;
},
/**
* 批量删除人脸库
* @typedef obj_face_result
* @property {Array} ids -人脸库id
*/
/**
* 批量删除人脸库
* @function deleteFace -批量删除人脸库
* @param {Obj.<obj_face_result>} options -必选 没有返回值
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
*/
deleteFace: function (options, fn1, fn2) {//删除人脸库
TY.compositePost(TY.URL.deleteFace, options, (options) => {
if(!options.id){
return 'id';
}
options.ids = [parseInt(options.id)];
}, fn1, fn2);
return command;
},
deleteFaces: function (options, fn1, fn2) {//批量删除人脸库
TY.compositePost(TY.URL.deleteFace, options, (options) => {
if(!options.ids){
return 'ids';
}
if(options.ids && typeof options.ids == 'string'){
options.ids = JSON.parse(options.ids);
}
}, fn1, fn2);
return command;
}
};
return command;
})();
TY.GpsManager = (function () {
var validation = function (options,fn1,fn2) {
if (!options || typeof options != 'object') {
return 1;
} else if (fn1 && typeof fn1 != 'function') {
return 2;
} else if (fn2 && typeof fn2 != 'function') {
return 3;
}
return 0;
};
var composite_post = function (options,url,fn1,fn2,fn){
if(validation(options,fn1,fn2)){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL));
}
var msg;
if(fn && (msg = fn(options))){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
}
TY.reqPost(options, url, fn1, fn2);
};
var composite_get = function (options,url,fn1,fn2,fn){
if(validation(options,fn1,fn2)){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL));
}
var msg;
if(fn && (msg = fn(options))){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
}
TY.reqGet(options, url, fn1, fn2);
};
var socket = self._socket;
function send(id,comm,fn){
socket.emit("config", {
personnel:[id],
command:comm
}, fn);
}
var composite_socket = function (options,data,fn1,fn2,fn){
if(validation(options,fn1,fn2)){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL));
return command;
}
var msg;
if(fn && (msg = fn(options))){
fn2(TY.RcCode.returnMsg(TY.RcCode.PARAMETERS_ABNORMAL,msg));
return command;
}
send(options.id,data,function(r){
if(r == false){
fn2&&fn2(TY.RcCode.returnMsg({code:204,msg:"设备不在线"}));
}else if(r == 1){
fn1&&fn1(TY.RcCode.returnMsg({code:200,msg:"操作成功"}));
}else if(r == 0){
fn1&&fn1(TY.RcCode.returnMsg({code:500,msg:"操作失败"}));
}else if(r == -1){
fn1&&fn1(TY.RcCode.returnMsg({code:204,msg:"警号已存在"}));
}
fn1&&fn1(TY.RcCode.returnMsg({code:204,data:r}));
});
};
var command = {
/**
* 返回值
* @typedef status
* @property {Array.<code>} status -返回码
* @property {String} msg -返回的信息
*/
/**
* 订阅设备的gps
* @function subscribeGpsByDevice -订阅设备的gps
* @param {int[]} options -personnel 人员id集合
* @example {
personnel:[1,2,3,4,5]
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<status>} ... -状态返回
*/
subscribeGpsByDevice:function(options, fn1, fn2){//=>personnel []
composite_post(options,TY.URL.subscribeGpsByDevice,fn1,fn2,function(options){
if(!options.personnel){
return 'personnel not found';
}
return 0;
});
return command;
},
/**
* 订阅一个组的gps
* @function subscribeGpsByGroup -订阅一个组的gps
* @param {int} room -组id
* @example {
room:2312
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<status>} ... -状态返回
*/
subscribeGpsByGroup:function(options, fn1, fn2){//=>personnel []
options.room = options.id;
composite_post(options,TY.URL.subscribeGpsByGroup,fn1,fn2,function(options){
if(!options.room){
return 'room not found';
}
return 0;
});
return command;
},
/**
* @function unsubscribeAll 取消所有的订阅
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
*/
unsubscribeAll:function(fn1, fn2){
var options={};
composite_post(options,TY.URL.unsubscribeGps,fn1,fn2);
return command;
},
/**
* 取消单个或所有的订阅
* @function unsubscribeGps -取消单个或所有的订阅
* @param {Array} personnel -人员id集合
* @example {
personnel:[1,2,3,4,5]
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
*/
unsubscribeGps:function(options, fn1, fn2){
if(typeof options == 'function'){
fn2 = fn1;
fn1 = options;
options = {};
}
composite_post(options,TY.URL.unsubscribeGps,fn1,fn2);
return command;
},
};
return command;
})();
TY.UnitManager = (function () {
var command = {
/**
* addUnit 添加部门 参数
* @typedef param_addUnit 添加部门
* @property {int} parent_id -父级部门id
* @property {String} name -部门名称
* @property {String} department_code -部门编号
* @example
* {
parent_id:'2', //父级部门id
name:'研发部门', //部门名称
department_code:'abc' //部门编号
}
*/
/**
* 添加部门
* @function addUnit -添加部门
* @param {obj.<param_addUnit>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<status>} ... -状态返回
*/
addUnit: function (options, fn1, fn2) {//添加部门
TY.compositePost(TY.URL.addUnit, options, (options) => {
if(options.parent_id==null){
return 'parent_id';
}
if(!options.name){
return 'name';
}
}, fn1, fn2);
return command;
},
/**
* 删除部门
* @function deleteUnit -删除部门
* @param {int} id -部门id
* @example
* {
id:1 //部门id
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<status>} ... -状态返回
*/
deleteUnit: function (options, fn1, fn2) {//删除部门
TY.compositePost(TY.URL.deleteUnit, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 修改部门 参数
* @typedef param_modifyUnit 修改部门
* @property {int} id -部门id
* @property {int} parent_id -父级部门id
* @property {String} name -部门编名称
* @property {String} department_code -部门编号
* @example
* {
id:1, //部门id
parent_id:'2', //父级部门id
name:'研发部门', //部门名称
department_code:'abc' //部门编号
}
*/
/**
* 修改部门
* @function modifyUnit -修改部门
* @param {obj.<param_modifyUnit>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<status>} ... -状态返回
*/
modifyUnit: function (options, fn1, fn2) {//修改部门
TY.compositePost(TY.URL.modifyUnit, options, (options) => {
if(!options.id){
return 'id';
}
if(options.parent_id==null){
return 'parent_id';
}
if(!options.name){
return 'name';
}
}, fn1, fn2);
return command;
},
/**
* 部门列表
* @typedef param_getUnitsByParams 部门列表
* @property {int.<code>} status -返回码
* @property {String} name -返回信息
* @property {object[]} department -部门集合
* @example
* {
department:[
{
id:1, //主键id
name:'主部门', //部门名称
type:1, //部门类型
pId:0, //父级id
code:'abc' //部门编号
}
]
}
*/
/**
* 部门列表 返回值:部门列表
* @function getUnitsByParams -部门列表
* @param {} ... -参数无
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<param_getUnitsByParams>} ... -部门列表
*/
getUnitsByParams: function (options, fn1, fn2) {//部门列表
TY.compositePost(TY.URL.getUnitsByParams, options, null, fn1, fn2);
return command;
},
/**
* 人员列表
* @typedef getUserByUnit 人员列表
* @property {int.<code>} status -返回码
* @property {String} msg -返回信息
* @property {int} total -数据总数量
* @property {object[]} row -部门成员集合
* @property {int} rows.id -人员id
* @property {string} rows.account -人员账号
* @property {string} rows.name -人员名称
* @property {string} rows.phone -phone
* @property {int} rows.roleId -角色id
* @property {string} rows.email -邮箱
* @property {string} rows.departmentName -所属部门吗v
* @property {int} rows.departmentId -所属部门id
* @property {string} rows.describe -备注信息
* @property {int} rows.line 是否在线 0:离线 1:在线
* @property {string} rows.roleName 角色名称
* @example
* {
"status": 200,
"rows": [
{
"id": 708,
"account": "zhuxiao",
"name": "zhuxiao",
"phone": "",
"roleId": 61,
"email": "",
"departmentName": "主部门",
"departmentId": 8,
"describe": "",
"line": 0,
"roleName": "zhuxiao"
},
...
],
"total": 82
}
*/
/**
* 获取部门下的人员列表
* @typedef param_getUserByUnit 获取部门下的人员列表
* @property {int} id -部门id
* @property {int} line -在线状态
* @property {string} name -人员名称
* @property {string} account -人员编号
* @property {int} pageSize -行数
* @property {int} pageNumber -页数
* @property {string} sortable -排序列
* @example
* {
id:1, //部门id
line:1, //在线状态
name:'', //人员名称
account:'', //人员编号
sortable:{
prop:'', //排序列(根据返回列)
order:'desc' //排序方式
}
}
*/
/**
* 获取部门下的人员列表 返回值:人员列表
* @function getUserByUnit -获取部门下的人员列表
* @param {obj.<param_getUserByUnit>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<getUserByUnit>} ... -返回值:人员列表
*/
getUserByUnit: function (options, fn1, fn2) {//获取部门下的人员列表
TY.compositePost(TY.URL.getUserByUnit, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 获取部门和部门成员
* @typedef loadDepartmentCough 获取部门和部门成员
* @property {int.<code>} status -返回码
* @property {string} msg -返回信息
* @example
* {
department:[
{
id:1, //主键id
name:'', //名称
type:'', //类型
pId:1, //父级id
isParent:1, //是否是部门
status:, //设备状态
line:, //在线状态
DevNo:'', //设备编号
sn:'ccc' //产品序号
isBound:0 //是否绑定
}
]
}
*/
/**
* 获取部门和部门成员 返回值:部门和部门成员
* @function loadDepartmentCough -获取部门和部门成员
* @param {int} id -部门id
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<loadDepartmentCough>} ... -获取部门和部门成员
*/
loadDepartmentCough: function (options, fn1, fn2) {//获取主部门
TY.compositePost(TY.URL.loadDepartmentCough, options, null, fn1, fn2);
return command;
}
};
return command;
})();
TY.VersionManager = (function () {
var command = {
/**
* 添加或修改app
* @typedef param_modifyApp 添加或修改app
* @property {int} id -id(添加时不需要)
* @property {int} instruction -备注
* @property {int} name -app名称
* @example
* {
id:1, //id
instruction:'详细信息', //详细信息
name:'版本名称', //版本名称
}
*/
/**
* 添加或者修改app 返回值:操作状态
* @function modifyApp -添加或者修改app
* @param {obj.<param_modifyApp>} id -部门id
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<code>} ... -返回参数
* @example
* {
"status": 200
}
*/
modifyApp: function (options, fn1, fn2) {//添加&修改App
TY.compositePost(TY.URL.modifyApp, options, (options) => {
if(!options.name){
return 'name';
}
}, fn1, fn2);
return command;
},
/**
* @typedef getAppInfo
* @property {array.<code>} status -返回码
* @property {int} id -id
* @property {int} instruction -备注
* @property {int} name -APP名称
* @example
*{
"status": 200, //返回码
id:1, //id
instruction:'详细信息', //备注
name:'版本名称', //APP名称
}
*/
/**
* 获取app详情 返回值:APP详情
* @function getAppInfo -获取app详情
* @param {int} id -id
* @example{ id:1 //id}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<getAppInfo>} ... -返回参数
*
*/
getAppInfo: function (options, fn1, fn2) {//获取App详情
TY.compositePost(TY.URL.getAppInfo, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 删除aapp 返回值:操作状态
* @function deleteApp -删除App
* @param {int} id -id
* @example
* {
id:1 //APPid
}
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<code>} ... -返回参数
* @example{"status": 200}
*/
deleteApp: function (options, fn1, fn2) {//删除App
TY.compositePost(TY.URL.deleteApp, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* @typedef getAppList
* @property {array.<code>} status -返回码
* @property {int} total -数据总条数
* @property {array} rows -数据集合
* @property {int} rows.id -id
* @property {string} rows.app_id -APP ID唯一标识
* @property {string} rows.createtime -创建时间
* @property {string} rows.instruction -备注
* @property {string} rows.name -APP名称
* @example
* {
"total": 3,
"rows": [
{
"id": 32,
"app_id": "e165421110ba03099a1c0393373c5b43",
"createtime": "2019-02-16 11:14:22",
"instruction": "1",
"name": "233"
},
...
],
"status": 200
}
*/
/**
* 获取app列表
* @typedef param_getAppList 获取app列表
* @property {int} pageNumber -页数
* @property {int} pageSize -行数
* @property {String} sortable -排序列(参数返回列)
* @example
* {
pageNumber:1, //页数
pageSize:10, //行数
sortable:'name', //排序列(参考返回列)
}
*/
/**
* 获取app列表 返回值:APP列表
* @function getAppList -获取app列表
* @param {obj.<param_getAppList>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<getAppList>} ... -返回参数
*/
getAppList: function (options, fn1, fn2) {//获取App列表
options = options || {};
TY.compositePost(TY.URL.getAppList, options, null, fn1, fn2);
return command;
},
/**
* 添加或者修改APP版本
* @typedef param_modifyAppVersion 添加或者修改APP版本
* @property {file} file -文件流(FormData(file))
* @property {int} activite -状态(0 不可用 / 1 可用)
* @property {String} filename -文件名称
* @property {String} instruction -备注
* @property {String} os -系统 (Android or ios)
* @property {String} title -标题
* @property {int} type -类型(0 不强制更新 / 1 强制)
* @property {String} version -版本名
* @property {String} version_code -版本编号
* @property {file} id -版本id (添加非必要)
* @example
* {
activite:1, //状态(0 不可用 / 1 可用)
filename:'1.2.1.zip', //文件名称
instruction:'解决了..,添加了...', //描述
os:'android', //系统
title:'节日定制', //标题
type:1, //类型(0 不强制更新 / 1 强制)
version:'1.2.1', //版本名
version_code:'abc', //版本编号
id:1 //版本id (添加非必要)
}
*/
/**
* 添加或修改APP版本 返回值:操作状态
* @function modifyAppVersion -添加&修改App版本
* @param {obj.<param_modifyAppVersion>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<code>} ... -返回参数
*/
modifyAppVersion: function (options, fn1, fn2, fn3) {//添加&修改App版本
TY.compositeUpload(TY.URL.modifyAppVersion, options, (options) => {
if(!options.filename){
return 'filename';
}
if(!options.os){
return 'os';
}
if(!options.version){
return 'version';
}
if(!options.version_code){
return 'version_code';
}
if(!options.type){
return 'type';
}
}, fn1, fn2, fn3);
return command;
},
/**
* 获取APP版本详情 返回值:APP版本详情
* @function getAppVersionInfo -获取APP版本详情
* @param {int} id -版本id
* @example {id:1 //版本id }
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @example 返回参数
* {
"id": 71, //int 版本id (添加非必要)
"activite": 1, //int 状态(0 不可用 / 1 可用)
"filename": "ivmcc_telyes_release_1.7.24_993.apk", //string 文件名称
"instruction": "", //string 备注
"os": "Android", //os 系统(Android or ios)
"type": 0, //int 类型(0 不强制更新 / 1 强制)
"version": "ivmcc_telyes_release_1.7.24_993", //string 版本名
"version_code": 993, //string 版本编号
"status": 200
}
*/
getAppVersionInfo: function (options, fn1, fn2) {//获取App版本详情
TY.compositePost(TY.URL.getAppVersionInfo, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 删除App版本 返回值:操作状态
* @function deleteAppVersion -删除app版本
* @param {int} id -版本id
* @example { id:1 //版本id }
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<code>} ... -返回参数
* @example 返回参数 { "status": 200}
*/
deleteAppVersion: function (options, fn1, fn2) {//删除App版本
TY.compositePost(TY.URL.deleteAppVersion, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
/**
* 获取App版本列表
* @typedef getAppVersionList
* @property {array.<code>} status -返回码
* @property {int} total -数据总条数
* @property {array} rows -数据集合
* @property {int} rows.activite -状态(0不可用/可用)
* @property {String} rows.filename -文件名称
* @property {String} rows.instruction -备注
* @property {string} rows.os -系统 (Android or ios)
* @property {String} rows.title -标题
* @property {int} rows.type -类型(0 不强制更新 / 1 强制)
* @property {string} rows.version -版本名
* @property {string} rows.version_code -版本编号
* @property {int} rows.id 版本id(添加非必要)
* @example
* {
"total": 1,
"rows": [
{
"id": 71,
"activite": 1,
"filename": "ivmcc_telyes_release_1.7.24_993.apk",
"instruction": "",
"os": "Android",
"type": 0,
"version": "ivmcc_telyes_release_1.7.24_993",
"version_code": 993,
},
...
],
"status": 200
}
*/
/**
* 获取App版本列表
* @typedef param_getAppVersionList 获取App版本列表
* @property {string} app_id -APP 的 app_id字段 (必需)(VersionManager.getAppList.row.id)
* @property {int} pageNumber -当前页
* @property {int} pageSize -行数
* @property {object} sortable -排序(参照返回列)
* @example 返回参数
* {
app_id:'1E2D3365E', //版本id
pageNumber:1, //当前页
pageSize:10, //行数
sortable:{
prop:'', //排序列(参照返回列)
order:'desc' //排序方式
}
}
*/
/**
* 获取App版本列表 返回值:APP版本列表
* @function getAppVersionList -获取App版本列表
* @param {obj.<param_getAppVersionList>} options -参数
* @param {Function} resolve - 必选 成功的回调
* @param {Function} reject -不是必选 失败的回调
* @param {obj.<getAppVersionList>} ... -返回参数
*/
getAppVersionList: function (options, fn1, fn2) {//获取App版本列表
TY.reqPost(options, TY.URL.getAppVersionList, fn1, fn2);
return command;
}
}
return command;
})();
TY.EvidenceManager = (function () {
var command = {
getDeptList: function (options, fn1, fn2) {//获取部门列表
TY.reqPost(options, TY.URL.evidence.deptList, fn1, fn2);
return command;
},
getSortList: function (options, fn1, fn2) {//获取文件类型列表
TY.reqPost(options, TY.URL.evidence.sortList, fn1, fn2);
return command;
},
getCaseTopicList: function (options, fn1, fn2) {//获取案件类型列表
TY.reqPost(options, TY.URL.evidence.caseTopicList, fn1, fn2);
return command;
},
getFileList: function (options, fn1, fn2) {//获取文件列表
TY.reqPost(options, TY.URL.evidence.fileList, fn1, fn2);
return command;
},
getDevList: function (options, fn1, fn2) {//获取设备列表
TY.reqPost(options, TY.URL.evidence.devList, fn1, fn2);
return command;
},
getFileDetail: function (options, fn1, fn2) {//获取文件信息
TY.compositePost(TY.URL.evidence.fileDetail, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
delFile: function (options, fn1, fn2) {//删除文件
TY.compositePost(TY.URL.evidence.delFile, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
saveFile: function (options, fn1, fn2) {//编辑文件信息
TY.compositePost(TY.URL.evidence.saveFile, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2);
return command;
},
downFile: function (options, fn1, fn2, fn3) {//下载文件
return TY.compositeDown(TY.URL.evidence.downFile, options, (options) => {
if(!options.id){
return 'id';
}
}, fn1, fn2, fn3);
},
exportCsv: function (options, fn1, fn2, fn3) {//导出文件
return TY.compositeDown(TY.URL.evidence.exportCsv, options, null, fn1, fn2, fn3);
},
openN: function (options, fn1, fn2) {//记录播放次数
TY.reqPost(options, TY.URL.evidence.openN, fn1, fn2);
return command;
},
uploadFile: function (options, fn1, fn2, fn3) {//上传文件
return TY.compositeUpload(TY.URL.evidence.uploadFile, options, null, fn1, fn2, fn3);
},
getMajorCount: function (options, fn1, fn2) {//获取文档重要级别统计
TY.compositePost(TY.URL.evidence.majorCount, options, null, fn1, fn2);
return command;
},
getImportCount: function (options, fn1, fn2) {//获取文档导入时段统计
TY.compositePost(TY.URL.evidence.importCount, options, null, fn1, fn2);
return command;
},
getPersonnelCount: function (options, fn1, fn2) {//获取人员资料统计
TY.compositePost(TY.URL.evidence.personnelCount, options, null, fn1, fn2);
return command;
},
getAssessCount: function (options, fn1, fn2) {//获取人员考核统计
TY.compositePost(TY.URL.evidence.assessCount, options, null, fn1, fn2);
return command;
},
};
return command;
})();
TY.SOS = (function () {
var callback, data;
var addListenEvent = function (callback) {//注册信息通知回调事件
callback = callback;
};
var getInfo = function () {//获取sos信息
return data;
};
return this;
})();
TY.reqUpload = function (params, url, successCallback, failCallback, progressCallback) { //post上传请求
console.log("请求参数", params, url);
var xmlhttp;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp = new XMLHttpRequest();
} else {
// IE6, IE5 浏览器执行代码
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == TY.RcCode.RC_CODE_S_OK.code) {
var data = eval('(' + ('response' in xmlhttp ? xmlhttp.response : xmlhttp.responseText || xmlhttp.responseXML) + ')');
console.log("返回结果", data);
if (typeof data == 'object' && data.status == TY.RcCode.RC_CODE_S_OK.code && successCallback) {
successCallback(data);
return;
}
}
TY.USER.checkTokenInvalidEvent&&TY.USER.checkTokenInvalidEvent(data && data.status || '');
if (failCallback) {
failCallback(TY.RcCode.returnMsg(TY.RcCode.getRcCode(data && data.status || '')));
}
}
};
xmlhttp.open("POST", url, true);
if (progressCallback) xmlhttp.upload.onprogress = progressCallback;
if (TY.USER.token) {
xmlhttp.setRequestHeader("x-access-token", TY.USER.token);
}
let fd = new FormData();
for (let key in params) {
fd.append(key, params[key]);
}
xmlhttp.send(fd);
return xmlhttp;
};
TY.reqDown = function (params, url, resolve, reject, progressCallback) { //下载文件
var req = new XMLHttpRequest();
req.open("POST", url, true);
//监听进度事件
/**
* 添加事件监听事件
* @function addEventListener -添加事件监听事件
* @param {} event
* @param {} eventCallback(data)
*/
req.addEventListener("progress", function (evt) {
// console.log(evt.loaded / evt.total);
progressCallback && progressCallback(evt);
}, false);
req.responseType = "blob";
req.setRequestHeader("x-access-token",TY.USER.token);
req.setRequestHeader("Content-Type","application/json");
req.onreadystatechange = function () {
var status = req.getResponseHeader("status");
if (req.readyState === 4 && req.status === TY.RcCode.RC_CODE_S_OK.code&&status==TY.RcCode.RC_CODE_S_OK.code) {
if (typeof window.chrome != 'undefined') {
// Chrome version
var link = document.createElement('a');
link.href = window.URL.createObjectURL(req.response);
link.download = decodeURIComponent(req.getResponseHeader("filename"));
link.click();
resolve&&resolve(TY.RcCode.returnMsg(TY.RcCode.getRcCode(req.getResponseHeader("status"))));
return;
}
// else if (typeof window.navigator.msSaveBlob !== 'undefined') {
// // IE version
// var blob = new Blob([req.response], { type:req.getResponseHeader("content-type") });
// window.navigator.msSaveBlob(blob, filename);
// } else {
// // Firefox version
// var file = new File([req.response], filename, { type: 'application/force-download' });
// window.open(URL.createObjectURL(file));
// }
TY.USER.checkTokenInvalidEvent&&TY.USER.checkTokenInvalidEvent(data && data.status);
reject&&reject(TY.RcCode.returnMsg(TY.RcCode.ABORT_ERR));
}
};
req.send(JSON.stringify(params));
return req;
};
TY.reqPost = function (params, url, successCallback, failCallback) { //所有的post请求
console.log("请求参数", params, url);
var xmlhttp;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp = new XMLHttpRequest();
} else {
// IE6, IE5 浏览器执行代码
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
if( xmlhttp.status == TY.RcCode.RC_CODE_S_OK.code){
var data = eval('(' + ('response' in xmlhttp ? xmlhttp.response : xmlhttp.responseText || xmlhttp.responseXML) + ')');
console.log("返回结果", data);
if (typeof data == 'object' && data.status == TY.RcCode.RC_CODE_S_OK.code && successCallback) {
successCallback(data);
return;
}
}
TY.USER.checkTokenInvalidEvent&&TY.USER.checkTokenInvalidEvent(data.status);
if (failCallback) {
failCallback(TY.RcCode.returnMsg(TY.RcCode.getRcCode(data.status)));
}
}
};
xmlhttp.open("POST", url, true);
if (TY.USER.token) {
xmlhttp.setRequestHeader("x-access-token", TY.USER.token);
}
xmlhttp.setRequestHeader("content-type", "application/json");
xmlhttp.send(JSON.stringify(params));
};
TY.reqGet = function (params, url, successCallback, failCallback) { //所有的get请求
console.log("请求参数", params, url);
var xmlhttp;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp = new XMLHttpRequest();
} else {
// IE6, IE5 浏览器执行代码
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == TY.RcCode.RC_CODE_S_OK.code) {
var data = eval('(' + ('response' in xmlhttp ? xmlhttp.response : xmlhttp.responseText || xmlhttp.responseXML) + ')');
console.log("返回结果", data);
if (typeof data == 'object' && data.status == TY.RcCode.RC_CODE_S_OK.code) {
if (successCallback) {
successCallback(data);
}
return;
}
}
TY.USER.checkTokenInvalidEvent&&TY.USER.checkTokenInvalidEvent(data.status);
if (failCallback) {
failCallback(data);
}
}
};
xmlhttp.open("GET", url, true);
if (TY.USER.token) {
xmlhttp.setRequestHeader("x-access-token", TY.USER.token);
}
xmlhttp.setRequestHeader("content-type", "application/json");
xmlhttp.send(JSON.stringify(params));
};
TY.reqPut = function (params, url, successCallback, failCallback) { //所有的get请求
console.log("请求参数", params, url);
var xmlhttp;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp = new XMLHttpRequest();
} else {
// IE6, IE5 浏览器执行代码
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == TY.RcCode.RC_CODE_S_OK.code) {
var data = eval('(' + ('response' in xmlhttp ? xmlhttp.response : xmlhttp.responseText || xmlhttp.responseXML) + ')');
console.log("返回结果", data);
if (typeof data == 'object' && data.status == TY.RcCode.RC_CODE_S_OK.code) {
if (successCallback) {
successCallback(data);
}
return;
}
}
TY.USER.checkTokenInvalidEvent&&TY.USER.checkTokenInvalidEvent(data.status);
if (failCallback) {
failCallback(data);
}
}
};
xmlhttp.open("PUT", url, true);
if (TY.USER.token) {
xmlhttp.setRequestHeader("x-access-token", TY.USER.token);
}
xmlhttp.setRequestHeader("content-type", "application/json");
xmlhttp.send(JSON.stringify(params));
};
TY.RcCode = { //返回码
returnMsg(RC_CODE,msg){
if (!RC_CODE) {
RC_CODE= this.OTHER_ABNORMAL;
}
msg=msg?msg:"";
return {status:RC_CODE.code,msg:msg+RC_CODE.msg};
},
getRcCode(code){
for(var p in this){
var obj = this[p];
if(!typeof(obj)!='function'&&obj.code==code){
return obj;
}
}
return null;
},
SOCKET_INIT_SUCCESS: {code:0,msg:"初始化成功"}, //socket初始化成功
RC_CODE_S_OK: {code:200,msg:"请求成功"}, //请求成功
ACTION_BY_BIND: {code:202,msg:"存在绑定关系,操作失败"},
GROUP_NAME_SINGLE: {code:203,msg:"名称不能重复"},
USER_EXIST: {code:204,msg:"已存在,不能重复"}, //请求成功
FENCE_BY_BIND: {code:205,msg:"围栏绑定了设备,请解绑后删除"},
WORK_SHFIT_BY_BIND: {code:206,msg:"班次绑定了设备,请解绑后删除"},
ROLE_BY_BIND: {code:207,msg:"该角色已被用户绑定,请解绑后删除"},
GROUP_BY_BIND: {code:208,msg:"群组绑定了设备,请解绑后删除"},
DEPT_BY_BIND: {code:209,msg:"该部门下有人员,请删除后再次操作"},
USER_CODE_EXIST: {code:210,msg:"人员编号冲突"},
DEV_CODE_EXIST: {code:211,msg:"该设备识别号已注册"},
APP_VERSION_EXIST: {code:212,msg:"app下已经发布了版本,请删除后再次操作"},
EMPTY_TOKEN: {code:300,msg:"token为空,可能未登录"}, //请求头没有token
SPECIAL_PARAMS: {code:213,msg:"参数不能包括特殊字符"}, //请求头没有token
INVALID_TOKEN: {code:401,msg:"token无效"}, //请求头没有token
DEV_OUTLINE: {code:406,msg:"设备不在线"},
DISPATCHER_DISPATCHER: {code:407,msg:"创建调度的人在调度"},
EMPTY_SERVER: {code:460,msg:"服务器地址为空"}, //请求头没有token
EMPTY_PROPS: {code:461,msg:"参数不能为空"}, //请求头没有token
PARAMETERS_ABNORMAL: {code:462,msg:"参数异常"},//参数异常
LOGINED: {code:463,msg:"已经登录过"}, //请求头没有token
DEVICE_FILE_DELETE_LIMIT: {code:463,msg:"不能删除设备文件"}, //请求头没有token
MICROPHONE_LIMIT: {code:465,msg:"麦克风不可用"}, //请求头没有token
RTC_ABNOMAL: {code:466,msg:"RTC异常"}, //请求头没有token
OTHER_TALK: {code:467,msg:"已有人在说话"}, //请求头没有tokenNOT_SUBSCRIBE_DEVICE
NOT_STREAM: {code:468,msg:"该设备不在监控房间"}, //请求头没有token
NOT_SUBSCRIBE_DEVICE: {code:469,msg:"该设备不是被监控状态"}, //请求头没有token
DEVICE_MONITER_EXIST: {code:470,msg:"已经邀请该设备进入监控"}, //请求头没有token
RECORD_LESS: {code:471,msg:"结束失败,录制时间太短"}, //请求头没有token
SYSTEM_ERROR: {code:500,msg:"系统错误"},
LOGIN_FAIL:{code:410,msg:"账号密码错误"},//登录失败==》账号密码错误
LOGIN_USER_FAIL:{code:411,msg:"用户不存在"},//登录失败==》用户不存在
LIMIT_AUTHORITY: {code:403,msg:"没有权限"}, //权限不够,
OTHER_ABNORMAL: {code:999,msg:"其他异常"}//参数异常
}
})(TY);
function getmatrix(a, b, c, d, e, f) {
var aa = Math.round(180 * Math.asin(a) / Math.PI);
var bb = Math.round(180 * Math.acos(b) / Math.PI);
var cc = Math.round(180 * Math.asin(c) / Math.PI);
var dd = Math.round(180 * Math.acos(d) / Math.PI);
var deg = 0;
if (aa == bb || -aa == bb) {
deg = dd;
} else if (-aa + bb == 180) {
deg = 180 + cc;
} else if (aa + bb == 180) {
deg = 360 - cc || 360 - dd;
}
return deg >= 360 ? 0 : deg;
//return (aa+','+bb+','+cc+','+dd);
};
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.io=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(_dereq_,module,exports){module.exports=_dereq_("./lib/")},{"./lib/":2}],2:[function(_dereq_,module,exports){module.exports=_dereq_("./socket");module.exports.parser=_dereq_("engine.io-parser")},{"./socket":3,"engine.io-parser":19}],3:[function(_dereq_,module,exports){(function(global){var transports=_dereq_("./transports");var Emitter=_dereq_("component-emitter");var debug=_dereq_("debug")("engine.io-client:socket");var index=_dereq_("indexof");var parser=_dereq_("engine.io-parser");var parseuri=_dereq_("parseuri");var parsejson=_dereq_("parsejson");var parseqs=_dereq_("parseqs");module.exports=Socket;function noop(){}function Socket(uri,opts){if(!(this instanceof Socket))return new Socket(uri,opts);opts=opts||{};if(uri&&"object"==typeof uri){opts=uri;uri=null}if(uri){uri=parseuri(uri);opts.hostname=uri.host;opts.secure=uri.protocol=="https"||uri.protocol=="wss";opts.port=uri.port;if(uri.query)opts.query=uri.query}else if(opts.host){opts.hostname=parseuri(opts.host).host}this.secure=null!=opts.secure?opts.secure:global.location&&"https:"==location.protocol;if(opts.hostname&&!opts.port){opts.port=this.secure?"443":"80"}this.agent=opts.agent||false;this.hostname=opts.hostname||(global.location?location.hostname:"localhost");this.port=opts.port||(global.location&&location.port?location.port:this.secure?443:80);this.query=opts.query||{};if("string"==typeof this.query)this.query=parseqs.decode(this.query);this.upgrade=false!==opts.upgrade;this.path=(opts.path||"/engine.io").replace(/\/$/,"")+"/";this.forceJSONP=!!opts.forceJSONP;this.jsonp=false!==opts.jsonp;this.forceBase64=!!opts.forceBase64;this.enablesXDR=!!opts.enablesXDR;this.timestampParam=opts.timestampParam||"t";this.timestampRequests=opts.timestampRequests;this.transports=opts.transports||["polling","websocket"];this.readyState="";this.writeBuffer=[];this.policyPort=opts.policyPort||843;this.rememberUpgrade=opts.rememberUpgrade||false;this.binaryType=null;this.onlyBinaryUpgrades=opts.onlyBinaryUpgrades;this.perMessageDeflate=false!==opts.perMessageDeflate?opts.perMessageDeflate||{}:false;if(true===this.perMessageDeflate)this.perMessageDeflate={};if(this.perMessageDeflate&&null==this.perMessageDeflate.threshold){this.perMessageDeflate.threshold=1024}this.pfx=opts.pfx||null;this.key=opts.key||null;this.passphrase=opts.passphrase||null;this.cert=opts.cert||null;this.ca=opts.ca||null;this.ciphers=opts.ciphers||null;this.rejectUnauthorized=opts.rejectUnauthorized===undefined?null:opts.rejectUnauthorized;var freeGlobal=typeof global=="object"&&global;if(freeGlobal.global===freeGlobal){if(opts.extraHeaders&&Object.keys(opts.extraHeaders).length>0){this.extraHeaders=opts.extraHeaders}}this.open()}Socket.priorWebsocketSuccess=false;Emitter(Socket.prototype);Socket.protocol=parser.protocol;Socket.Socket=Socket;Socket.Transport=_dereq_("./transport");Socket.transports=_dereq_("./transports");Socket.parser=_dereq_("engine.io-parser");Socket.prototype.createTransport=function(name){debug('creating transport "%s"',name);var query=clone(this.query);query.EIO=parser.protocol;query.transport=name;if(this.id)query.sid=this.id;var transport=new transports[name]({agent:this.agent,hostname:this.hostname,port:this.port,secure:this.secure,path:this.path,query:query,forceJSONP:this.forceJSONP,jsonp:this.jsonp,forceBase64:this.forceBase64,enablesXDR:this.enablesXDR,timestampRequests:this.timestampRequests,timestampParam:this.timestampParam,policyPort:this.policyPort,socket:this,pfx:this.pfx,key:this.key,passphrase:this.passphrase,cert:this.cert,ca:this.ca,ciphers:this.ciphers,rejectUnauthorized:this.rejectUnauthorized,perMessageDeflate:this.perMessageDeflate,extraHeaders:this.extraHeaders});return transport};function clone(obj){var o={};for(var i in obj){if(obj.hasOwnProperty(i)){o[i]=obj[i]}}return o}Socket.prototype.open=function(){var transport;if(this.rememberUpgrade&&Socket.priorWebsocketSuccess&&this.transports.indexOf("websocket")!=-1){transport="websocket"}else if(0===this.transports.length){var self=this;setTimeout(function(){self.emit("error","No transports available")},0);return}else{transport=this.transports[0]}this.readyState="opening";try{transport=this.createTransport(transport)}catch(e){this.transports.shift();this.open();return}transport.open();this.setTransport(transport)};Socket.prototype.setTransport=function(transport){debug("setting transport %s",transport.name);var self=this;if(this.transport){debug("clearing existing transport %s",this.transport.name);this.transport.removeAllListeners()}this.transport=transport;transport.on("drain",function(){self.onDrain()}).on("packet",function(packet){self.onPacket(packet)}).on("error",function(e){self.onError(e)}).on("close",function(){self.onClose("transport close")})};Socket.prototype.probe=function(name){debug('probing transport "%s"',name);var transport=this.createTransport(name,{probe:1}),failed=false,self=this;Socket.priorWebsocketSuccess=false;function onTransportOpen(){if(self.onlyBinaryUpgrades){var upgradeLosesBinary=!this.supportsBinary&&self.transport.supportsBinary;failed=failed||upgradeLosesBinary}if(failed)return;debug('probe transport "%s" opened',name);transport.send([{type:"ping",data:"probe"}]);transport.once("packet",function(msg){if(failed)return;if("pong"==msg.type&&"probe"==msg.data){debug('probe transport "%s" pong',name);self.upgrading=true;self.emit("upgrading",transport);if(!transport)return;Socket.priorWebsocketSuccess="websocket"==transport.name;debug('pausing current transport "%s"',self.transport.name);self.transport.pause(function(){if(failed)return;if("closed"==self.readyState)return;debug("changing transport and sending upgrade packet");cleanup();self.setTransport(transport);transport.send([{type:"upgrade"}]);self.emit("upgrade",transport);transport=null;self.upgrading=false;self.flush()})}else{debug('probe transport "%s" failed',name);var err=new Error("probe error");err.transport=transport.name;self.emit("upgradeError",err)}})}function freezeTransport(){if(failed)return;failed=true;cleanup();transport.close();transport=null}function onerror(err){var error=new Error("probe error: "+err);error.transport=transport.name;freezeTransport();debug('probe transport "%s" failed because of error: %s',name,err);self.emit("upgradeError",error)}function onTransportClose(){onerror("transport closed")}function onclose(){onerror("socket closed")}function onupgrade(to){if(transport&&to.name!=transport.name){debug('"%s" works - aborting "%s"',to.name,transport.name);freezeTransport()}}function cleanup(){transport.removeListener("open",onTransportOpen);transport.removeListener("error",onerror);transport.removeListener("close",onTransportClose);self.removeListener("close",onclose);self.removeListener("upgrading",onupgrade)}transport.once("open",onTransportOpen);transport.once("error",onerror);transport.once("close",onTransportClose);this.once("close",onclose);this.once("upgrading",onupgrade);transport.open()};Socket.prototype.onOpen=function(){debug("socket open");this.readyState="open";Socket.priorWebsocketSuccess="websocket"==this.transport.name;this.emit("open");this.flush();if("open"==this.readyState&&this.upgrade&&this.transport.pause){debug("starting upgrade probes");for(var i=0,l=this.upgrades.length;i<l;i++){this.probe(this.upgrades[i])}}};Socket.prototype.onPacket=function(packet){if("opening"==this.readyState||"open"==this.readyState){debug('socket receive: type "%s", data "%s"',packet.type,packet.data);this.emit("packet",packet);this.emit("heartbeat");switch(packet.type){case"open":this.onHandshake(parsejson(packet.data));break;case"pong":this.setPing();this.emit("pong");break;case"error":var err=new Error("server error");err.code=packet.data;this.onError(err);break;case"message":this.emit("data",packet.data);this.emit("message",packet.data);break}}else{debug('packet received with socket readyState "%s"',this.readyState)}};Socket.prototype.onHandshake=function(data){this.emit("handshake",data);this.id=data.sid;this.transport.query.sid=data.sid;this.upgrades=this.filterUpgrades(data.upgrades);this.pingInterval=data.pingInterval;this.pingTimeout=data.pingTimeout;this.onOpen();if("closed"==this.readyState)return;this.setPing();this.removeListener("heartbeat",this.onHeartbeat);this.on("heartbeat",this.onHeartbeat)};Socket.prototype.onHeartbeat=function(timeout){clearTimeout(this.pingTimeoutTimer);var self=this;self.pingTimeoutTimer=setTimeout(function(){if("closed"==self.readyState)return;self.onClose("ping timeout")},timeout||self.pingInterval+self.pingTimeout)};Socket.prototype.setPing=function(){var self=this;clearTimeout(self.pingIntervalTimer);self.pingIntervalTimer=setTimeout(function(){debug("writing ping packet - expecting pong within %sms",self.pingTimeout);self.ping();self.onHeartbeat(self.pingTimeout)},self.pingInterval)};Socket.prototype.ping=function(){var self=this;this.sendPacket("ping",function(){self.emit("ping")})};Socket.prototype.onDrain=function(){this.writeBuffer.splice(0,this.prevBufferLen);this.prevBufferLen=0;if(0===this.writeBuffer.length){this.emit("drain")}else{this.flush()}};Socket.prototype.flush=function(){if("closed"!=this.readyState&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length){debug("flushing %d packets in socket",this.writeBuffer.length);this.transport.send(this.writeBuffer);this.prevBufferLen=this.writeBuffer.length;this.emit("flush")}};Socket.prototype.write=Socket.prototype.send=function(msg,options,fn){this.sendPacket("message",msg,options,fn);return this};Socket.prototype.sendPacket=function(type,data,options,fn){if("function"==typeof data){fn=data;data=undefined}if("function"==typeof options){fn=options;options=null}if("closing"==this.readyState||"closed"==this.readyState){return}options=options||{};options.compress=false!==options.compress;var packet={type:type,data:data,options:options};this.emit("packetCreate",packet);this.writeBuffer.push(packet);if(fn)this.once("flush",fn);this.flush()};Socket.prototype.close=function(){if("opening"==this.readyState||"open"==this.readyState){this.readyState="closing";var self=this;if(this.writeBuffer.length){this.once("drain",function(){if(this.upgrading){waitForUpgrade()}else{close()}})}else if(this.upgrading){waitForUpgrade()}else{close()}}function close(){self.onClose("forced close");debug("socket closing - telling transport to close");self.transport.close()}function cleanupAndClose(){self.removeListener("upgrade",cleanupAndClose);self.removeListener("upgradeError",cleanupAndClose);close()}function waitForUpgrade(){self.once("upgrade",cleanupAndClose);self.once("upgradeError",cleanupAndClose)}return this};Socket.prototype.onError=function(err){debug("socket error %j",err);Socket.priorWebsocketSuccess=false;this.emit("error",err);this.onClose("transport error",err)};Socket.prototype.onClose=function(reason,desc){if("opening"==this.readyState||"open"==this.readyState||"closing"==this.readyState){debug('socket close with reason: "%s"',reason);var self=this;clearTimeout(this.pingIntervalTimer);clearTimeout(this.pingTimeoutTimer);this.transport.removeAllListeners("close");this.transport.close();this.transport.removeAllListeners();this.readyState="closed";this.id=null;this.emit("close",reason,desc);self.writeBuffer=[];self.prevBufferLen=0}};Socket.prototype.filterUpgrades=function(upgrades){var filteredUpgrades=[];for(var i=0,j=upgrades.length;i<j;i++){if(~index(this.transports,upgrades[i]))filteredUpgrades.push(upgrades[i])}return filteredUpgrades}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{"./transport":4,"./transports":5,"component-emitter":15,debug:17,"engine.io-parser":19,indexof:23,parsejson:26,parseqs:27,parseuri:28}],4:[function(_dereq_,module,exports){var parser=_dereq_("engine.io-parser");var Emitter=_dereq_("component-emitter");module.exports=Transport;function Transport(opts){this.path=opts.path;this.hostname=opts.hostname;this.port=opts.port;this.secure=opts.secure;this.query=opts.query;this.timestampParam=opts.timestampParam;this.timestampRequests=opts.timestampRequests;this.readyState="";this.agent=opts.agent||false;this.socket=opts.socket;this.enablesXDR=opts.enablesXDR;this.pfx=opts.pfx;this.key=opts.key;this.passphrase=opts.passphrase;this.cert=opts.cert;this.ca=opts.ca;this.ciphers=opts.ciphers;this.rejectUnauthorized=opts.rejectUnauthorized;this.extraHeaders=opts.extraHeaders}Emitter(Transport.prototype);Transport.prototype.onError=function(msg,desc){var err=new Error(msg);err.type="TransportError";err.description=desc;this.emit("error",err);return this};Transport.prototype.open=function(){if("closed"==this.readyState||""==this.readyState){this.readyState="opening";this.doOpen()}return this};Transport.prototype.close=function(){if("opening"==this.readyState||"open"==this.readyState){this.doClose();this.onClose()}return this};Transport.prototype.send=function(packets){if("open"==this.readyState){this.write(packets)}else{throw new Error("Transport not open")}};Transport.prototype.onOpen=function(){this.readyState="open";this.writable=true;this.emit("open")};Transport.prototype.onData=function(data){var packet=parser.decodePacket(data,this.socket.binaryType);this.onPacket(packet)};Transport.prototype.onPacket=function(packet){this.emit("packet",packet)};Transport.prototype.onClose=function(){this.readyState="closed";this.emit("close")}},{"component-emitter":15,"engine.io-parser":19}],5:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_("xmlhttprequest-ssl");var XHR=_dereq_("./polling-xhr");var JSONP=_dereq_("./polling-jsonp");var websocket=_dereq_("./websocket");exports.polling=polling;exports.websocket=websocket;function polling(opts){var xhr;var xd=false;var xs=false;var jsonp=false!==opts.jsonp;if(global.location){var isSSL="https:"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}xd=opts.hostname!=location.hostname||port!=opts.port;xs=opts.secure!=isSSL}opts.xdomain=xd;opts.xscheme=xs;xhr=new XMLHttpRequest(opts);if("open"in xhr&&!opts.forceJSONP){return new XHR(opts)}else{if(!jsonp)throw new Error("JSONP disabled");return new JSONP(opts)}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{"./polling-jsonp":6,"./polling-xhr":7,"./websocket":9,"xmlhttprequest-ssl":10}],6:[function(_dereq_,module,exports){(function(global){var Polling=_dereq_("./polling");var inherit=_dereq_("component-inherit");module.exports=JSONPPolling;var rNewline=/\n/g;var rEscapedNewline=/\\n/g;var callbacks;var index=0;function empty(){}function JSONPPolling(opts){Polling.call(this,opts);this.query=this.query||{};if(!callbacks){if(!global.___eio)global.___eio=[];callbacks=global.___eio}this.index=callbacks.length;var self=this;callbacks.push(function(msg){self.onData(msg)});this.query.j=this.index;if(global.document&&global.addEventListener){global.addEventListener("beforeunload",function(){if(self.script)self.script.onerror=empty},false)}}inherit(JSONPPolling,Polling);JSONPPolling.prototype.supportsBinary=false;JSONPPolling.prototype.doClose=function(){if(this.script){this.script.parentNode.removeChild(this.script);this.script=null}if(this.form){this.form.parentNode.removeChild(this.form);this.form=null;this.iframe=null}Polling.prototype.doClose.call(this)};JSONPPolling.prototype.doPoll=function(){var self=this;var script=document.createElement("script");if(this.script){this.script.parentNode.removeChild(this.script);this.script=null}script.async=true;script.src=this.uri();script.onerror=function(e){self.onError("jsonp poll error",e)};var insertAt=document.getElementsByTagName("script")[0];if(insertAt){insertAt.parentNode.insertBefore(script,insertAt)}else{(document.head||document.body).appendChild(script)}this.script=script;var isUAgecko="undefined"!=typeof navigator&&/gecko/i.test(navigator.userAgent);if(isUAgecko){setTimeout(function(){var iframe=document.createElement("iframe");document.body.appendChild(iframe);document.body.removeChild(iframe)},100)}};JSONPPolling.prototype.doWrite=function(data,fn){var self=this;if(!this.form){var form=document.createElement("form");var area=document.createElement("textarea");var id=this.iframeId="eio_iframe_"+this.index;var iframe;form.className="socketio";form.style.position="absolute";form.style.top="-1000px";form.style.left="-1000px";form.target=id;form.method="POST";form.setAttribute("accept-charset","utf-8");area.name="d";form.appendChild(area);document.body.appendChild(form);this.form=form;this.area=area}this.form.action=this.uri();function complete(){initIframe();fn()}function initIframe(){if(self.iframe){try{self.form.removeChild(self.iframe)}catch(e){self.onError("jsonp polling iframe removal error",e)}}try{var html='<iframe src="javascript:0" name="'+self.iframeId+'">';iframe=document.createElement(html)}catch(e){iframe=document.createElement("iframe");iframe.name=self.iframeId;iframe.src="javascript:0"}iframe.id=self.iframeId;self.form.appendChild(iframe);self.iframe=iframe}initIframe();data=data.replace(rEscapedNewline,"\\\n");this.area.value=data.replace(rNewline,"\\n");try{this.form.submit()}catch(e){}if(this.iframe.attachEvent){this.iframe.onreadystatechange=function(){if(self.iframe.readyState=="complete"){complete()}}}else{this.iframe.onload=complete}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{"./polling":8,"component-inherit":16}],7:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_("xmlhttprequest-ssl");var Polling=_dereq_("./polling");var Emitter=_dereq_("component-emitter");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:polling-xhr");module.exports=XHR;module.exports.Request=Request;function empty(){}function XHR(opts){Polling.call(this,opts);if(global.location){var isSSL="https:"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}this.xd=opts.hostname!=global.location.hostname||port!=opts.port;this.xs=opts.secure!=isSSL}else{this.extraHeaders=opts.extraHeaders}}inherit(XHR,Polling);XHR.prototype.supportsBinary=true;XHR.prototype.request=function(opts){opts=opts||{};opts.uri=this.uri();opts.xd=this.xd;opts.xs=this.xs;opts.agent=this.agent||false;opts.supportsBinary=this.supportsBinary;opts.enablesXDR=this.enablesXDR;opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;opts.extraHeaders=this.extraHeaders;return new Request(opts)};XHR.prototype.doWrite=function(data,fn){var isBinary=typeof data!=="string"&&data!==undefined;var req=this.request({method:"POST",data:data,isBinary:isBinary});var self=this;req.on("success",fn);req.on("error",function(err){self.onError("xhr post error",err)});this.sendXhr=req};XHR.prototype.doPoll=function(){debug("xhr poll");var req=this.request();var self=this;req.on("data",function(data){self.onData(data)});req.on("error",function(err){self.onError("xhr poll error",err)});this.pollXhr=req};function Request(opts){this.method=opts.method||"GET";this.uri=opts.uri;this.xd=!!opts.xd;this.xs=!!opts.xs;this.async=false!==opts.async;this.data=undefined!=opts.data?opts.data:null;this.agent=opts.agent;this.isBinary=opts.isBinary;this.supportsBinary=opts.supportsBinary;this.enablesXDR=opts.enablesXDR;this.pfx=opts.pfx;this.key=opts.key;this.passphrase=opts.passphrase;this.cert=opts.cert;this.ca=opts.ca;this.ciphers=opts.ciphers;this.rejectUnauthorized=opts.rejectUnauthorized;this.extraHeaders=opts.extraHeaders;this.create()}Emitter(Request.prototype);Request.prototype.create=function(){var opts={agent:this.agent,xdomain:this.xd,xscheme:this.xs,enablesXDR:this.enablesXDR};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;var xhr=this.xhr=new XMLHttpRequest(opts);var self=this;try{debug("xhr open %s: %s",this.method,this.uri);xhr.open(this.method,this.uri,this.async);try{if(this.extraHeaders){xhr.setDisableHeaderCheck(true);for(var i in this.extraHeaders){if(this.extraHeaders.hasOwnProperty(i)){xhr.setRequestHeader(i,this.extraHeaders[i])}}}}catch(e){}if(this.supportsBinary){xhr.responseType="arraybuffer"}if("POST"==this.method){try{if(this.isBinary){xhr.setRequestHeader("Content-type","application/octet-stream")}else{xhr.setRequestHeader("Content-type","text/plain;charset=UTF-8")}}catch(e){}}if("withCredentials"in xhr){xhr.withCredentials=true}if(this.hasXDR()){xhr.onload=function(){self.onLoad()};xhr.onerror=function(){self.onError(xhr.responseText)}}else{xhr.onreadystatechange=function(){if(4!=xhr.readyState)return;if(200==xhr.status||1223==xhr.status){self.onLoad()}else{setTimeout(function(){self.onError(xhr.status)},0)}}}debug("xhr data %s",this.data);xhr.send(this.data)}catch(e){setTimeout(function(){self.onError(e)},0);return}if(global.document){this.index=Request.requestsCount++;Request.requests[this.index]=this}};Request.prototype.onSuccess=function(){this.emit("success");this.cleanup()};Request.prototype.onData=function(data){this.emit("data",data);this.onSuccess()};Request.prototype.onError=function(err){this.emit("error",err);this.cleanup(true)};Request.prototype.cleanup=function(fromError){if("undefined"==typeof this.xhr||null===this.xhr){return}if(this.hasXDR()){this.xhr.onload=this.xhr.onerror=empty}else{this.xhr.onreadystatechange=empty}if(fromError){try{this.xhr.abort()}catch(e){}}if(global.document){delete Request.requests[this.index]}this.xhr=null};Request.prototype.onLoad=function(){var data;try{var contentType;try{contentType=this.xhr.getResponseHeader("Content-Type").split(";")[0]}catch(e){}if(contentType==="application/octet-stream"){data=this.xhr.response}else{if(!this.supportsBinary){data=this.xhr.responseText}else{try{data=String.fromCharCode.apply(null,new Uint8Array(this.xhr.response))}catch(e){var ui8Arr=new Uint8Array(this.xhr.response);var dataArray=[];for(var idx=0,length=ui8Arr.length;idx<length;idx++){dataArray.push(ui8Arr[idx])}data=String.fromCharCode.apply(null,dataArray)}}}}catch(e){this.onError(e)}if(null!=data){this.onData(data)}};Request.prototype.hasXDR=function(){return"undefined"!==typeof global.XDomainRequest&&!this.xs&&this.enablesXDR};Request.prototype.abort=function(){this.cleanup()};if(global.document){Request.requestsCount=0;Request.requests={};if(global.attachEvent){global.attachEvent("onunload",unloadHandler)}else if(global.addEventListener){global.addEventListener("beforeunload",unloadHandler,false)}}function unloadHandler(){for(var i in Request.requests){if(Request.requests.hasOwnProperty(i)){Request.requests[i].abort()}}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{"./polling":8,"component-emitter":15,"component-inherit":16,debug:17,"xmlhttprequest-ssl":10}],8:[function(_dereq_,module,exports){var Transport=_dereq_("../transport");var parseqs=_dereq_("parseqs");var parser=_dereq_("engine.io-parser");var inherit=_dereq_("component-inherit");var yeast=_dereq_("yeast");var debug=_dereq_("debug")("engine.io-client:polling");module.exports=Polling;var hasXHR2=function(){var XMLHttpRequest=_dereq_("xmlhttprequest-ssl");var xhr=new XMLHttpRequest({xdomain:false});return null!=xhr.responseType}();function Polling(opts){var forceBase64=opts&&opts.forceBase64;if(!hasXHR2||forceBase64){this.supportsBinary=false}Transport.call(this,opts)}inherit(Polling,Transport);Polling.prototype.name="polling";Polling.prototype.doOpen=function(){this.poll()};Polling.prototype.pause=function(onPause){var pending=0;var self=this;this.readyState="pausing";function pause(){debug("paused");self.readyState="paused";onPause()}if(this.polling||!this.writable){var total=0;if(this.polling){debug("we are currently polling - waiting to pause");total++;this.once("pollComplete",function(){debug("pre-pause polling complete");--total||pause()})}if(!this.writable){debug("we are currently writing - waiting to pause");total++;this.once("drain",function(){debug("pre-pause writing complete");--total||pause()})}}else{pause()}};Polling.prototype.poll=function(){debug("polling");this.polling=true;this.doPoll();this.emit("poll")};Polling.prototype.onData=function(data){var self=this;debug("polling got data %s",data);var callback=function(packet,index,total){if("opening"==self.readyState){self.onOpen()}if("close"==packet.type){self.onClose();return false}self.onPacket(packet)};parser.decodePayload(data,this.socket.binaryType,callback);if("closed"!=this.readyState){this.polling=false;this.emit("pollComplete");if("open"==this.readyState){this.poll()}else{debug('ignoring poll - transport state "%s"',this.readyState)}}};Polling.prototype.doClose=function(){var self=this;function close(){debug("writing close packet");self.write([{type:"close"}])}if("open"==this.readyState){debug("transport open - closing");close()}else{debug("transport not open - deferring close");this.once("open",close)}};Polling.prototype.write=function(packets){var self=this;this.writable=false;var callbackfn=function(){self.writable=true;self.emit("drain")};var self=this;parser.encodePayload(packets,this.supportsBinary,function(data){self.doWrite(data,callbackfn)})};Polling.prototype.uri=function(){var query=this.query||{};var schema=this.secure?"https":"http";var port="";if(false!==this.timestampRequests){query[this.timestampParam]=yeast()}if(!this.supportsBinary&&!query.sid){query.b64=1}query=parseqs.encode(query);if(this.port&&("https"==schema&&this.port!=443||"http"==schema&&this.port!=80)){port=":"+this.port}if(query.length){query="?"+query}var ipv6=this.hostname.indexOf(":")!==-1;return schema+"://"+(ipv6?"["+this.hostname+"]":this.hostname)+port+this.path+query}},{"../transport":4,"component-inherit":16,debug:17,"engine.io-parser":19,parseqs:27,"xmlhttprequest-ssl":10,yeast:30}],9:[function(_dereq_,module,exports){(function(global){var Transport=_dereq_("../transport");var parser=_dereq_("engine.io-parser");var parseqs=_dereq_("parseqs");var inherit=_dereq_("component-inherit");var yeast=_dereq_("yeast");var debug=_dereq_("debug")("engine.io-client:websocket");var BrowserWebSocket=global.WebSocket||global.MozWebSocket;var WebSocket=BrowserWebSocket;if(!WebSocket&&typeof window==="undefined"){try{WebSocket=_dereq_("ws")}catch(e){}}module.exports=WS;function WS(opts){var forceBase64=opts&&opts.forceBase64;if(forceBase64){this.supportsBinary=false}this.perMessageDeflate=opts.perMessageDeflate;Transport.call(this,opts)}inherit(WS,Transport);WS.prototype.name="websocket";WS.prototype.supportsBinary=true;WS.prototype.doOpen=function(){if(!this.check()){return}var self=this;var uri=this.uri();var protocols=void 0;var opts={agent:this.agent,perMessageDeflate:this.perMessageDeflate};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;if(this.extraHeaders){opts.headers=this.extraHeaders}this.ws=BrowserWebSocket?new WebSocket(uri):new WebSocket(uri,protocols,opts);if(this.ws.binaryType===undefined){this.supportsBinary=false}if(this.ws.supports&&this.ws.supports.binary){this.supportsBinary=true;this.ws.binaryType="buffer"}else{this.ws.binaryType="arraybuffer"}this.addEventListeners()};WS.prototype.addEventListeners=function(){var self=this;this.ws.onopen=function(){self.onOpen()};this.ws.onclose=function(){self.onClose()};this.ws.onmessage=function(ev){self.onData(ev.data)};this.ws.onerror=function(e){self.onError("websocket error",e)}};if("undefined"!=typeof navigator&&/iPad|iPhone|iPod/i.test(navigator.userAgent)){WS.prototype.onData=function(data){var self=this;setTimeout(function(){Transport.prototype.onData.call(self,data)},0)}}WS.prototype.write=function(packets){var self=this;this.writable=false;var total=packets.length;for(var i=0,l=total;i<l;i++){(function(packet){parser.encodePacket(packet,self.supportsBinary,function(data){if(!BrowserWebSocket){var opts={};if(packet.options){opts.compress=packet.options.compress}if(self.perMessageDeflate){var len="string"==typeof data?global.Buffer.byteLength(data):data.length;if(len<self.perMessageDeflate.threshold){opts.compress=false}}}try{if(BrowserWebSocket){self.ws.send(data)}else{self.ws.send(data,opts)}}catch(e){debug("websocket closed before onclose event")}--total||done()})})(packets[i])}function done(){self.emit("flush");setTimeout(function(){self.writable=true;self.emit("drain")},0)}};WS.prototype.onClose=function(){Transport.prototype.onClose.call(this)};WS.prototype.doClose=function(){if(typeof this.ws!=="undefined"){this.ws.close()}};WS.prototype.uri=function(){var query=this.query||{};var schema=this.secure?"wss":"ws";var port="";if(this.port&&("wss"==schema&&this.port!=443||"ws"==schema&&this.port!=80)){port=":"+this.port}if(this.timestampRequests){query[this.timestampParam]=yeast()}if(!this.supportsBinary){query.b64=1}query=parseqs.encode(query);if(query.length){query="?"+query}var ipv6=this.hostname.indexOf(":")!==-1;return schema+"://"+(ipv6?"["+this.hostname+"]":this.hostname)+port+this.path+query};WS.prototype.check=function(){return!!WebSocket&&!("__initialize"in WebSocket&&this.name===WS.prototype.name)}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{"../transport":4,"component-inherit":16,debug:17,"engine.io-parser":19,parseqs:27,ws:undefined,yeast:30}],10:[function(_dereq_,module,exports){var hasCORS=_dereq_("has-cors");module.exports=function(opts){var xdomain=opts.xdomain;var xscheme=opts.xscheme;var enablesXDR=opts.enablesXDR;try{if("undefined"!=typeof XMLHttpRequest&&(!xdomain||hasCORS)){return new XMLHttpRequest}}catch(e){}try{if("undefined"!=typeof XDomainRequest&&!xscheme&&enablesXDR){return new XDomainRequest}}catch(e){}if(!xdomain){try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}}}},{"has-cors":22}],11:[function(_dereq_,module,exports){module.exports=after;function after(count,callback,err_cb){var bail=false;err_cb=err_cb||noop;proxy.count=count;return count===0?callback():proxy;function proxy(err,result){if(proxy.count<=0){throw new Error("after called too many times")}--proxy.count;if(err){bail=true;callback(err);callback=err_cb}else if(proxy.count===0&&!bail){callback(null,result)}}}function noop(){}},{}],12:[function(_dereq_,module,exports){module.exports=function(arraybuffer,start,end){var bytes=arraybuffer.byteLength;start=start||0;end=end||bytes;if(arraybuffer.slice){return arraybuffer.slice(start,end)}if(start<0){start+=bytes}if(end<0){end+=bytes}if(end>bytes){end=bytes}if(start>=bytes||start>=end||bytes===0){return new ArrayBuffer(0)}var abv=new Uint8Array(arraybuffer);var result=new Uint8Array(end-start);for(var i=start,ii=0;i<end;i++,ii++){result[ii]=abv[i]}return result.buffer}},{}],13:[function(_dereq_,module,exports){(function(chars){"use strict";exports.encode=function(arraybuffer){var bytes=new Uint8Array(arraybuffer),i,len=bytes.length,base64="";for(i=0;i<len;i+=3){base64+=chars[bytes[i]>>2];
base64+=chars[(bytes[i]&3)<<4|bytes[i+1]>>4];base64+=chars[(bytes[i+1]&15)<<2|bytes[i+2]>>6];base64+=chars[bytes[i+2]&63]}if(len%3===2){base64=base64.substring(0,base64.length-1)+"="}else if(len%3===1){base64=base64.substring(0,base64.length-2)+"=="}return base64};exports.decode=function(base64){var bufferLength=base64.length*.75,len=base64.length,i,p=0,encoded1,encoded2,encoded3,encoded4;if(base64[base64.length-1]==="="){bufferLength--;if(base64[base64.length-2]==="="){bufferLength--}}var arraybuffer=new ArrayBuffer(bufferLength),bytes=new Uint8Array(arraybuffer);for(i=0;i<len;i+=4){encoded1=chars.indexOf(base64[i]);encoded2=chars.indexOf(base64[i+1]);encoded3=chars.indexOf(base64[i+2]);encoded4=chars.indexOf(base64[i+3]);bytes[p++]=encoded1<<2|encoded2>>4;bytes[p++]=(encoded2&15)<<4|encoded3>>2;bytes[p++]=(encoded3&3)<<6|encoded4&63}return arraybuffer}})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")},{}],14:[function(_dereq_,module,exports){(function(global){var BlobBuilder=global.BlobBuilder||global.WebKitBlobBuilder||global.MSBlobBuilder||global.MozBlobBuilder;var blobSupported=function(){try{var a=new Blob(["hi"]);return a.size===2}catch(e){return false}}();var blobSupportsArrayBufferView=blobSupported&&function(){try{var b=new Blob([new Uint8Array([1,2])]);return b.size===2}catch(e){return false}}();var blobBuilderSupported=BlobBuilder&&BlobBuilder.prototype.append&&BlobBuilder.prototype.getBlob;function mapArrayBufferViews(ary){for(var i=0;i<ary.length;i++){var chunk=ary[i];if(chunk.buffer instanceof ArrayBuffer){var buf=chunk.buffer;if(chunk.byteLength!==buf.byteLength){var copy=new Uint8Array(chunk.byteLength);copy.set(new Uint8Array(buf,chunk.byteOffset,chunk.byteLength));buf=copy.buffer}ary[i]=buf}}}function BlobBuilderConstructor(ary,options){options=options||{};var bb=new BlobBuilder;mapArrayBufferViews(ary);for(var i=0;i<ary.length;i++){bb.append(ary[i])}return options.type?bb.getBlob(options.type):bb.getBlob()}function BlobConstructor(ary,options){mapArrayBufferViews(ary);return new Blob(ary,options||{})}module.exports=function(){if(blobSupported){return blobSupportsArrayBufferView?global.Blob:BlobConstructor}else if(blobBuilderSupported){return BlobBuilderConstructor}else{return undefined}}()}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{}],15:[function(_dereq_,module,exports){module.exports=Emitter;function Emitter(obj){if(obj)return mixin(obj)}function mixin(obj){for(var key in Emitter.prototype){obj[key]=Emitter.prototype[key]}return obj}Emitter.prototype.on=Emitter.prototype.addEventListener=function(event,fn){this._callbacks=this._callbacks||{};(this._callbacks[event]=this._callbacks[event]||[]).push(fn);return this};Emitter.prototype.once=function(event,fn){var self=this;this._callbacks=this._callbacks||{};function on(){self.off(event,on);fn.apply(this,arguments)}on.fn=fn;this.on(event,on);return this};Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(event,fn){this._callbacks=this._callbacks||{};if(0==arguments.length){this._callbacks={};return this}var callbacks=this._callbacks[event];if(!callbacks)return this;if(1==arguments.length){delete this._callbacks[event];return this}var cb;for(var i=0;i<callbacks.length;i++){cb=callbacks[i];if(cb===fn||cb.fn===fn){callbacks.splice(i,1);break}}return this};Emitter.prototype.emit=function(event){this._callbacks=this._callbacks||{};var args=[].slice.call(arguments,1),callbacks=this._callbacks[event];if(callbacks){callbacks=callbacks.slice(0);for(var i=0,len=callbacks.length;i<len;++i){callbacks[i].apply(this,args)}}return this};Emitter.prototype.listeners=function(event){this._callbacks=this._callbacks||{};return this._callbacks[event]||[]};Emitter.prototype.hasListeners=function(event){return!!this.listeners(event).length}},{}],16:[function(_dereq_,module,exports){module.exports=function(a,b){var fn=function(){};fn.prototype=b.prototype;a.prototype=new fn;a.prototype.constructor=a}},{}],17:[function(_dereq_,module,exports){exports=module.exports=_dereq_("./debug");exports.log=log;exports.formatArgs=formatArgs;exports.save=save;exports.load=load;exports.useColors=useColors;exports.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:localstorage();exports.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"];function useColors(){return"WebkitAppearance"in document.documentElement.style||window.console&&(console.firebug||console.exception&&console.table)||navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31}exports.formatters.j=function(v){return JSON.stringify(v)};function formatArgs(){var args=arguments;var useColors=this.useColors;args[0]=(useColors?"%c":"")+this.namespace+(useColors?" %c":" ")+args[0]+(useColors?"%c ":" ")+"+"+exports.humanize(this.diff);if(!useColors)return args;var c="color: "+this.color;args=[args[0],c,"color: inherit"].concat(Array.prototype.slice.call(args,1));var index=0;var lastC=0;args[0].replace(/%[a-z%]/g,function(match){if("%%"===match)return;index++;if("%c"===match){lastC=index}});args.splice(lastC,0,c);return args}function log(){return"object"===typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function save(namespaces){try{if(null==namespaces){exports.storage.removeItem("debug")}else{exports.storage.debug=namespaces}}catch(e){}}function load(){var r;try{r=exports.storage.debug}catch(e){}return r}exports.enable(load());function localstorage(){try{return window.localStorage}catch(e){}}},{"./debug":18}],18:[function(_dereq_,module,exports){exports=module.exports=debug;exports.coerce=coerce;exports.disable=disable;exports.enable=enable;exports.enabled=enabled;exports.humanize=_dereq_("ms");exports.names=[];exports.skips=[];exports.formatters={};var prevColor=0;var prevTime;function selectColor(){return exports.colors[prevColor++%exports.colors.length]}function debug(namespace){function disabled(){}disabled.enabled=false;function enabled(){var self=enabled;var curr=+new Date;var ms=curr-(prevTime||curr);self.diff=ms;self.prev=prevTime;self.curr=curr;prevTime=curr;if(null==self.useColors)self.useColors=exports.useColors();if(null==self.color&&self.useColors)self.color=selectColor();var args=Array.prototype.slice.call(arguments);args[0]=exports.coerce(args[0]);if("string"!==typeof args[0]){args=["%o"].concat(args)}var index=0;args[0]=args[0].replace(/%([a-z%])/g,function(match,format){if(match==="%%")return match;index++;var formatter=exports.formatters[format];if("function"===typeof formatter){var val=args[index];match=formatter.call(self,val);args.splice(index,1);index--}return match});if("function"===typeof exports.formatArgs){args=exports.formatArgs.apply(self,args)}var logFn=enabled.log||exports.log||console.log.bind(console);logFn.apply(self,args)}enabled.enabled=true;var fn=exports.enabled(namespace)?enabled:disabled;fn.namespace=namespace;return fn}function enable(namespaces){exports.save(namespaces);var split=(namespaces||"").split(/[\s,]+/);var len=split.length;for(var i=0;i<len;i++){if(!split[i])continue;namespaces=split[i].replace(/\*/g,".*?");if(namespaces[0]==="-"){exports.skips.push(new RegExp("^"+namespaces.substr(1)+"$"))}else{exports.names.push(new RegExp("^"+namespaces+"$"))}}}function disable(){exports.enable("")}function enabled(name){var i,len;for(i=0,len=exports.skips.length;i<len;i++){if(exports.skips[i].test(name)){return false}}for(i=0,len=exports.names.length;i<len;i++){if(exports.names[i].test(name)){return true}}return false}function coerce(val){if(val instanceof Error)return val.stack||val.message;return val}},{ms:25}],19:[function(_dereq_,module,exports){(function(global){var keys=_dereq_("./keys");var hasBinary=_dereq_("has-binary");var sliceBuffer=_dereq_("arraybuffer.slice");var base64encoder=_dereq_("base64-arraybuffer");var after=_dereq_("after");var utf8=_dereq_("utf8");var isAndroid=navigator.userAgent.match(/Android/i);var isPhantomJS=/PhantomJS/i.test(navigator.userAgent);var dontSendBlobs=isAndroid||isPhantomJS;exports.protocol=3;var packets=exports.packets={open:0,close:1,ping:2,pong:3,message:4,upgrade:5,noop:6};var packetslist=keys(packets);var err={type:"error",data:"parser error"};var Blob=_dereq_("blob");exports.encodePacket=function(packet,supportsBinary,utf8encode,callback){if("function"==typeof supportsBinary){callback=supportsBinary;supportsBinary=false}if("function"==typeof utf8encode){callback=utf8encode;utf8encode=null}var data=packet.data===undefined?undefined:packet.data.buffer||packet.data;if(global.ArrayBuffer&&data instanceof ArrayBuffer){return encodeArrayBuffer(packet,supportsBinary,callback)}else if(Blob&&data instanceof global.Blob){return encodeBlob(packet,supportsBinary,callback)}if(data&&data.base64){return encodeBase64Object(packet,callback)}var encoded=packets[packet.type];if(undefined!==packet.data){encoded+=utf8encode?utf8.encode(String(packet.data)):String(packet.data)}return callback(""+encoded)};function encodeBase64Object(packet,callback){var message="b"+exports.packets[packet.type]+packet.data.data;return callback(message)}function encodeArrayBuffer(packet,supportsBinary,callback){if(!supportsBinary){return exports.encodeBase64Packet(packet,callback)}var data=packet.data;var contentArray=new Uint8Array(data);var resultBuffer=new Uint8Array(1+data.byteLength);resultBuffer[0]=packets[packet.type];for(var i=0;i<contentArray.length;i++){resultBuffer[i+1]=contentArray[i]}return callback(resultBuffer.buffer)}function encodeBlobAsArrayBuffer(packet,supportsBinary,callback){if(!supportsBinary){return exports.encodeBase64Packet(packet,callback)}var fr=new FileReader;fr.onload=function(){packet.data=fr.result;exports.encodePacket(packet,supportsBinary,true,callback)};return fr.readAsArrayBuffer(packet.data)}function encodeBlob(packet,supportsBinary,callback){if(!supportsBinary){return exports.encodeBase64Packet(packet,callback)}if(dontSendBlobs){return encodeBlobAsArrayBuffer(packet,supportsBinary,callback)}var length=new Uint8Array(1);length[0]=packets[packet.type];var blob=new Blob([length.buffer,packet.data]);return callback(blob)}exports.encodeBase64Packet=function(packet,callback){var message="b"+exports.packets[packet.type];if(Blob&&packet.data instanceof global.Blob){var fr=new FileReader;fr.onload=function(){var b64=fr.result.split(",")[1];callback(message+b64)};return fr.readAsDataURL(packet.data)}var b64data;try{b64data=String.fromCharCode.apply(null,new Uint8Array(packet.data))}catch(e){var typed=new Uint8Array(packet.data);var basic=new Array(typed.length);for(var i=0;i<typed.length;i++){basic[i]=typed[i]}b64data=String.fromCharCode.apply(null,basic)}message+=global.btoa(b64data);return callback(message)};exports.decodePacket=function(data,binaryType,utf8decode){if(typeof data=="string"||data===undefined){if(data.charAt(0)=="b"){return exports.decodeBase64Packet(data.substr(1),binaryType)}if(utf8decode){try{data=utf8.decode(data)}catch(e){return err}}var type=data.charAt(0);if(Number(type)!=type||!packetslist[type]){return err}if(data.length>1){return{type:packetslist[type],data:data.substring(1)}}else{return{type:packetslist[type]}}}var asArray=new Uint8Array(data);var type=asArray[0];var rest=sliceBuffer(data,1);if(Blob&&binaryType==="blob"){rest=new Blob([rest])}return{type:packetslist[type],data:rest}};exports.decodeBase64Packet=function(msg,binaryType){var type=packetslist[msg.charAt(0)];if(!global.ArrayBuffer){return{type:type,data:{base64:true,data:msg.substr(1)}}}var data=base64encoder.decode(msg.substr(1));if(binaryType==="blob"&&Blob){data=new Blob([data])}return{type:type,data:data}};exports.encodePayload=function(packets,supportsBinary,callback){if(typeof supportsBinary=="function"){callback=supportsBinary;supportsBinary=null}var isBinary=hasBinary(packets);if(supportsBinary&&isBinary){if(Blob&&!dontSendBlobs){return exports.encodePayloadAsBlob(packets,callback)}return exports.encodePayloadAsArrayBuffer(packets,callback)}if(!packets.length){return callback("0:")}function setLengthHeader(message){return message.length+":"+message}function encodeOne(packet,doneCallback){exports.encodePacket(packet,!isBinary?false:supportsBinary,true,function(message){doneCallback(null,setLengthHeader(message))})}map(packets,encodeOne,function(err,results){return callback(results.join(""))})};function map(ary,each,done){var result=new Array(ary.length);var next=after(ary.length,done);var eachWithIndex=function(i,el,cb){each(el,function(error,msg){result[i]=msg;cb(error,result)})};for(var i=0;i<ary.length;i++){eachWithIndex(i,ary[i],next)}}exports.decodePayload=function(data,binaryType,callback){if(typeof data!="string"){return exports.decodePayloadAsBinary(data,binaryType,callback)}if(typeof binaryType==="function"){callback=binaryType;binaryType=null}var packet;if(data==""){return callback(err,0,1)}var length="",n,msg;for(var i=0,l=data.length;i<l;i++){var chr=data.charAt(i);if(":"!=chr){length+=chr}else{if(""==length||length!=(n=Number(length))){return callback(err,0,1)}msg=data.substr(i+1,n);if(length!=msg.length){return callback(err,0,1)}if(msg.length){packet=exports.decodePacket(msg,binaryType,true);if(err.type==packet.type&&err.data==packet.data){return callback(err,0,1)}var ret=callback(packet,i+n,l);if(false===ret)return}i+=n;length=""}}if(length!=""){return callback(err,0,1)}};exports.encodePayloadAsArrayBuffer=function(packets,callback){if(!packets.length){return callback(new ArrayBuffer(0))}function encodeOne(packet,doneCallback){exports.encodePacket(packet,true,true,function(data){return doneCallback(null,data)})}map(packets,encodeOne,function(err,encodedPackets){var totalLength=encodedPackets.reduce(function(acc,p){var len;if(typeof p==="string"){len=p.length}else{len=p.byteLength}return acc+len.toString().length+len+2},0);var resultArray=new Uint8Array(totalLength);var bufferIndex=0;encodedPackets.forEach(function(p){var isString=typeof p==="string";var ab=p;if(isString){var view=new Uint8Array(p.length);for(var i=0;i<p.length;i++){view[i]=p.charCodeAt(i)}ab=view.buffer}if(isString){resultArray[bufferIndex++]=0}else{resultArray[bufferIndex++]=1}var lenStr=ab.byteLength.toString();for(var i=0;i<lenStr.length;i++){resultArray[bufferIndex++]=parseInt(lenStr[i])}resultArray[bufferIndex++]=255;var view=new Uint8Array(ab);for(var i=0;i<view.length;i++){resultArray[bufferIndex++]=view[i]}});return callback(resultArray.buffer)})};exports.encodePayloadAsBlob=function(packets,callback){function encodeOne(packet,doneCallback){exports.encodePacket(packet,true,true,function(encoded){var binaryIdentifier=new Uint8Array(1);binaryIdentifier[0]=1;if(typeof encoded==="string"){var view=new Uint8Array(encoded.length);for(var i=0;i<encoded.length;i++){view[i]=encoded.charCodeAt(i)}encoded=view.buffer;binaryIdentifier[0]=0}var len=encoded instanceof ArrayBuffer?encoded.byteLength:encoded.size;var lenStr=len.toString();var lengthAry=new Uint8Array(lenStr.length+1);for(var i=0;i<lenStr.length;i++){lengthAry[i]=parseInt(lenStr[i])}lengthAry[lenStr.length]=255;if(Blob){var blob=new Blob([binaryIdentifier.buffer,lengthAry.buffer,encoded]);doneCallback(null,blob)}})}map(packets,encodeOne,function(err,results){return callback(new Blob(results))})};exports.decodePayloadAsBinary=function(data,binaryType,callback){if(typeof binaryType==="function"){callback=binaryType;binaryType=null}var bufferTail=data;var buffers=[];var numberTooLong=false;while(bufferTail.byteLength>0){var tailArray=new Uint8Array(bufferTail);var isString=tailArray[0]===0;var msgLength="";for(var i=1;;i++){if(tailArray[i]==255)break;if(msgLength.length>310){numberTooLong=true;break}msgLength+=tailArray[i]}if(numberTooLong)return callback(err,0,1);bufferTail=sliceBuffer(bufferTail,2+msgLength.length);msgLength=parseInt(msgLength);var msg=sliceBuffer(bufferTail,0,msgLength);if(isString){try{msg=String.fromCharCode.apply(null,new Uint8Array(msg))}catch(e){var typed=new Uint8Array(msg);msg="";for(var i=0;i<typed.length;i++){msg+=String.fromCharCode(typed[i])}}}buffers.push(msg);bufferTail=sliceBuffer(bufferTail,msgLength)}var total=buffers.length;buffers.forEach(function(buffer,i){callback(exports.decodePacket(buffer,binaryType,true),i,total)})}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{"./keys":20,after:11,"arraybuffer.slice":12,"base64-arraybuffer":13,blob:14,"has-binary":21,utf8:29}],20:[function(_dereq_,module,exports){module.exports=Object.keys||function keys(obj){var arr=[];var has=Object.prototype.hasOwnProperty;for(var i in obj){if(has.call(obj,i)){arr.push(i)}}return arr}},{}],21:[function(_dereq_,module,exports){(function(global){var isArray=_dereq_("isarray");module.exports=hasBinary;function hasBinary(data){function _hasBinary(obj){if(!obj)return false;if(global.Buffer&&global.Buffer.isBuffer(obj)||global.ArrayBuffer&&obj instanceof ArrayBuffer||global.Blob&&obj instanceof Blob||global.File&&obj instanceof File){return true}if(isArray(obj)){for(var i=0;i<obj.length;i++){if(_hasBinary(obj[i])){return true}}}else if(obj&&"object"==typeof obj){if(obj.toJSON){obj=obj.toJSON()}for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key)&&_hasBinary(obj[key])){return true}}}return false}return _hasBinary(data)}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{isarray:24}],22:[function(_dereq_,module,exports){try{module.exports=typeof XMLHttpRequest!=="undefined"&&"withCredentials"in new XMLHttpRequest}catch(err){module.exports=false}},{}],23:[function(_dereq_,module,exports){var indexOf=[].indexOf;module.exports=function(arr,obj){if(indexOf)return arr.indexOf(obj);for(var i=0;i<arr.length;++i){if(arr[i]===obj)return i}return-1}},{}],24:[function(_dereq_,module,exports){module.exports=Array.isArray||function(arr){return Object.prototype.toString.call(arr)=="[object Array]"}},{}],25:[function(_dereq_,module,exports){var s=1e3;var m=s*60;var h=m*60;var d=h*24;var y=d*365.25;module.exports=function(val,options){options=options||{};if("string"==typeof val)return parse(val);return options.long?long(val):short(val)};function parse(str){str=""+str;if(str.length>1e4)return;var match=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str);if(!match)return;var n=parseFloat(match[1]);var type=(match[2]||"ms").toLowerCase();switch(type){case"years":case"year":case"yrs":case"yr":case"y":return n*y;case"days":case"day":case"d":return n*d;case"hours":case"hour":case"hrs":case"hr":case"h":return n*h;case"minutes":case"minute":case"mins":case"min":case"m":return n*m;case"seconds":case"second":case"secs":case"sec":case"s":return n*s;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return n}}function short(ms){if(ms>=d)return Math.round(ms/d)+"d";if(ms>=h)return Math.round(ms/h)+"h";if(ms>=m)return Math.round(ms/m)+"m";if(ms>=s)return Math.round(ms/s)+"s";return ms+"ms"}function long(ms){return plural(ms,d,"day")||plural(ms,h,"hour")||plural(ms,m,"minute")||plural(ms,s,"second")||ms+" ms"}function plural(ms,n,name){if(ms<n)return;if(ms<n*1.5)return Math.floor(ms/n)+" "+name;return Math.ceil(ms/n)+" "+name+"s"}},{}],26:[function(_dereq_,module,exports){(function(global){var rvalidchars=/^[\],:{}\s]*$/;var rvalidescape=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;var rvalidtokens=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;var rvalidbraces=/(?:^|:|,)(?:\s*\[)+/g;var rtrimLeft=/^\s+/;var rtrimRight=/\s+$/;module.exports=function parsejson(data){if("string"!=typeof data||!data){return null}data=data.replace(rtrimLeft,"").replace(rtrimRight,"");if(global.JSON&&JSON.parse){return JSON.parse(data)}if(rvalidchars.test(data.replace(rvalidescape,"@").replace(rvalidtokens,"]").replace(rvalidbraces,""))){return new Function("return "+data)()}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{}],27:[function(_dereq_,module,exports){exports.encode=function(obj){var str="";for(var i in obj){if(obj.hasOwnProperty(i)){if(str.length)str+="&";str+=encodeURIComponent(i)+"="+encodeURIComponent(obj[i])}}return str};exports.decode=function(qs){var qry={};var pairs=qs.split("&");for(var i=0,l=pairs.length;i<l;i++){var pair=pairs[i].split("=");qry[decodeURIComponent(pair[0])]=decodeURIComponent(pair[1])}return qry}},{}],28:[function(_dereq_,module,exports){var re=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;var parts=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];module.exports=function parseuri(str){var src=str,b=str.indexOf("["),e=str.indexOf("]");if(b!=-1&&e!=-1){str=str.substring(0,b)+str.substring(b,e).replace(/:/g,";")+str.substring(e,str.length)}var m=re.exec(str||""),uri={},i=14;while(i--){uri[parts[i]]=m[i]||""}if(b!=-1&&e!=-1){uri.source=src;uri.host=uri.host.substring(1,uri.host.length-1).replace(/;/g,":");uri.authority=uri.authority.replace("[","").replace("]","").replace(/;/g,":");uri.ipv6uri=true}return uri}},{}],29:[function(_dereq_,module,exports){(function(global){(function(root){var freeExports=typeof exports=="object"&&exports;var freeModule=typeof module=="object"&&module&&module.exports==freeExports&&module;var freeGlobal=typeof global=="object"&&global;if(freeGlobal.global===freeGlobal||freeGlobal.window===freeGlobal){root=freeGlobal}var stringFromCharCode=String.fromCharCode;function ucs2decode(string){var output=[];var counter=0;var length=string.length;var value;var extra;while(counter<length){value=string.charCodeAt(counter++);if(value>=55296&&value<=56319&&counter<length){extra=string.charCodeAt(counter++);if((extra&64512)==56320){output.push(((value&1023)<<10)+(extra&1023)+65536)}else{output.push(value);counter--}}else{output.push(value)}}return output}function ucs2encode(array){var length=array.length;var index=-1;var value;var output="";while(++index<length){value=array[index];if(value>65535){value-=65536;output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value)}return output}function checkScalarValue(codePoint){if(codePoint>=55296&&codePoint<=57343){throw Error("Lone surrogate U+"+codePoint.toString(16).toUpperCase()+" is not a scalar value")}}function createByte(codePoint,shift){return stringFromCharCode(codePoint>>shift&63|128)}function encodeCodePoint(codePoint){if((codePoint&4294967168)==0){return stringFromCharCode(codePoint)}var symbol="";if((codePoint&4294965248)==0){symbol=stringFromCharCode(codePoint>>6&31|192)}else if((codePoint&4294901760)==0){checkScalarValue(codePoint);symbol=stringFromCharCode(codePoint>>12&15|224);symbol+=createByte(codePoint,6)}else if((codePoint&4292870144)==0){symbol=stringFromCharCode(codePoint>>18&7|240);symbol+=createByte(codePoint,12);symbol+=createByte(codePoint,6)}symbol+=stringFromCharCode(codePoint&63|128);return symbol}function utf8encode(string){var codePoints=ucs2decode(string);var length=codePoints.length;var index=-1;var codePoint;var byteString="";while(++index<length){codePoint=codePoints[index];byteString+=encodeCodePoint(codePoint)}return byteString}function readContinuationByte(){if(byteIndex>=byteCount){throw Error("Invalid byte index")}var continuationByte=byteArray[byteIndex]&255;byteIndex++;if((continuationByte&192)==128){return continuationByte&63}throw Error("Invalid continuation byte")}function decodeSymbol(){var byte1;var byte2;var byte3;var byte4;var codePoint;if(byteIndex>byteCount){throw Error("Invalid byte index")}if(byteIndex==byteCount){return false}byte1=byteArray[byteIndex]&255;byteIndex++;if((byte1&128)==0){return byte1}if((byte1&224)==192){var byte2=readContinuationByte();codePoint=(byte1&31)<<6|byte2;if(codePoint>=128){return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&240)==224){byte2=readContinuationByte();byte3=readContinuationByte();codePoint=(byte1&15)<<12|byte2<<6|byte3;if(codePoint>=2048){checkScalarValue(codePoint);return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&248)==240){byte2=readContinuationByte();byte3=readContinuationByte();byte4=readContinuationByte();codePoint=(byte1&15)<<18|byte2<<12|byte3<<6|byte4;if(codePoint>=65536&&codePoint<=1114111){return codePoint}}throw Error("Invalid UTF-8 detected")}var byteArray;var byteCount;var byteIndex;function utf8decode(byteString){byteArray=ucs2decode(byteString);byteCount=byteArray.length;byteIndex=0;var codePoints=[];var tmp;while((tmp=decodeSymbol())!==false){codePoints.push(tmp)}return ucs2encode(codePoints)}var utf8={version:"2.0.0",encode:utf8encode,decode:utf8decode};if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){define(function(){return utf8})}else if(freeExports&&!freeExports.nodeType){if(freeModule){freeModule.exports=utf8}else{var object={};var hasOwnProperty=object.hasOwnProperty;for(var key in utf8){hasOwnProperty.call(utf8,key)&&(freeExports[key]=utf8[key])}}}else{root.utf8=utf8}})(this)}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{}],30:[function(_dereq_,module,exports){"use strict";var alphabet="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".split(""),length=64,map={},seed=0,i=0,prev;function encode(num){var encoded="";do{encoded=alphabet[num%length]+encoded;num=Math.floor(num/length)}while(num>0);return encoded}function decode(str){var decoded=0;for(i=0;i<str.length;i++){decoded=decoded*length+map[str.charAt(i)]}return decoded}function yeast(){var now=encode(+new Date);if(now!==prev)return seed=0,prev=now;return now+"."+encode(seed++)}for(;i<length;i++)map[alphabet[i]]=i;yeast.encode=encode;yeast.decode=decode;module.exports=yeast},{}],31:[function(_dereq_,module,exports){var url=_dereq_("./url");var parser=_dereq_("socket.io-parser");var Manager=_dereq_("./manager");var debug=_dereq_("debug")("socket.io-client");module.exports=exports=lookup;var cache=exports.managers={};function lookup(uri,opts){if(typeof uri=="object"){opts=uri;uri=undefined}opts=opts||{};var parsed=url(uri);var source=parsed.source;var id=parsed.id;var path=parsed.path;var sameNamespace=cache[id]&&path in cache[id].nsps;var newConnection=opts.forceNew||opts["force new connection"]||false===opts.multiplex||sameNamespace;var io;if(newConnection){debug("ignoring socket cache for %s",source);io=Manager(source,opts)}else{if(!cache[id]){debug("new io instance for %s",source);cache[id]=Manager(source,opts)}io=cache[id]}return io.socket(parsed.path)}exports.protocol=parser.protocol;exports.connect=lookup;exports.Manager=_dereq_("./manager");exports.Socket=_dereq_("./socket")},{"./manager":32,"./socket":34,"./url":35,debug:39,"socket.io-parser":47}],32:[function(_dereq_,module,exports){var eio=_dereq_("engine.io-client");var Socket=_dereq_("./socket");var Emitter=_dereq_("component-emitter");var parser=_dereq_("socket.io-parser");var on=_dereq_("./on");var bind=_dereq_("component-bind");var debug=_dereq_("debug")("socket.io-client:manager");var indexOf=_dereq_("indexof");var Backoff=_dereq_("backo2");var has=Object.prototype.hasOwnProperty;module.exports=Manager;function Manager(uri,opts){if(!(this instanceof Manager))return new Manager(uri,opts);if(uri&&"object"==typeof uri){opts=uri;uri=undefined}opts=opts||{};opts.path=opts.path||"/socket.io";this.nsps={};this.subs=[];this.opts=opts;this.reconnection(opts.reconnection!==false);this.reconnectionAttempts(opts.reconnectionAttempts||Infinity);this.reconnectionDelay(opts.reconnectionDelay||1e3);this.reconnectionDelayMax(opts.reconnectionDelayMax||5e3);this.randomizationFactor(opts.randomizationFactor||.5);this.backoff=new Backoff({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()});this.timeout(null==opts.timeout?2e4:opts.timeout);this.readyState="closed";this.uri=uri;this.connecting=[];this.lastPing=null;this.encoding=false;this.packetBuffer=[];this.encoder=new parser.Encoder;this.decoder=new parser.Decoder;this.autoConnect=opts.autoConnect!==false;if(this.autoConnect)this.open()}Manager.prototype.emitAll=function(){this.emit.apply(this,arguments);for(var nsp in this.nsps){if(has.call(this.nsps,nsp)){this.nsps[nsp].emit.apply(this.nsps[nsp],arguments)}}};Manager.prototype.updateSocketIds=function(){for(var nsp in this.nsps){if(has.call(this.nsps,nsp)){this.nsps[nsp].id=this.engine.id}}};Emitter(Manager.prototype);Manager.prototype.reconnection=function(v){if(!arguments.length)return this._reconnection;this._reconnection=!!v;return this};Manager.prototype.reconnectionAttempts=function(v){if(!arguments.length)return this._reconnectionAttempts;this._reconnectionAttempts=v;return this};Manager.prototype.reconnectionDelay=function(v){if(!arguments.length)return this._reconnectionDelay;this._reconnectionDelay=v;this.backoff&&this.backoff.setMin(v);return this};Manager.prototype.randomizationFactor=function(v){if(!arguments.length)return this._randomizationFactor;this._randomizationFactor=v;this.backoff&&this.backoff.setJitter(v);return this};Manager.prototype.reconnectionDelayMax=function(v){if(!arguments.length)return this._reconnectionDelayMax;this._reconnectionDelayMax=v;this.backoff&&this.backoff.setMax(v);return this};Manager.prototype.timeout=function(v){if(!arguments.length)return this._timeout;this._timeout=v;return this};Manager.prototype.maybeReconnectOnOpen=function(){if(!this.reconnecting&&this._reconnection&&this.backoff.attempts===0){this.reconnect()}};Manager.prototype.open=Manager.prototype.connect=function(fn){debug("readyState %s",this.readyState);if(~this.readyState.indexOf("open"))return this;debug("opening %s",this.uri);this.engine=eio(this.uri,this.opts);var socket=this.engine;var self=this;this.readyState="opening";this.skipReconnect=false;var openSub=on(socket,"open",function(){self.onopen();fn&&fn()});var errorSub=on(socket,"error",function(data){debug("connect_error");self.cleanup();self.readyState="closed";self.emitAll("connect_error",data);if(fn){var err=new Error("Connection error");err.data=data;fn(err)}else{self.maybeReconnectOnOpen()}});if(false!==this._timeout){var timeout=this._timeout;debug("connect attempt will timeout after %d",timeout);var timer=setTimeout(function(){debug("connect attempt timed out after %d",timeout);openSub.destroy();socket.close();socket.emit("error","timeout");self.emitAll("connect_timeout",timeout)},timeout);this.subs.push({destroy:function(){clearTimeout(timer)}})}this.subs.push(openSub);this.subs.push(errorSub);return this};Manager.prototype.onopen=function(){debug("open");this.cleanup();this.readyState="open";this.emit("open");var socket=this.engine;this.subs.push(on(socket,"data",bind(this,"ondata")));this.subs.push(on(socket,"ping",bind(this,"onping")));this.subs.push(on(socket,"pong",bind(this,"onpong")));this.subs.push(on(socket,"error",bind(this,"onerror")));this.subs.push(on(socket,"close",bind(this,"onclose")));this.subs.push(on(this.decoder,"decoded",bind(this,"ondecoded")))};Manager.prototype.onping=function(){this.lastPing=new Date;this.emitAll("ping")};Manager.prototype.onpong=function(){this.emitAll("pong",new Date-this.lastPing)};Manager.prototype.ondata=function(data){this.decoder.add(data)};Manager.prototype.ondecoded=function(packet){this.emit("packet",packet)};Manager.prototype.onerror=function(err){debug("error",err);this.emitAll("error",err)};Manager.prototype.socket=function(nsp){var socket=this.nsps[nsp];if(!socket){socket=new Socket(this,nsp);this.nsps[nsp]=socket;var self=this;socket.on("connecting",onConnecting);
socket.on("connect",function(){socket.id=self.engine.id});if(this.autoConnect){onConnecting()}}function onConnecting(){if(!~indexOf(self.connecting,socket)){self.connecting.push(socket)}}return socket};Manager.prototype.destroy=function(socket){var index=indexOf(this.connecting,socket);if(~index)this.connecting.splice(index,1);if(this.connecting.length)return;this.close()};Manager.prototype.packet=function(packet){debug("writing packet %j",packet);var self=this;if(!self.encoding){self.encoding=true;this.encoder.encode(packet,function(encodedPackets){for(var i=0;i<encodedPackets.length;i++){self.engine.write(encodedPackets[i],packet.options)}self.encoding=false;self.processPacketQueue()})}else{self.packetBuffer.push(packet)}};Manager.prototype.processPacketQueue=function(){if(this.packetBuffer.length>0&&!this.encoding){var pack=this.packetBuffer.shift();this.packet(pack)}};Manager.prototype.cleanup=function(){debug("cleanup");var sub;while(sub=this.subs.shift())sub.destroy();this.packetBuffer=[];this.encoding=false;this.lastPing=null;this.decoder.destroy()};Manager.prototype.close=Manager.prototype.disconnect=function(){debug("disconnect");this.skipReconnect=true;this.reconnecting=false;if("opening"==this.readyState){this.cleanup()}this.backoff.reset();this.readyState="closed";if(this.engine)this.engine.close()};Manager.prototype.onclose=function(reason){debug("onclose");this.cleanup();this.backoff.reset();this.readyState="closed";this.emit("close",reason);if(this._reconnection&&!this.skipReconnect){this.reconnect()}};Manager.prototype.reconnect=function(){if(this.reconnecting||this.skipReconnect)return this;var self=this;if(this.backoff.attempts>=this._reconnectionAttempts){debug("reconnect failed");this.backoff.reset();this.emitAll("reconnect_failed");this.reconnecting=false}else{var delay=this.backoff.duration();debug("will wait %dms before reconnect attempt",delay);this.reconnecting=true;var timer=setTimeout(function(){if(self.skipReconnect)return;debug("attempting reconnect");self.emitAll("reconnect_attempt",self.backoff.attempts);self.emitAll("reconnecting",self.backoff.attempts);if(self.skipReconnect)return;self.open(function(err){if(err){debug("reconnect attempt error");self.reconnecting=false;self.reconnect();self.emitAll("reconnect_error",err.data)}else{debug("reconnect success");self.onreconnect()}})},delay);this.subs.push({destroy:function(){clearTimeout(timer)}})}};Manager.prototype.onreconnect=function(){var attempt=this.backoff.attempts;this.reconnecting=false;this.backoff.reset();this.updateSocketIds();this.emitAll("reconnect",attempt)}},{"./on":33,"./socket":34,backo2:36,"component-bind":37,"component-emitter":38,debug:39,"engine.io-client":1,indexof:42,"socket.io-parser":47}],33:[function(_dereq_,module,exports){module.exports=on;function on(obj,ev,fn){obj.on(ev,fn);return{destroy:function(){obj.removeListener(ev,fn)}}}},{}],34:[function(_dereq_,module,exports){var parser=_dereq_("socket.io-parser");var Emitter=_dereq_("component-emitter");var toArray=_dereq_("to-array");var on=_dereq_("./on");var bind=_dereq_("component-bind");var debug=_dereq_("debug")("socket.io-client:socket");var hasBin=_dereq_("has-binary");module.exports=exports=Socket;var events={connect:1,connect_error:1,connect_timeout:1,connecting:1,disconnect:1,error:1,reconnect:1,reconnect_attempt:1,reconnect_failed:1,reconnect_error:1,reconnecting:1,ping:1,pong:1};var emit=Emitter.prototype.emit;function Socket(io,nsp){this.io=io;this.nsp=nsp;this.json=this;this.ids=0;this.acks={};this.receiveBuffer=[];this.sendBuffer=[];this.connected=false;this.disconnected=true;if(this.io.autoConnect)this.open()}Emitter(Socket.prototype);Socket.prototype.subEvents=function(){if(this.subs)return;var io=this.io;this.subs=[on(io,"open",bind(this,"onopen")),on(io,"packet",bind(this,"onpacket")),on(io,"close",bind(this,"onclose"))]};Socket.prototype.open=Socket.prototype.connect=function(){if(this.connected)return this;this.subEvents();this.io.open();if("open"==this.io.readyState)this.onopen();this.emit("connecting");return this};Socket.prototype.send=function(){var args=toArray(arguments);args.unshift("message");this.emit.apply(this,args);return this};Socket.prototype.emit=function(ev){if(events.hasOwnProperty(ev)){emit.apply(this,arguments);return this}var args=toArray(arguments);var parserType=parser.EVENT;if(hasBin(args)){parserType=parser.BINARY_EVENT}var packet={type:parserType,data:args};packet.options={};packet.options.compress=!this.flags||false!==this.flags.compress;if("function"==typeof args[args.length-1]){debug("emitting packet with ack id %d",this.ids);this.acks[this.ids]=args.pop();packet.id=this.ids++}if(this.connected){this.packet(packet)}else{this.sendBuffer.push(packet)}delete this.flags;return this};Socket.prototype.packet=function(packet){packet.nsp=this.nsp;this.io.packet(packet)};Socket.prototype.onopen=function(){debug("transport is open - connecting");if("/"!=this.nsp){this.packet({type:parser.CONNECT})}};Socket.prototype.onclose=function(reason){debug("close (%s)",reason);this.connected=false;this.disconnected=true;delete this.id;this.emit("disconnect",reason)};Socket.prototype.onpacket=function(packet){if(packet.nsp!=this.nsp)return;switch(packet.type){case parser.CONNECT:this.onconnect();break;case parser.EVENT:this.onevent(packet);break;case parser.BINARY_EVENT:this.onevent(packet);break;case parser.ACK:this.onack(packet);break;case parser.BINARY_ACK:this.onack(packet);break;case parser.DISCONNECT:this.ondisconnect();break;case parser.ERROR:this.emit("error",packet.data);break}};Socket.prototype.onevent=function(packet){var args=packet.data||[];debug("emitting event %j",args);if(null!=packet.id){debug("attaching ack callback to event");args.push(this.ack(packet.id))}if(this.connected){emit.apply(this,args)}else{this.receiveBuffer.push(args)}};Socket.prototype.ack=function(id){var self=this;var sent=false;return function(){if(sent)return;sent=true;var args=toArray(arguments);debug("sending ack %j",args);var type=hasBin(args)?parser.BINARY_ACK:parser.ACK;self.packet({type:type,id:id,data:args})}};Socket.prototype.onack=function(packet){var ack=this.acks[packet.id];if("function"==typeof ack){debug("calling ack %s with %j",packet.id,packet.data);ack.apply(this,packet.data);delete this.acks[packet.id]}else{debug("bad ack %s",packet.id)}};Socket.prototype.onconnect=function(){this.connected=true;this.disconnected=false;this.emit("connect");this.emitBuffered()};Socket.prototype.emitBuffered=function(){var i;for(i=0;i<this.receiveBuffer.length;i++){emit.apply(this,this.receiveBuffer[i])}this.receiveBuffer=[];for(i=0;i<this.sendBuffer.length;i++){this.packet(this.sendBuffer[i])}this.sendBuffer=[]};Socket.prototype.ondisconnect=function(){debug("server disconnect (%s)",this.nsp);this.destroy();this.onclose("io server disconnect")};Socket.prototype.destroy=function(){if(this.subs){for(var i=0;i<this.subs.length;i++){this.subs[i].destroy()}this.subs=null}this.io.destroy(this)};Socket.prototype.close=Socket.prototype.disconnect=function(){if(this.connected){debug("performing disconnect (%s)",this.nsp);this.packet({type:parser.DISCONNECT})}this.destroy();if(this.connected){this.onclose("io client disconnect")}return this};Socket.prototype.compress=function(compress){this.flags=this.flags||{};this.flags.compress=compress;return this}},{"./on":33,"component-bind":37,"component-emitter":38,debug:39,"has-binary":41,"socket.io-parser":47,"to-array":51}],35:[function(_dereq_,module,exports){(function(global){var parseuri=_dereq_("parseuri");var debug=_dereq_("debug")("socket.io-client:url");module.exports=url;function url(uri,loc){var obj=uri;var loc=loc||global.location;if(null==uri)uri=loc.protocol+"//"+loc.host;if("string"==typeof uri){if("/"==uri.charAt(0)){if("/"==uri.charAt(1)){uri=loc.protocol+uri}else{uri=loc.host+uri}}if(!/^(https?|wss?):\/\//.test(uri)){debug("protocol-less url %s",uri);if("undefined"!=typeof loc){uri=loc.protocol+"//"+uri}else{uri="https://"+uri}}debug("parse %s",uri);obj=parseuri(uri)}if(!obj.port){if(/^(http|ws)$/.test(obj.protocol)){obj.port="80"}else if(/^(http|ws)s$/.test(obj.protocol)){obj.port="443"}}obj.path=obj.path||"/";var ipv6=obj.host.indexOf(":")!==-1;var host=ipv6?"["+obj.host+"]":obj.host;obj.id=obj.protocol+"://"+host+":"+obj.port;obj.href=obj.protocol+"://"+host+(loc&&loc.port==obj.port?"":":"+obj.port);return obj}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{debug:39,parseuri:45}],36:[function(_dereq_,module,exports){module.exports=Backoff;function Backoff(opts){opts=opts||{};this.ms=opts.min||100;this.max=opts.max||1e4;this.factor=opts.factor||2;this.jitter=opts.jitter>0&&opts.jitter<=1?opts.jitter:0;this.attempts=0}Backoff.prototype.duration=function(){var ms=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var rand=Math.random();var deviation=Math.floor(rand*this.jitter*ms);ms=(Math.floor(rand*10)&1)==0?ms-deviation:ms+deviation}return Math.min(ms,this.max)|0};Backoff.prototype.reset=function(){this.attempts=0};Backoff.prototype.setMin=function(min){this.ms=min};Backoff.prototype.setMax=function(max){this.max=max};Backoff.prototype.setJitter=function(jitter){this.jitter=jitter}},{}],37:[function(_dereq_,module,exports){var slice=[].slice;module.exports=function(obj,fn){if("string"==typeof fn)fn=obj[fn];if("function"!=typeof fn)throw new Error("bind() requires a function");var args=slice.call(arguments,2);return function(){return fn.apply(obj,args.concat(slice.call(arguments)))}}},{}],38:[function(_dereq_,module,exports){module.exports=Emitter;function Emitter(obj){if(obj)return mixin(obj)}function mixin(obj){for(var key in Emitter.prototype){obj[key]=Emitter.prototype[key]}return obj}Emitter.prototype.on=Emitter.prototype.addEventListener=function(event,fn){this._callbacks=this._callbacks||{};(this._callbacks["$"+event]=this._callbacks["$"+event]||[]).push(fn);return this};Emitter.prototype.once=function(event,fn){function on(){this.off(event,on);fn.apply(this,arguments)}on.fn=fn;this.on(event,on);return this};Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(event,fn){this._callbacks=this._callbacks||{};if(0==arguments.length){this._callbacks={};return this}var callbacks=this._callbacks["$"+event];if(!callbacks)return this;if(1==arguments.length){delete this._callbacks["$"+event];return this}var cb;for(var i=0;i<callbacks.length;i++){cb=callbacks[i];if(cb===fn||cb.fn===fn){callbacks.splice(i,1);break}}return this};Emitter.prototype.emit=function(event){this._callbacks=this._callbacks||{};var args=[].slice.call(arguments,1),callbacks=this._callbacks["$"+event];if(callbacks){callbacks=callbacks.slice(0);for(var i=0,len=callbacks.length;i<len;++i){callbacks[i].apply(this,args)}}return this};Emitter.prototype.listeners=function(event){this._callbacks=this._callbacks||{};return this._callbacks["$"+event]||[]};Emitter.prototype.hasListeners=function(event){return!!this.listeners(event).length}},{}],39:[function(_dereq_,module,exports){arguments[4][17][0].apply(exports,arguments)},{"./debug":40,dup:17}],40:[function(_dereq_,module,exports){arguments[4][18][0].apply(exports,arguments)},{dup:18,ms:44}],41:[function(_dereq_,module,exports){(function(global){var isArray=_dereq_("isarray");module.exports=hasBinary;function hasBinary(data){function _hasBinary(obj){if(!obj)return false;if(global.Buffer&&global.Buffer.isBuffer&&global.Buffer.isBuffer(obj)||global.ArrayBuffer&&obj instanceof ArrayBuffer||global.Blob&&obj instanceof Blob||global.File&&obj instanceof File){return true}if(isArray(obj)){for(var i=0;i<obj.length;i++){if(_hasBinary(obj[i])){return true}}}else if(obj&&"object"==typeof obj){if(obj.toJSON&&"function"==typeof obj.toJSON){obj=obj.toJSON()}for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key)&&_hasBinary(obj[key])){return true}}}return false}return _hasBinary(data)}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{isarray:43}],42:[function(_dereq_,module,exports){arguments[4][23][0].apply(exports,arguments)},{dup:23}],43:[function(_dereq_,module,exports){arguments[4][24][0].apply(exports,arguments)},{dup:24}],44:[function(_dereq_,module,exports){arguments[4][25][0].apply(exports,arguments)},{dup:25}],45:[function(_dereq_,module,exports){arguments[4][28][0].apply(exports,arguments)},{dup:28}],46:[function(_dereq_,module,exports){(function(global){var isArray=_dereq_("isarray");var isBuf=_dereq_("./is-buffer");exports.deconstructPacket=function(packet){var buffers=[];var packetData=packet.data;function _deconstructPacket(data){if(!data)return data;if(isBuf(data)){var placeholder={_placeholder:true,num:buffers.length};buffers.push(data);return placeholder}else if(isArray(data)){var newData=new Array(data.length);for(var i=0;i<data.length;i++){newData[i]=_deconstructPacket(data[i])}return newData}else if("object"==typeof data&&!(data instanceof Date)){var newData={};for(var key in data){newData[key]=_deconstructPacket(data[key])}return newData}return data}var pack=packet;pack.data=_deconstructPacket(packetData);pack.attachments=buffers.length;return{packet:pack,buffers:buffers}};exports.reconstructPacket=function(packet,buffers){var curPlaceHolder=0;function _reconstructPacket(data){if(data&&data._placeholder){var buf=buffers[data.num];return buf}else if(isArray(data)){for(var i=0;i<data.length;i++){data[i]=_reconstructPacket(data[i])}return data}else if(data&&"object"==typeof data){for(var key in data){data[key]=_reconstructPacket(data[key])}return data}return data}packet.data=_reconstructPacket(packet.data);packet.attachments=undefined;return packet};exports.removeBlobs=function(data,callback){function _removeBlobs(obj,curKey,containingObject){if(!obj)return obj;if(global.Blob&&obj instanceof Blob||global.File&&obj instanceof File){pendingBlobs++;var fileReader=new FileReader;fileReader.onload=function(){if(containingObject){containingObject[curKey]=this.result}else{bloblessData=this.result}if(!--pendingBlobs){callback(bloblessData)}};fileReader.readAsArrayBuffer(obj)}else if(isArray(obj)){for(var i=0;i<obj.length;i++){_removeBlobs(obj[i],i,obj)}}else if(obj&&"object"==typeof obj&&!isBuf(obj)){for(var key in obj){_removeBlobs(obj[key],key,obj)}}}var pendingBlobs=0;var bloblessData=data;_removeBlobs(bloblessData);if(!pendingBlobs){callback(bloblessData)}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{"./is-buffer":48,isarray:43}],47:[function(_dereq_,module,exports){var debug=_dereq_("debug")("socket.io-parser");var json=_dereq_("json3");var isArray=_dereq_("isarray");var Emitter=_dereq_("component-emitter");var binary=_dereq_("./binary");var isBuf=_dereq_("./is-buffer");exports.protocol=4;exports.types=["CONNECT","DISCONNECT","EVENT","BINARY_EVENT","ACK","BINARY_ACK","ERROR"];exports.CONNECT=0;exports.DISCONNECT=1;exports.EVENT=2;exports.ACK=3;exports.ERROR=4;exports.BINARY_EVENT=5;exports.BINARY_ACK=6;exports.Encoder=Encoder;exports.Decoder=Decoder;function Encoder(){}Encoder.prototype.encode=function(obj,callback){debug("encoding packet %j",obj);if(exports.BINARY_EVENT==obj.type||exports.BINARY_ACK==obj.type){encodeAsBinary(obj,callback)}else{var encoding=encodeAsString(obj);callback([encoding])}};function encodeAsString(obj){var str="";var nsp=false;str+=obj.type;if(exports.BINARY_EVENT==obj.type||exports.BINARY_ACK==obj.type){str+=obj.attachments;str+="-"}if(obj.nsp&&"/"!=obj.nsp){nsp=true;str+=obj.nsp}if(null!=obj.id){if(nsp){str+=",";nsp=false}str+=obj.id}if(null!=obj.data){if(nsp)str+=",";str+=json.stringify(obj.data)}debug("encoded %j as %s",obj,str);return str}function encodeAsBinary(obj,callback){function writeEncoding(bloblessData){var deconstruction=binary.deconstructPacket(bloblessData);var pack=encodeAsString(deconstruction.packet);var buffers=deconstruction.buffers;buffers.unshift(pack);callback(buffers)}binary.removeBlobs(obj,writeEncoding)}function Decoder(){this.reconstructor=null}Emitter(Decoder.prototype);Decoder.prototype.add=function(obj){var packet;if("string"==typeof obj){packet=decodeString(obj);if(exports.BINARY_EVENT==packet.type||exports.BINARY_ACK==packet.type){this.reconstructor=new BinaryReconstructor(packet);if(this.reconstructor.reconPack.attachments===0){this.emit("decoded",packet)}}else{this.emit("decoded",packet)}}else if(isBuf(obj)||obj.base64){if(!this.reconstructor){throw new Error("got binary data when not reconstructing a packet")}else{packet=this.reconstructor.takeBinaryData(obj);if(packet){this.reconstructor=null;this.emit("decoded",packet)}}}else{throw new Error("Unknown type: "+obj)}};function decodeString(str){var p={};var i=0;p.type=Number(str.charAt(0));if(null==exports.types[p.type])return error();if(exports.BINARY_EVENT==p.type||exports.BINARY_ACK==p.type){var buf="";while(str.charAt(++i)!="-"){buf+=str.charAt(i);if(i==str.length)break}if(buf!=Number(buf)||str.charAt(i)!="-"){throw new Error("Illegal attachments")}p.attachments=Number(buf)}if("/"==str.charAt(i+1)){p.nsp="";while(++i){var c=str.charAt(i);if(","==c)break;p.nsp+=c;if(i==str.length)break}}else{p.nsp="/"}var next=str.charAt(i+1);if(""!==next&&Number(next)==next){p.id="";while(++i){var c=str.charAt(i);if(null==c||Number(c)!=c){--i;break}p.id+=str.charAt(i);if(i==str.length)break}p.id=Number(p.id)}if(str.charAt(++i)){try{p.data=json.parse(str.substr(i))}catch(e){return error()}}debug("decoded %s as %j",str,p);return p}Decoder.prototype.destroy=function(){if(this.reconstructor){this.reconstructor.finishedReconstruction()}};function BinaryReconstructor(packet){this.reconPack=packet;this.buffers=[]}BinaryReconstructor.prototype.takeBinaryData=function(binData){this.buffers.push(binData);if(this.buffers.length==this.reconPack.attachments){var packet=binary.reconstructPacket(this.reconPack,this.buffers);this.finishedReconstruction();return packet}return null};BinaryReconstructor.prototype.finishedReconstruction=function(){this.reconPack=null;this.buffers=[]};function error(data){return{type:exports.ERROR,data:"parser error"}}},{"./binary":46,"./is-buffer":48,"component-emitter":49,debug:39,isarray:43,json3:50}],48:[function(_dereq_,module,exports){(function(global){module.exports=isBuf;function isBuf(obj){return global.Buffer&&global.Buffer.isBuffer(obj)||global.ArrayBuffer&&obj instanceof ArrayBuffer}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{}],49:[function(_dereq_,module,exports){arguments[4][15][0].apply(exports,arguments)},{dup:15}],50:[function(_dereq_,module,exports){(function(global){(function(){var isLoader=typeof define==="function"&&define.amd;var objectTypes={"function":true,object:true};var freeExports=objectTypes[typeof exports]&&exports&&!exports.nodeType&&exports;var root=objectTypes[typeof window]&&window||this,freeGlobal=freeExports&&objectTypes[typeof module]&&module&&!module.nodeType&&typeof global=="object"&&global;if(freeGlobal&&(freeGlobal["global"]===freeGlobal||freeGlobal["window"]===freeGlobal||freeGlobal["self"]===freeGlobal)){root=freeGlobal}function runInContext(context,exports){context||(context=root["Object"]());exports||(exports=root["Object"]());var Number=context["Number"]||root["Number"],String=context["String"]||root["String"],Object=context["Object"]||root["Object"],Date=context["Date"]||root["Date"],SyntaxError=context["SyntaxError"]||root["SyntaxError"],TypeError=context["TypeError"]||root["TypeError"],Math=context["Math"]||root["Math"],nativeJSON=context["JSON"]||root["JSON"];if(typeof nativeJSON=="object"&&nativeJSON){exports.stringify=nativeJSON.stringify;exports.parse=nativeJSON.parse}var objectProto=Object.prototype,getClass=objectProto.toString,isProperty,forEach,undef;var isExtended=new Date(-0xc782b5b800cec);try{isExtended=isExtended.getUTCFullYear()==-109252&&isExtended.getUTCMonth()===0&&isExtended.getUTCDate()===1&&isExtended.getUTCHours()==10&&isExtended.getUTCMinutes()==37&&isExtended.getUTCSeconds()==6&&isExtended.getUTCMilliseconds()==708}catch(exception){}function has(name){if(has[name]!==undef){return has[name]}var isSupported;if(name=="bug-string-char-index"){isSupported="a"[0]!="a"}else if(name=="json"){isSupported=has("json-stringify")&&has("json-parse")}else{var value,serialized='{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';if(name=="json-stringify"){var stringify=exports.stringify,stringifySupported=typeof stringify=="function"&&isExtended;if(stringifySupported){(value=function(){return 1}).toJSON=value;try{stringifySupported=stringify(0)==="0"&&stringify(new Number)==="0"&&stringify(new String)=='""'&&stringify(getClass)===undef&&stringify(undef)===undef&&stringify()===undef&&stringify(value)==="1"&&stringify([value])=="[1]"&&stringify([undef])=="[null]"&&stringify(null)=="null"&&stringify([undef,getClass,null])=="[null,null,null]"&&stringify({a:[value,true,false,null,"\x00\b\n\f\r "]})==serialized&&stringify(null,value)==="1"&&stringify([1,2],null,1)=="[\n 1,\n 2\n]"&&stringify(new Date(-864e13))=='"-271821-04-20T00:00:00.000Z"'&&stringify(new Date(864e13))=='"+275760-09-13T00:00:00.000Z"'&&stringify(new Date(-621987552e5))=='"-000001-01-01T00:00:00.000Z"'&&stringify(new Date(-1))=='"1969-12-31T23:59:59.999Z"'}catch(exception){stringifySupported=false}}isSupported=stringifySupported}if(name=="json-parse"){var parse=exports.parse;if(typeof parse=="function"){try{if(parse("0")===0&&!parse(false)){value=parse(serialized);var parseSupported=value["a"].length==5&&value["a"][0]===1;if(parseSupported){try{parseSupported=!parse('" "')}catch(exception){}if(parseSupported){try{parseSupported=parse("01")!==1}catch(exception){}}if(parseSupported){try{parseSupported=parse("1.")!==1}catch(exception){}}}}}catch(exception){parseSupported=false}}isSupported=parseSupported}}return has[name]=!!isSupported}if(!has("json")){var functionClass="[object Function]",dateClass="[object Date]",numberClass="[object Number]",stringClass="[object String]",arrayClass="[object Array]",booleanClass="[object Boolean]";var charIndexBuggy=has("bug-string-char-index");if(!isExtended){var floor=Math.floor;var Months=[0,31,59,90,120,151,181,212,243,273,304,334];var getDay=function(year,month){return Months[month]+365*(year-1970)+floor((year-1969+(month=+(month>1)))/4)-floor((year-1901+month)/100)+floor((year-1601+month)/400)}}if(!(isProperty=objectProto.hasOwnProperty)){isProperty=function(property){var members={},constructor;if((members.__proto__=null,members.__proto__={toString:1},members).toString!=getClass){isProperty=function(property){var original=this.__proto__,result=property in(this.__proto__=null,this);this.__proto__=original;return result}}else{constructor=members.constructor;isProperty=function(property){var parent=(this.constructor||constructor).prototype;return property in this&&!(property in parent&&this[property]===parent[property])}}members=null;return isProperty.call(this,property)}}forEach=function(object,callback){var size=0,Properties,members,property;(Properties=function(){this.valueOf=0}).prototype.valueOf=0;members=new Properties;for(property in members){if(isProperty.call(members,property)){size++}}Properties=members=null;if(!size){members=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"];forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,length;var hasProperty=!isFunction&&typeof object.constructor!="function"&&objectTypes[typeof object.hasOwnProperty]&&object.hasOwnProperty||isProperty;for(property in object){if(!(isFunction&&property=="prototype")&&hasProperty.call(object,property)){callback(property)}}for(length=members.length;property=members[--length];hasProperty.call(object,property)&&callback(property));}}else if(size==2){forEach=function(object,callback){var members={},isFunction=getClass.call(object)==functionClass,property;for(property in object){if(!(isFunction&&property=="prototype")&&!isProperty.call(members,property)&&(members[property]=1)&&isProperty.call(object,property)){callback(property)}}}}else{forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,isConstructor;for(property in object){if(!(isFunction&&property=="prototype")&&isProperty.call(object,property)&&!(isConstructor=property==="constructor")){callback(property)}}if(isConstructor||isProperty.call(object,property="constructor")){callback(property)}}}return forEach(object,callback)};if(!has("json-stringify")){var Escapes={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"};var leadingZeroes="000000";var toPaddedString=function(width,value){return(leadingZeroes+(value||0)).slice(-width)};var unicodePrefix="\\u00";var quote=function(value){var result='"',index=0,length=value.length,useCharIndex=!charIndexBuggy||length>10;var symbols=useCharIndex&&(charIndexBuggy?value.split(""):value);for(;index<length;index++){var charCode=value.charCodeAt(index);switch(charCode){case 8:case 9:case 10:case 12:case 13:case 34:case 92:result+=Escapes[charCode];break;default:if(charCode<32){result+=unicodePrefix+toPaddedString(2,charCode.toString(16));break}result+=useCharIndex?symbols[index]:value.charAt(index)}}return result+'"'};var serialize=function(property,object,callback,properties,whitespace,indentation,stack){var value,className,year,month,date,time,hours,minutes,seconds,milliseconds,results,element,index,length,prefix,result;try{value=object[property]}catch(exception){}if(typeof value=="object"&&value){className=getClass.call(value);if(className==dateClass&&!isProperty.call(value,"toJSON")){if(value>-1/0&&value<1/0){if(getDay){date=floor(value/864e5);for(year=floor(date/365.2425)+1970-1;getDay(year+1,0)<=date;year++);for(month=floor((date-getDay(year,0))/30.42);getDay(year,month+1)<=date;month++);date=1+date-getDay(year,month);time=(value%864e5+864e5)%864e5;hours=floor(time/36e5)%24;minutes=floor(time/6e4)%60;seconds=floor(time/1e3)%60;milliseconds=time%1e3}else{year=value.getUTCFullYear();month=value.getUTCMonth();date=value.getUTCDate();hours=value.getUTCHours();minutes=value.getUTCMinutes();seconds=value.getUTCSeconds();milliseconds=value.getUTCMilliseconds()}value=(year<=0||year>=1e4?(year<0?"-":"+")+toPaddedString(6,year<0?-year:year):toPaddedString(4,year))+"-"+toPaddedString(2,month+1)+"-"+toPaddedString(2,date)+"T"+toPaddedString(2,hours)+":"+toPaddedString(2,minutes)+":"+toPaddedString(2,seconds)+"."+toPaddedString(3,milliseconds)+"Z"}else{value=null}}else if(typeof value.toJSON=="function"&&(className!=numberClass&&className!=stringClass&&className!=arrayClass||isProperty.call(value,"toJSON"))){value=value.toJSON(property)}}if(callback){value=callback.call(object,property,value)}if(value===null){return"null"}className=getClass.call(value);if(className==booleanClass){return""+value}else if(className==numberClass){return value>-1/0&&value<1/0?""+value:"null"}else if(className==stringClass){return quote(""+value)}if(typeof value=="object"){for(length=stack.length;length--;){if(stack[length]===value){throw TypeError()}}stack.push(value);results=[];prefix=indentation;indentation+=whitespace;if(className==arrayClass){for(index=0,length=value.length;index<length;index++){element=serialize(index,value,callback,properties,whitespace,indentation,stack);results.push(element===undef?"null":element)}result=results.length?whitespace?"[\n"+indentation+results.join(",\n"+indentation)+"\n"+prefix+"]":"["+results.join(",")+"]":"[]"}else{forEach(properties||value,function(property){var element=serialize(property,value,callback,properties,whitespace,indentation,stack);if(element!==undef){results.push(quote(property)+":"+(whitespace?" ":"")+element)}});result=results.length?whitespace?"{\n"+indentation+results.join(",\n"+indentation)+"\n"+prefix+"}":"{"+results.join(",")+"}":"{}"}stack.pop();return result}};exports.stringify=function(source,filter,width){var whitespace,callback,properties,className;if(objectTypes[typeof filter]&&filter){if((className=getClass.call(filter))==functionClass){callback=filter}else if(className==arrayClass){properties={};for(var index=0,length=filter.length,value;index<length;value=filter[index++],(className=getClass.call(value),className==stringClass||className==numberClass)&&(properties[value]=1));}}if(width){if((className=getClass.call(width))==numberClass){if((width-=width%1)>0){for(whitespace="",width>10&&(width=10);whitespace.length<width;whitespace+=" ");}}else if(className==stringClass){whitespace=width.length<=10?width:width.slice(0,10)}}return serialize("",(value={},value[""]=source,value),callback,properties,whitespace,"",[])}}if(!has("json-parse")){var fromCharCode=String.fromCharCode;var Unescapes={92:"\\",34:'"',47:"/",98:"\b",116:" ",110:"\n",102:"\f",114:"\r"};var Index,Source;var abort=function(){Index=Source=null;throw SyntaxError()};var lex=function(){var source=Source,length=source.length,value,begin,position,isSigned,charCode;while(Index<length){charCode=source.charCodeAt(Index);switch(charCode){case 9:case 10:case 13:case 32:Index++;break;case 123:case 125:case 91:case 93:case 58:case 44:value=charIndexBuggy?source.charAt(Index):source[Index];Index++;return value;case 34:for(value="@",Index++;Index<length;){charCode=source.charCodeAt(Index);if(charCode<32){abort()}else if(charCode==92){charCode=source.charCodeAt(++Index);switch(charCode){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:value+=Unescapes[charCode];Index++;break;case 117:begin=++Index;for(position=Index+4;Index<position;Index++){charCode=source.charCodeAt(Index);if(!(charCode>=48&&charCode<=57||charCode>=97&&charCode<=102||charCode>=65&&charCode<=70)){abort()}}value+=fromCharCode("0x"+source.slice(begin,Index));break;default:abort()}}else{if(charCode==34){break}charCode=source.charCodeAt(Index);begin=Index;while(charCode>=32&&charCode!=92&&charCode!=34){charCode=source.charCodeAt(++Index)}value+=source.slice(begin,Index)}}if(source.charCodeAt(Index)==34){Index++;return value}abort();default:begin=Index;if(charCode==45){isSigned=true;charCode=source.charCodeAt(++Index)}if(charCode>=48&&charCode<=57){if(charCode==48&&(charCode=source.charCodeAt(Index+1),charCode>=48&&charCode<=57)){abort()}isSigned=false;for(;Index<length&&(charCode=source.charCodeAt(Index),charCode>=48&&charCode<=57);Index++);if(source.charCodeAt(Index)==46){position=++Index;for(;position<length&&(charCode=source.charCodeAt(position),charCode>=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}charCode=source.charCodeAt(Index);if(charCode==101||charCode==69){charCode=source.charCodeAt(++Index);if(charCode==43||charCode==45){Index++}for(position=Index;position<length&&(charCode=source.charCodeAt(position),charCode>=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}return+source.slice(begin,Index)}if(isSigned){abort()}if(source.slice(Index,Index+4)=="true"){Index+=4;return true}else if(source.slice(Index,Index+5)=="false"){Index+=5;return false}else if(source.slice(Index,Index+4)=="null"){Index+=4;return null}abort()}}return"$"};var get=function(value){var results,hasMembers;if(value=="$"){abort()}if(typeof value=="string"){if((charIndexBuggy?value.charAt(0):value[0])=="@"){return value.slice(1)}if(value=="["){results=[];for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="]"){break}if(hasMembers){if(value==","){value=lex();if(value=="]"){abort()}}else{abort()}}if(value==","){abort()}results.push(get(value))}return results}else if(value=="{"){results={};for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="}"){break}if(hasMembers){if(value==","){value=lex();if(value=="}"){abort()}}else{abort()}}if(value==","||typeof value!="string"||(charIndexBuggy?value.charAt(0):value[0])!="@"||lex()!=":"){abort()}results[value.slice(1)]=get(lex())
}return results}abort()}return value};var update=function(source,property,callback){var element=walk(source,property,callback);if(element===undef){delete source[property]}else{source[property]=element}};var walk=function(source,property,callback){var value=source[property],length;if(typeof value=="object"&&value){if(getClass.call(value)==arrayClass){for(length=value.length;length--;){update(value,length,callback)}}else{forEach(value,function(property){update(value,property,callback)})}}return callback.call(source,property,value)};exports.parse=function(source,callback){var result,value;Index=0;Source=""+source;result=get(lex());if(lex()!="$"){abort()}Index=Source=null;return callback&&getClass.call(callback)==functionClass?walk((value={},value[""]=result,value),"",callback):result}}}exports["runInContext"]=runInContext;return exports}if(freeExports&&!isLoader){runInContext(root,freeExports)}else{var nativeJSON=root.JSON,previousJSON=root["JSON3"],isRestored=false;var JSON3=runInContext(root,root["JSON3"]={noConflict:function(){if(!isRestored){isRestored=true;root.JSON=nativeJSON;root["JSON3"]=previousJSON;nativeJSON=previousJSON=null}return JSON3}});root.JSON={parse:JSON3.parse,stringify:JSON3.stringify}}if(isLoader){define(function(){return JSON3})}}).call(this)}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:{})},{}],51:[function(_dereq_,module,exports){module.exports=toArray;function toArray(list,index){var array=[];index=index||0;for(var i=index||0;i<list.length;i++){array[i-index]=list[i]}return array}},{}]},{},[31])(31)});
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/* eslint-env node */
'use strict';
// SDP helpers.
var SDPUtils = {};
// Generate an alphanumeric identifier for cname or mids.
// TODO: use UUIDs instead? https://gist.github.com/jed/982883
SDPUtils.generateIdentifier = function() {
return Math.random().toString(36).substr(2, 10);
};
// The RTCP CNAME used by all peerconnections from the same JS.
SDPUtils.localCName = SDPUtils.generateIdentifier();
// Splits SDP into lines, dealing with both CRLF and LF.
SDPUtils.splitLines = function(blob) {
return blob.trim().split('\n').map(function(line) {
return line.trim();
});
};
// Splits SDP into sessionpart and mediasections. Ensures CRLF.
SDPUtils.splitSections = function(blob) {
var parts = blob.split('\nm=');
return parts.map(function(part, index) {
return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
});
};
// Returns lines that start with a certain prefix.
SDPUtils.matchPrefix = function(blob, prefix) {
return SDPUtils.splitLines(blob).filter(function(line) {
return line.indexOf(prefix) === 0;
});
};
// Parses an ICE candidate line. Sample input:
// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
// rport 55996"
SDPUtils.parseCandidate = function(line) {
var parts;
// Parse both variants.
if (line.indexOf('a=candidate:') === 0) {
parts = line.substring(12).split(' ');
} else {
parts = line.substring(10).split(' ');
}
var candidate = {
foundation: parts[0],
component: parseInt(parts[1], 10),
protocol: parts[2].toLowerCase(),
priority: parseInt(parts[3], 10),
ip: parts[4],
port: parseInt(parts[5], 10),
// skip parts[6] == 'typ'
type: parts[7]
};
for (var i = 8; i < parts.length; i += 2) {
switch (parts[i]) {
case 'raddr':
candidate.relatedAddress = parts[i + 1];
break;
case 'rport':
candidate.relatedPort = parseInt(parts[i + 1], 10);
break;
case 'tcptype':
candidate.tcpType = parts[i + 1];
break;
default: // extension handling, in particular ufrag
candidate[parts[i]] = parts[i + 1];
break;
}
}
return candidate;
};
// Translates a candidate object into SDP candidate attribute.
SDPUtils.writeCandidate = function(candidate) {
var sdp = [];
sdp.push(candidate.foundation);
sdp.push(candidate.component);
sdp.push(candidate.protocol.toUpperCase());
sdp.push(candidate.priority);
sdp.push(candidate.ip);
sdp.push(candidate.port);
var type = candidate.type;
sdp.push('typ');
sdp.push(type);
if (type !== 'host' && candidate.relatedAddress &&
candidate.relatedPort) {
sdp.push('raddr');
sdp.push(candidate.relatedAddress); // was: relAddr
sdp.push('rport');
sdp.push(candidate.relatedPort); // was: relPort
}
if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
sdp.push('tcptype');
sdp.push(candidate.tcpType);
}
if (candidate.ufrag) {
sdp.push('ufrag');
sdp.push(candidate.ufrag);
}
return 'candidate:' + sdp.join(' ');
};
// Parses an ice-options line, returns an array of option tags.
// a=ice-options:foo bar
SDPUtils.parseIceOptions = function(line) {
return line.substr(14).split(' ');
}
// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
// a=rtpmap:111 opus/48000/2
SDPUtils.parseRtpMap = function(line) {
var parts = line.substr(9).split(' ');
var parsed = {
payloadType: parseInt(parts.shift(), 10) // was: id
};
parts = parts[0].split('/');
parsed.name = parts[0];
parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
// was: channels
parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
return parsed;
};
// Generate an a=rtpmap line from RTCRtpCodecCapability or
// RTCRtpCodecParameters.
SDPUtils.writeRtpMap = function(codec) {
var pt = codec.payloadType;
if (codec.preferredPayloadType !== undefined) {
pt = codec.preferredPayloadType;
}
return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
(codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\r\n';
};
// Parses an a=extmap line (headerextension from RFC 5285). Sample input:
// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
SDPUtils.parseExtmap = function(line) {
var parts = line.substr(9).split(' ');
return {
id: parseInt(parts[0], 10),
direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
uri: parts[1]
};
};
// Generates a=extmap line from RTCRtpHeaderExtensionParameters or
// RTCRtpHeaderExtension.
SDPUtils.writeExtmap = function(headerExtension) {
return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +
(headerExtension.direction && headerExtension.direction !== 'sendrecv'
? '/' + headerExtension.direction
: '') +
' ' + headerExtension.uri + '\r\n';
};
// Parses an ftmp line, returns dictionary. Sample input:
// a=fmtp:96 vbr=on;cng=on
// Also deals with vbr=on; cng=on
SDPUtils.parseFmtp = function(line) {
var parsed = {};
var kv;
var parts = line.substr(line.indexOf(' ') + 1).split(';');
for (var j = 0; j < parts.length; j++) {
kv = parts[j].trim().split('=');
parsed[kv[0].trim()] = kv[1];
}
return parsed;
};
// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
SDPUtils.writeFmtp = function(codec) {
var line = '';
var pt = codec.payloadType;
if (codec.preferredPayloadType !== undefined) {
pt = codec.preferredPayloadType;
}
if (codec.parameters && Object.keys(codec.parameters).length) {
var params = [];
Object.keys(codec.parameters).forEach(function(param) {
params.push(param + '=' + codec.parameters[param]);
});
line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
}
return line;
};
// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
// a=rtcp-fb:98 nack rpsi
SDPUtils.parseRtcpFb = function(line) {
var parts = line.substr(line.indexOf(' ') + 1).split(' ');
return {
type: parts.shift(),
parameter: parts.join(' ')
};
};
// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
SDPUtils.writeRtcpFb = function(codec) {
var lines = '';
var pt = codec.payloadType;
if (codec.preferredPayloadType !== undefined) {
pt = codec.preferredPayloadType;
}
if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
// FIXME: special handling for trr-int?
codec.rtcpFeedback.forEach(function(fb) {
lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +
(fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +
'\r\n';
});
}
return lines;
};
// Parses an RFC 5576 ssrc media attribute. Sample input:
// a=ssrc:3735928559 cname:something
SDPUtils.parseSsrcMedia = function(line) {
var sp = line.indexOf(' ');
var parts = {
ssrc: parseInt(line.substr(7, sp - 7), 10)
};
var colon = line.indexOf(':', sp);
if (colon > -1) {
parts.attribute = line.substr(sp + 1, colon - sp - 1);
parts.value = line.substr(colon + 1);
} else {
parts.attribute = line.substr(sp + 1);
}
return parts;
};
// Extracts the MID (RFC 5888) from a media section.
// returns the MID or undefined if no mid line was found.
SDPUtils.getMid = function(mediaSection) {
var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
if (mid) {
return mid.substr(6);
}
}
SDPUtils.parseFingerprint = function(line) {
var parts = line.substr(14).split(' ');
return {
algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
value: parts[1]
};
};
// Extracts DTLS parameters from SDP media section or sessionpart.
// FIXME: for consistency with other functions this should only
// get the fingerprint line as input. See also getIceParameters.
SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
'a=fingerprint:');
// Note: a=setup line is ignored since we use the 'auto' role.
// Note2: 'algorithm' is not case sensitive except in Edge.
return {
role: 'auto',
fingerprints: lines.map(SDPUtils.parseFingerprint)
};
};
// Serializes DTLS parameters to SDP.
SDPUtils.writeDtlsParameters = function(params, setupType) {
var sdp = 'a=setup:' + setupType + '\r\n';
params.fingerprints.forEach(function(fp) {
sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
});
return sdp;
};
// Parses ICE information from SDP media section or sessionpart.
// FIXME: for consistency with other functions this should only
// get the ice-ufrag and ice-pwd lines as input.
SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
var lines = SDPUtils.splitLines(mediaSection);
// Search in session part, too.
lines = lines.concat(SDPUtils.splitLines(sessionpart));
var iceParameters = {
usernameFragment: lines.filter(function(line) {
return line.indexOf('a=ice-ufrag:') === 0;
})[0].substr(12),
password: lines.filter(function(line) {
return line.indexOf('a=ice-pwd:') === 0;
})[0].substr(10)
};
return iceParameters;
};
// Serializes ICE parameters to SDP.
SDPUtils.writeIceParameters = function(params) {
return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
'a=ice-pwd:' + params.password + '\r\n';
};
// Parses the SDP media section and returns RTCRtpParameters.
SDPUtils.parseRtpParameters = function(mediaSection) {
var description = {
codecs: [],
headerExtensions: [],
fecMechanisms: [],
rtcp: []
};
var lines = SDPUtils.splitLines(mediaSection);
var mline = lines[0].split(' ');
for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
var pt = mline[i];
var rtpmapline = SDPUtils.matchPrefix(
mediaSection, 'a=rtpmap:' + pt + ' ')[0];
if (rtpmapline) {
var codec = SDPUtils.parseRtpMap(rtpmapline);
var fmtps = SDPUtils.matchPrefix(
mediaSection, 'a=fmtp:' + pt + ' ');
// Only the first a=fmtp:<pt> is considered.
codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
codec.rtcpFeedback = SDPUtils.matchPrefix(
mediaSection, 'a=rtcp-fb:' + pt + ' ')
.map(SDPUtils.parseRtcpFb);
description.codecs.push(codec);
// parse FEC mechanisms from rtpmap lines.
switch (codec.name.toUpperCase()) {
case 'RED':
case 'ULPFEC':
description.fecMechanisms.push(codec.name.toUpperCase());
break;
default: // only RED and ULPFEC are recognized as FEC mechanisms.
break;
}
}
}
SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {
description.headerExtensions.push(SDPUtils.parseExtmap(line));
});
// FIXME: parse rtcp.
return description;
};
// Generates parts of the SDP media section describing the capabilities /
// parameters.
SDPUtils.writeRtpDescription = function(kind, caps) {
var sdp = '';
// Build the mline.
sdp += 'm=' + kind + ' ';
sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
sdp += ' UDP/TLS/RTP/SAVPF ';
sdp += caps.codecs.map(function(codec) {
if (codec.preferredPayloadType !== undefined) {
return codec.preferredPayloadType;
}
return codec.payloadType;
}).join(' ') + '\r\n';
sdp += 'c=IN IP4 0.0.0.0\r\n';
sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
// Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
caps.codecs.forEach(function(codec) {
sdp += SDPUtils.writeRtpMap(codec);
sdp += SDPUtils.writeFmtp(codec);
sdp += SDPUtils.writeRtcpFb(codec);
});
var maxptime = 0;
caps.codecs.forEach(function(codec) {
if (codec.maxptime > maxptime) {
maxptime = codec.maxptime;
}
});
if (maxptime > 0) {
sdp += 'a=maxptime:' + maxptime + '\r\n';
}
sdp += 'a=rtcp-mux\r\n';
caps.headerExtensions.forEach(function(extension) {
sdp += SDPUtils.writeExtmap(extension);
});
// FIXME: write fecMechanisms.
return sdp;
};
// Parses the SDP media section and returns an array of
// RTCRtpEncodingParameters.
SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
var encodingParameters = [];
var description = SDPUtils.parseRtpParameters(mediaSection);
var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
// filter a=ssrc:... cname:, ignore PlanB-msid
var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
.map(function(line) {
return SDPUtils.parseSsrcMedia(line);
})
.filter(function(parts) {
return parts.attribute === 'cname';
});
var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
var secondarySsrc;
var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')
.map(function(line) {
var parts = line.split(' ');
parts.shift();
return parts.map(function(part) {
return parseInt(part, 10);
});
});
if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
secondarySsrc = flows[0][1];
}
description.codecs.forEach(function(codec) {
if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
var encParam = {
ssrc: primarySsrc,
codecPayloadType: parseInt(codec.parameters.apt, 10),
rtx: {
ssrc: secondarySsrc
}
};
encodingParameters.push(encParam);
if (hasRed) {
encParam = JSON.parse(JSON.stringify(encParam));
encParam.fec = {
ssrc: secondarySsrc,
mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
};
encodingParameters.push(encParam);
}
}
});
if (encodingParameters.length === 0 && primarySsrc) {
encodingParameters.push({
ssrc: primarySsrc
});
}
// we support both b=AS and b=TIAS but interpret AS as TIAS.
var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
if (bandwidth.length) {
if (bandwidth[0].indexOf('b=TIAS:') === 0) {
bandwidth = parseInt(bandwidth[0].substr(7), 10);
} else if (bandwidth[0].indexOf('b=AS:') === 0) {
// use formula from JSEP to convert b=AS to TIAS value.
bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95
- (50 * 40 * 8);
} else {
bandwidth = undefined;
}
encodingParameters.forEach(function(params) {
params.maxBitrate = bandwidth;
});
}
return encodingParameters;
};
// parses http://draft.ortc.org/#rtcrtcpparameters*
SDPUtils.parseRtcpParameters = function(mediaSection) {
var rtcpParameters = {};
var cname;
// Gets the first SSRC. Note that with RTX there might be multiple
// SSRCs.
var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
.map(function(line) {
return SDPUtils.parseSsrcMedia(line);
})
.filter(function(obj) {
return obj.attribute === 'cname';
})[0];
if (remoteSsrc) {
rtcpParameters.cname = remoteSsrc.value;
rtcpParameters.ssrc = remoteSsrc.ssrc;
}
// Edge uses the compound attribute instead of reducedSize
// compound is !reducedSize
var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
rtcpParameters.reducedSize = rsize.length > 0;
rtcpParameters.compound = rsize.length === 0;
// parses the rtcp-mux attrіbute.
// Note that Edge does not support unmuxed RTCP.
var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
rtcpParameters.mux = mux.length > 0;
return rtcpParameters;
};
// parses either a=msid: or a=ssrc:... msid lines and returns
// the id of the MediaStream and MediaStreamTrack.
SDPUtils.parseMsid = function(mediaSection) {
var parts;
var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
if (spec.length === 1) {
parts = spec[0].substr(7).split(' ');
return {stream: parts[0], track: parts[1]};
}
var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
.map(function(line) {
return SDPUtils.parseSsrcMedia(line);
})
.filter(function(parts) {
return parts.attribute === 'msid';
});
if (planB.length > 0) {
parts = planB[0].value.split(' ');
return {stream: parts[0], track: parts[1]};
}
};
// Generate a session ID for SDP.
// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
// recommends using a cryptographically random +ve 64-bit value
// but right now this should be acceptable and within the right range
SDPUtils.generateSessionId = function() {
return Math.random().toString().substr(2, 21);
};
// Write boilder plate for start of SDP
// sessId argument is optional - if not supplied it will
// be generated randomly
// sessVersion is optional and defaults to 2
SDPUtils.writeSessionBoilerplate = function(sessId, sessVer) {
var sessionId;
var version = sessVer !== undefined ? sessVer : 2;
if (sessId) {
sessionId = sessId;
} else {
sessionId = SDPUtils.generateSessionId();
}
// FIXME: sess-id should be an NTP timestamp.
return 'v=0\r\n' +
'o=thisisadapterortc ' + sessionId + ' ' + version + ' IN IP4 127.0.0.1\r\n' +
's=-\r\n' +
't=0 0\r\n';
};
SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
// Map ICE parameters (ufrag, pwd) to SDP.
sdp += SDPUtils.writeIceParameters(
transceiver.iceGatherer.getLocalParameters());
// Map DTLS parameters to SDP.
sdp += SDPUtils.writeDtlsParameters(
transceiver.dtlsTransport.getLocalParameters(),
type === 'offer' ? 'actpass' : 'active');
sdp += 'a=mid:' + transceiver.mid + '\r\n';
if (transceiver.direction) {
sdp += 'a=' + transceiver.direction + '\r\n';
} else if (transceiver.rtpSender && transceiver.rtpReceiver) {
sdp += 'a=sendrecv\r\n';
} else if (transceiver.rtpSender) {
sdp += 'a=sendonly\r\n';
} else if (transceiver.rtpReceiver) {
sdp += 'a=recvonly\r\n';
} else {
sdp += 'a=inactive\r\n';
}
if (transceiver.rtpSender) {
// spec.
var msid = 'msid:' + stream.id + ' ' +
transceiver.rtpSender.track.id + '\r\n';
sdp += 'a=' + msid;
// for Chrome.
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
' ' + msid;
if (transceiver.sendEncodingParameters[0].rtx) {
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
' ' + msid;
sdp += 'a=ssrc-group:FID ' +
transceiver.sendEncodingParameters[0].ssrc + ' ' +
transceiver.sendEncodingParameters[0].rtx.ssrc +
'\r\n';
}
}
// FIXME: this should be written by writeRtpDescription.
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
' cname:' + SDPUtils.localCName + '\r\n';
if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
' cname:' + SDPUtils.localCName + '\r\n';
}
return sdp;
};
// Gets the direction from the mediaSection or the sessionpart.
SDPUtils.getDirection = function(mediaSection, sessionpart) {
// Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
var lines = SDPUtils.splitLines(mediaSection);
for (var i = 0; i < lines.length; i++) {
switch (lines[i]) {
case 'a=sendrecv':
case 'a=sendonly':
case 'a=recvonly':
case 'a=inactive':
return lines[i].substr(2);
default:
// FIXME: What should happen here?
}
}
if (sessionpart) {
return SDPUtils.getDirection(sessionpart);
}
return 'sendrecv';
};
SDPUtils.getKind = function(mediaSection) {
var lines = SDPUtils.splitLines(mediaSection);
var mline = lines[0].split(' ');
return mline[0].substr(2);
};
SDPUtils.isRejected = function(mediaSection) {
return mediaSection.split(' ', 2)[1] === '0';
};
// Expose public methods.
module.exports = SDPUtils;
},{}],2:[function(require,module,exports){
(function (global){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var adapterFactory = require('./adapter_factory.js');
module.exports = adapterFactory({window: global.window});
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./adapter_factory.js":3}],3:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
// Shimming starts here.
module.exports = function(dependencies) {
var window = dependencies && dependencies.window;
// Utils.
var utils = require('./utils');
var logging = utils.log;
var browserDetails = utils.detectBrowser(window);
// Export to the adapter global object visible in the browser.
var adapter = {
browserDetails: browserDetails,
extractVersion: utils.extractVersion,
disableLog: utils.disableLog,
disableWarnings: utils.disableWarnings
};
// Uncomment the line below if you want logging to occur, including logging
// for the switch statement below. Can also be turned on in the browser via
// adapter.disableLog(false), but then logging from the switch statement below
// will not appear.
// require('./utils').disableLog(false);
// Browser shims.
var chromeShim = require('./chrome/chrome_shim') || null;
var edgeShim = require('./edge/edge_shim') || null;
var firefoxShim = require('./firefox/firefox_shim') || null;
var safariShim = require('./safari/safari_shim') || null;
// Shim browser if found.
switch (browserDetails.browser) {
case 'chrome':
if (!chromeShim || !chromeShim.shimPeerConnection) {
logging('Chrome shim is not included in this adapter release.');
return adapter;
}
logging('adapter.js shimming chrome.');
// Export to the adapter global object visible in the browser.
adapter.browserShim = chromeShim;
chromeShim.shimGetUserMedia(window);
chromeShim.shimMediaStream(window);
utils.shimCreateObjectURL(window);
chromeShim.shimSourceObject(window);
chromeShim.shimPeerConnection(window);
chromeShim.shimOnTrack(window);
chromeShim.shimGetSendersWithDtmf(window);
break;
case 'firefox':
if (!firefoxShim || !firefoxShim.shimPeerConnection) {
logging('Firefox shim is not included in this adapter release.');
return adapter;
}
logging('adapter.js shimming firefox.');
// Export to the adapter global object visible in the browser.
adapter.browserShim = firefoxShim;
firefoxShim.shimGetUserMedia(window);
utils.shimCreateObjectURL(window);
firefoxShim.shimSourceObject(window);
firefoxShim.shimPeerConnection(window);
firefoxShim.shimOnTrack(window);
break;
case 'edge':
if (!edgeShim || !edgeShim.shimPeerConnection) {
logging('MS edge shim is not included in this adapter release.');
return adapter;
}
logging('adapter.js shimming edge.');
// Export to the adapter global object visible in the browser.
adapter.browserShim = edgeShim;
edgeShim.shimGetUserMedia(window);
utils.shimCreateObjectURL(window);
edgeShim.shimPeerConnection(window);
edgeShim.shimReplaceTrack(window);
break;
case 'safari':
if (!safariShim) {
logging('Safari shim is not included in this adapter release.');
return adapter;
}
logging('adapter.js shimming safari.');
// Export to the adapter global object visible in the browser.
adapter.browserShim = safariShim;
// shim window.URL.createObjectURL Safari (technical preview)
utils.shimCreateObjectURL(window);
safariShim.shimRTCIceServerUrls(window);
safariShim.shimCallbacksAPI(window);
safariShim.shimLocalStreamsAPI(window);
safariShim.shimRemoteStreamsAPI(window);
safariShim.shimGetUserMedia(window);
break;
default:
logging('Unsupported browser!');
break;
}
return adapter;
};
},{"./chrome/chrome_shim":4,"./edge/edge_shim":6,"./firefox/firefox_shim":9,"./safari/safari_shim":11,"./utils":12}],4:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var utils = require('../utils.js');
var logging = utils.log;
var chromeShim = {
shimMediaStream: function(window) {
window.MediaStream = window.MediaStream || window.webkitMediaStream;
},
shimOnTrack: function(window) {
if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
window.RTCPeerConnection.prototype)) {
Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
get: function() {
return this._ontrack;
},
set: function(f) {
var self = this;
if (this._ontrack) {
this.removeEventListener('track', this._ontrack);
this.removeEventListener('addstream', this._ontrackpoly);
}
this.addEventListener('track', this._ontrack = f);
this.addEventListener('addstream', this._ontrackpoly = function(e) {
// onaddstream does not fire when a track is added to an existing
// stream. But stream.onaddtrack is implemented so we use that.
e.stream.addEventListener('addtrack', function(te) {
var receiver;
if (window.RTCPeerConnection.prototype.getReceivers) {
receiver = self.getReceivers().find(function(r) {
return r.track.id === te.track.id;
});
} else {
receiver = {track: te.track};
}
var event = new Event('track');
event.track = te.track;
event.receiver = receiver;
event.streams = [e.stream];
self.dispatchEvent(event);
});
e.stream.getTracks().forEach(function(track) {
var receiver;
if (window.RTCPeerConnection.prototype.getReceivers) {
receiver = self.getReceivers().find(function(r) {
return r.track.id === track.id;
});
} else {
receiver = {track: track};
}
var event = new Event('track');
event.track = track;
event.receiver = receiver;
event.streams = [e.stream];
this.dispatchEvent(event);
}.bind(this));
}.bind(this));
}
});
}
},
shimGetSendersWithDtmf: function(window) {
if (typeof window === 'object' && window.RTCPeerConnection &&
!('getSenders' in window.RTCPeerConnection.prototype) &&
'createDTMFSender' in window.RTCPeerConnection.prototype) {
window.RTCPeerConnection.prototype.getSenders = function() {
return this._senders || [];
};
var origAddStream = window.RTCPeerConnection.prototype.addStream;
var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
if (!window.RTCPeerConnection.prototype.addTrack) {
window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
var pc = this;
if (pc.signalingState === 'closed') {
throw new DOMException(
'The RTCPeerConnection\'s signalingState is \'closed\'.',
'InvalidStateError');
}
var streams = [].slice.call(arguments, 1);
if (streams.length !== 1 ||
!streams[0].getTracks().find(function(t) {
return t === track;
})) {
// this is not fully correct but all we can manage without
// [[associated MediaStreams]] internal slot.
throw new DOMException(
'The adapter.js addTrack polyfill only supports a single ' +
' stream which is associated with the specified track.',
'NotSupportedError');
}
pc._senders = pc._senders || [];
var alreadyExists = pc._senders.find(function(t) {
return t.track === track;
});
if (alreadyExists) {
throw new DOMException('Track already exists.',
'InvalidAccessError');
}
pc._streams = pc._streams || {};
var oldStream = pc._streams[stream.id];
if (oldStream) {
oldStream.addTrack(track);
pc.removeStream(oldStream);
pc.addStream(oldStream);
} else {
var newStream = new window.MediaStream([track]);
pc._streams[stream.id] = newStream;
pc.addStream(newStream);
}
var sender = {
track: track,
get dtmf() {
if (this._dtmf === undefined) {
if (track.kind === 'audio') {
this._dtmf = pc.createDTMFSender(track);
} else {
this._dtmf = null;
}
}
return this._dtmf;
}
};
pc._senders.push(sender);
return sender;
};
}
window.RTCPeerConnection.prototype.addStream = function(stream) {
var pc = this;
pc._senders = pc._senders || [];
origAddStream.apply(pc, [stream]);
stream.getTracks().forEach(function(track) {
pc._senders.push({
track: track,
get dtmf() {
if (this._dtmf === undefined) {
if (track.kind === 'audio') {
this._dtmf = pc.createDTMFSender(track);
} else {
this._dtmf = null;
}
}
return this._dtmf;
}
});
});
};
window.RTCPeerConnection.prototype.removeStream = function(stream) {
var pc = this;
pc._senders = pc._senders || [];
origRemoveStream.apply(pc, [stream]);
stream.getTracks().forEach(function(track) {
var sender = pc._senders.find(function(s) {
return s.track === track;
});
if (sender) {
pc._senders.splice(pc._senders.indexOf(sender), 1); // remove sender
}
});
};
} else if (typeof window === 'object' && window.RTCPeerConnection &&
'getSenders' in window.RTCPeerConnection.prototype &&
'createDTMFSender' in window.RTCPeerConnection.prototype &&
window.RTCRtpSender &&
!('dtmf' in window.RTCRtpSender.prototype)) {
var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
window.RTCPeerConnection.prototype.getSenders = function() {
var pc = this;
var senders = origGetSenders.apply(pc, []);
senders.forEach(function(sender) {
sender._pc = pc;
});
return senders;
};
Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
get: function() {
if (this._dtmf === undefined) {
if (this.track.kind === 'audio') {
this._dtmf = this._pc.createDTMFSender(this.track);
} else {
this._dtmf = null;
}
}
return this._dtmf;
},
});
}
},
shimSourceObject: function(window) {
var URL = window && window.URL;
if (typeof window === 'object') {
if (window.HTMLMediaElement &&
!('srcObject' in window.HTMLMediaElement.prototype)) {
// Shim the srcObject property, once, when HTMLMediaElement is found.
Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
get: function() {
return this._srcObject;
},
set: function(stream) {
var self = this;
// Use _srcObject as a private property for this shim
this._srcObject = stream;
if (this.src) {
URL.revokeObjectURL(this.src);
}
if (!stream) {
this.src = '';
return undefined;
}
this.src = URL.createObjectURL(stream);
// We need to recreate the blob url when a track is added or
// removed. Doing it manually since we want to avoid a recursion.
stream.addEventListener('addtrack', function() {
if (self.src) {
URL.revokeObjectURL(self.src);
}
self.src = URL.createObjectURL(stream);
});
stream.addEventListener('removetrack', function() {
if (self.src) {
URL.revokeObjectURL(self.src);
}
self.src = URL.createObjectURL(stream);
});
}
});
}
}
},
shimPeerConnection: function(window) {
var browserDetails = utils.detectBrowser(window);
// The RTCPeerConnection object.
if (!window.RTCPeerConnection) {
window.RTCPeerConnection = function(pcConfig, pcConstraints) {
// Translate iceTransportPolicy to iceTransports,
// see https://code.google.com/p/webrtc/issues/detail?id=4869
// this was fixed in M56 along with unprefixing RTCPeerConnection.
logging('PeerConnection');
if (pcConfig && pcConfig.iceTransportPolicy) {
pcConfig.iceTransports = pcConfig.iceTransportPolicy;
}
return new window.webkitRTCPeerConnection(pcConfig, pcConstraints);
};
window.RTCPeerConnection.prototype =
window.webkitRTCPeerConnection.prototype;
// wrap static methods. Currently just generateCertificate.
if (window.webkitRTCPeerConnection.generateCertificate) {
Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
get: function() {
return window.webkitRTCPeerConnection.generateCertificate;
}
});
}
} else {
// migrate from non-spec RTCIceServer.url to RTCIceServer.urls
var OrigPeerConnection = window.RTCPeerConnection;
window.RTCPeerConnection = function(pcConfig, pcConstraints) {
if (pcConfig && pcConfig.iceServers) {
var newIceServers = [];
for (var i = 0; i < pcConfig.iceServers.length; i++) {
var server = pcConfig.iceServers[i];
if (!server.hasOwnProperty('urls') &&
server.hasOwnProperty('url')) {
console.warn('RTCIceServer.url is deprecated! Use urls instead.');
server = JSON.parse(JSON.stringify(server));
server.urls = server.url;
newIceServers.push(server);
} else {
newIceServers.push(pcConfig.iceServers[i]);
}
}
pcConfig.iceServers = newIceServers;
}
return new OrigPeerConnection(pcConfig, pcConstraints);
};
window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
// wrap static methods. Currently just generateCertificate.
Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
get: function() {
return OrigPeerConnection.generateCertificate;
}
});
}
var origGetStats = window.RTCPeerConnection.prototype.getStats;
window.RTCPeerConnection.prototype.getStats = function(selector,
successCallback, errorCallback) {
var self = this;
var args = arguments;
// If selector is a function then we are in the old style stats so just
// pass back the original getStats format to avoid breaking old users.
if (arguments.length > 0 && typeof selector === 'function') {
return origGetStats.apply(this, arguments);
}
// When spec-style getStats is supported, return those when called with
// either no arguments or the selector argument is null.
/*
if (origGetStats.length === 0 && (arguments.length === 0 ||
typeof arguments[0] !== 'function')) {
return origGetStats.apply(this, []);
}*/
var fixChromeStats_ = function(response) {
var standardReport = {};
var reports = response.result();
reports.forEach(function(report) {
var standardStats = {
id: report.id,
timestamp: report.timestamp,
type: {
localcandidate: 'local-candidate',
remotecandidate: 'remote-candidate'
}[report.type] || report.type
};
report.names().forEach(function(name) {
standardStats[name] = report.stat(name);
});
standardReport[standardStats.id] = standardStats;
});
return standardReport;
};
// shim getStats with maplike support
var makeMapStats = function(stats) {
return new Map(Object.keys(stats).map(function(key) {
return [key, stats[key]];
}));
};
if (arguments.length >= 2) {
var successCallbackWrapper_ = function(response) {
args[1](makeMapStats(fixChromeStats_(response)));
};
return origGetStats.apply(this, [successCallbackWrapper_,
arguments[0]]);
}
// promise-support
return new Promise(function(resolve, reject) {
origGetStats.apply(self, [
function(response) {
resolve(makeMapStats(fixChromeStats_(response)));
}, reject]);
}).then(successCallback, errorCallback);
};
// add promise support -- natively available in Chrome 51
if (browserDetails.version < 51) {
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
.forEach(function(method) {
var nativeMethod = window.RTCPeerConnection.prototype[method];
window.RTCPeerConnection.prototype[method] = function() {
var args = arguments;
var self = this;
var promise = new Promise(function(resolve, reject) {
nativeMethod.apply(self, [args[0], resolve, reject]);
});
if (args.length < 2) {
return promise;
}
return promise.then(function() {
args[1].apply(null, []);
},
function(err) {
if (args.length >= 3) {
args[2].apply(null, [err]);
}
});
};
});
}
// promise support for createOffer and createAnswer. Available (without
// bugs) since M52: crbug/619289
if (browserDetails.version < 52) {
['createOffer', 'createAnswer'].forEach(function(method) {
var nativeMethod = window.RTCPeerConnection.prototype[method];
window.RTCPeerConnection.prototype[method] = function() {
var self = this;
if (arguments.length < 1 || (arguments.length === 1 &&
typeof arguments[0] === 'object')) {
var opts = arguments.length === 1 ? arguments[0] : undefined;
return new Promise(function(resolve, reject) {
nativeMethod.apply(self, [resolve, reject, opts]);
});
}
return nativeMethod.apply(this, arguments);
};
});
}
// shim implicit creation of RTCSessionDescription/RTCIceCandidate
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
.forEach(function(method) {
var nativeMethod = window.RTCPeerConnection.prototype[method];
window.RTCPeerConnection.prototype[method] = function() {
arguments[0] = new ((method === 'addIceCandidate') ?
window.RTCIceCandidate :
window.RTCSessionDescription)(arguments[0]);
return nativeMethod.apply(this, arguments);
};
});
// support for addIceCandidate(null or undefined)
var nativeAddIceCandidate =
window.RTCPeerConnection.prototype.addIceCandidate;
window.RTCPeerConnection.prototype.addIceCandidate = function() {
if (!arguments[0]) {
if (arguments[1]) {
arguments[1].apply(null);
}
return Promise.resolve();
}
return nativeAddIceCandidate.apply(this, arguments);
};
}
};
// Expose public methods.
module.exports = {
shimMediaStream: chromeShim.shimMediaStream,
shimOnTrack: chromeShim.shimOnTrack,
shimGetSendersWithDtmf: chromeShim.shimGetSendersWithDtmf,
shimSourceObject: chromeShim.shimSourceObject,
shimPeerConnection: chromeShim.shimPeerConnection,
shimGetUserMedia: require('./getusermedia')
};
},{"../utils.js":12,"./getusermedia":5}],5:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var utils = require('../utils.js');
var logging = utils.log;
// Expose public methods.
module.exports = function(window) {
var browserDetails = utils.detectBrowser(window);
var navigator = window && window.navigator;
var constraintsToChrome_ = function(c) {
if (typeof c !== 'object' || c.mandatory || c.optional) {
return c;
}
var cc = {};
Object.keys(c).forEach(function(key) {
if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
return;
}
var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
if (r.exact !== undefined && typeof r.exact === 'number') {
r.min = r.max = r.exact;
}
var oldname_ = function(prefix, name) {
if (prefix) {
return prefix + name.charAt(0).toUpperCase() + name.slice(1);
}
return (name === 'deviceId') ? 'sourceId' : name;
};
if (r.ideal !== undefined) {
cc.optional = cc.optional || [];
var oc = {};
if (typeof r.ideal === 'number') {
oc[oldname_('min', key)] = r.ideal;
cc.optional.push(oc);
oc = {};
oc[oldname_('max', key)] = r.ideal;
cc.optional.push(oc);
} else {
oc[oldname_('', key)] = r.ideal;
cc.optional.push(oc);
}
}
if (r.exact !== undefined && typeof r.exact !== 'number') {
cc.mandatory = cc.mandatory || {};
cc.mandatory[oldname_('', key)] = r.exact;
} else {
['min', 'max'].forEach(function(mix) {
if (r[mix] !== undefined) {
cc.mandatory = cc.mandatory || {};
cc.mandatory[oldname_(mix, key)] = r[mix];
}
});
}
});
if (c.advanced) {
cc.optional = (cc.optional || []).concat(c.advanced);
}
return cc;
};
var shimConstraints_ = function(constraints, func) {
constraints = JSON.parse(JSON.stringify(constraints));
if (constraints && typeof constraints.audio === 'object') {
var remap = function(obj, a, b) {
if (a in obj && !(b in obj)) {
obj[b] = obj[a];
delete obj[a];
}
};
constraints = JSON.parse(JSON.stringify(constraints));
remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
constraints.audio = constraintsToChrome_(constraints.audio);
}
if (constraints && typeof constraints.video === 'object') {
// Shim facingMode for mobile & surface pro.
var face = constraints.video.facingMode;
face = face && ((typeof face === 'object') ? face : {ideal: face});
var getSupportedFacingModeLies = browserDetails.version < 61;
if ((face && (face.exact === 'user' || face.exact === 'environment' ||
face.ideal === 'user' || face.ideal === 'environment')) &&
!(navigator.mediaDevices.getSupportedConstraints &&
navigator.mediaDevices.getSupportedConstraints().facingMode &&
!getSupportedFacingModeLies)) {
delete constraints.video.facingMode;
var matches;
if (face.exact === 'environment' || face.ideal === 'environment') {
matches = ['back', 'rear'];
} else if (face.exact === 'user' || face.ideal === 'user') {
matches = ['front'];
}
if (matches) {
// Look for matches in label, or use last cam for back (typical).
return navigator.mediaDevices.enumerateDevices()
.then(function(devices) {
devices = devices.filter(function(d) {
return d.kind === 'videoinput';
});
var dev = devices.find(function(d) {
return matches.some(function(match) {
return d.label.toLowerCase().indexOf(match) !== -1;
});
});
if (!dev && devices.length && matches.indexOf('back') !== -1) {
dev = devices[devices.length - 1]; // more likely the back cam
}
if (dev) {
constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :
{ideal: dev.deviceId};
}
constraints.video = constraintsToChrome_(constraints.video);
logging('chrome: ' + JSON.stringify(constraints));
return func(constraints);
});
}
}
constraints.video = constraintsToChrome_(constraints.video);
}
logging('chrome: ' + JSON.stringify(constraints));
return func(constraints);
};
var shimError_ = function(e) {
return {
name: {
PermissionDeniedError: 'NotAllowedError',
InvalidStateError: 'NotReadableError',
DevicesNotFoundError: 'NotFoundError',
ConstraintNotSatisfiedError: 'OverconstrainedError',
TrackStartError: 'NotReadableError',
MediaDeviceFailedDueToShutdown: 'NotReadableError',
MediaDeviceKillSwitchOn: 'NotReadableError'
}[e.name] || e.name,
message: e.message,
constraint: e.constraintName,
toString: function() {
return this.name + (this.message && ': ') + this.message;
}
};
};
var getUserMedia_ = function(constraints, onSuccess, onError) {
shimConstraints_(constraints, function(c) {
navigator.webkitGetUserMedia(c, onSuccess, function(e) {
onError(shimError_(e));
});
});
};
navigator.getUserMedia = getUserMedia_;
// Returns the result of getUserMedia as a Promise.
var getUserMediaPromise_ = function(constraints) {
return new Promise(function(resolve, reject) {
navigator.getUserMedia(constraints, resolve, reject);
});
};
if (!navigator.mediaDevices) {
navigator.mediaDevices = {
getUserMedia: getUserMediaPromise_,
enumerateDevices: function() {
return new Promise(function(resolve) {
var kinds = {audio: 'audioinput', video: 'videoinput'};
return window.MediaStreamTrack.getSources(function(devices) {
resolve(devices.map(function(device) {
return {label: device.label,
kind: kinds[device.kind],
deviceId: device.id,
groupId: ''};
}));
});
});
},
getSupportedConstraints: function() {
return {
deviceId: true, echoCancellation: true, facingMode: true,
frameRate: true, height: true, width: true
};
}
};
}
// A shim for getUserMedia method on the mediaDevices object.
// TODO(KaptenJansson) remove once implemented in Chrome stable.
if (!navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia = function(constraints) {
return getUserMediaPromise_(constraints);
};
} else {
// Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
// function which returns a Promise, it does not accept spec-style
// constraints.
var origGetUserMedia = navigator.mediaDevices.getUserMedia.
bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function(cs) {
return shimConstraints_(cs, function(c) {
return origGetUserMedia(c).then(function(stream) {
if (c.audio && !stream.getAudioTracks().length ||
c.video && !stream.getVideoTracks().length) {
stream.getTracks().forEach(function(track) {
track.stop();
});
throw new DOMException('', 'NotFoundError');
}
return stream;
}, function(e) {
return Promise.reject(shimError_(e));
});
});
};
}
// Dummy devicechange event methods.
// TODO(KaptenJansson) remove once implemented in Chrome stable.
if (typeof navigator.mediaDevices.addEventListener === 'undefined') {
navigator.mediaDevices.addEventListener = function() {
logging('Dummy mediaDevices.addEventListener called.');
};
}
if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {
navigator.mediaDevices.removeEventListener = function() {
logging('Dummy mediaDevices.removeEventListener called.');
};
}
};
},{"../utils.js":12}],6:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var utils = require('../utils');
var shimRTCPeerConnection = require('./rtcpeerconnection_shim');
module.exports = {
shimGetUserMedia: require('./getusermedia'),
shimPeerConnection: function(window) {
var browserDetails = utils.detectBrowser(window);
if (window.RTCIceGatherer) {
// ORTC defines an RTCIceCandidate object but no constructor.
// Not implemented in Edge.
if (!window.RTCIceCandidate) {
window.RTCIceCandidate = function(args) {
return args;
};
}
// ORTC does not have a session description object but
// other browsers (i.e. Chrome) that will support both PC and ORTC
// in the future might have this defined already.
if (!window.RTCSessionDescription) {
window.RTCSessionDescription = function(args) {
return args;
};
}
// this adds an additional event listener to MediaStrackTrack that signals
// when a tracks enabled property was changed. Workaround for a bug in
// addStream, see below. No longer required in 15025+
if (browserDetails.version < 15025) {
var origMSTEnabled = Object.getOwnPropertyDescriptor(
window.MediaStreamTrack.prototype, 'enabled');
Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', {
set: function(value) {
origMSTEnabled.set.call(this, value);
var ev = new Event('enabled');
ev.enabled = value;
this.dispatchEvent(ev);
}
});
}
}
window.RTCPeerConnection =
shimRTCPeerConnection(window, browserDetails.version);
},
shimReplaceTrack: function(window) {
// ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
if (window.RTCRtpSender &&
!('replaceTrack' in window.RTCRtpSender.prototype)) {
window.RTCRtpSender.prototype.replaceTrack =
window.RTCRtpSender.prototype.setTrack;
}
}
};
},{"../utils":12,"./getusermedia":7,"./rtcpeerconnection_shim":8}],7:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
// Expose public methods.
module.exports = function(window) {
var navigator = window && window.navigator;
var shimError_ = function(e) {
return {
name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,
message: e.message,
constraint: e.constraint,
toString: function() {
return this.name;
}
};
};
// getUserMedia error shim.
var origGetUserMedia = navigator.mediaDevices.getUserMedia.
bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function(c) {
return origGetUserMedia(c).catch(function(e) {
return Promise.reject(shimError_(e));
});
};
};
},{}],8:[function(require,module,exports){
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var SDPUtils = require('sdp');
// sort tracks such that they follow an a-v-a-v...
// pattern.
function sortTracks(tracks) {
var audioTracks = tracks.filter(function(track) {
return track.kind === 'audio';
});
var videoTracks = tracks.filter(function(track) {
return track.kind === 'video';
});
tracks = [];
while (audioTracks.length || videoTracks.length) {
if (audioTracks.length) {
tracks.push(audioTracks.shift());
}
if (videoTracks.length) {
tracks.push(videoTracks.shift());
}
}
return tracks;
}
// Edge does not like
// 1) stun:
// 2) turn: that does not have all of turn:host:port?transport=udp
// 3) turn: with ipv6 addresses
// 4) turn: occurring muliple times
function filterIceServers(iceServers, edgeVersion) {
var hasTurn = false;
iceServers = JSON.parse(JSON.stringify(iceServers));
return iceServers.filter(function(server) {
if (server && (server.urls || server.url)) {
var urls = server.urls || server.url;
if (server.url && !server.urls) {
console.warn('RTCIceServer.url is deprecated! Use urls instead.');
}
var isString = typeof urls === 'string';
if (isString) {
urls = [urls];
}
urls = urls.filter(function(url) {
var validTurn = url.indexOf('turn:') === 0 &&
url.indexOf('transport=udp') !== -1 &&
url.indexOf('turn:[') === -1 &&
!hasTurn;
if (validTurn) {
hasTurn = true;
return true;
}
return url.indexOf('stun:') === 0 && edgeVersion >= 14393;
});
delete server.url;
server.urls = isString ? urls[0] : urls;
return !!urls.length;
}
return false;
});
}
// Determines the intersection of local and remote capabilities.
function getCommonCapabilities(localCapabilities, remoteCapabilities) {
var commonCapabilities = {
codecs: [],
headerExtensions: [],
fecMechanisms: []
};
var findCodecByPayloadType = function(pt, codecs) {
pt = parseInt(pt, 10);
for (var i = 0; i < codecs.length; i++) {
if (codecs[i].payloadType === pt ||
codecs[i].preferredPayloadType === pt) {
return codecs[i];
}
}
};
var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {
var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);
var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);
return lCodec && rCodec &&
lCodec.name.toLowerCase() === rCodec.name.toLowerCase();
};
localCapabilities.codecs.forEach(function(lCodec) {
for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
var rCodec = remoteCapabilities.codecs[i];
if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
lCodec.clockRate === rCodec.clockRate) {
if (lCodec.name.toLowerCase() === 'rtx' &&
lCodec.parameters && rCodec.parameters.apt) {
// for RTX we need to find the local rtx that has a apt
// which points to the same local codec as the remote one.
if (!rtxCapabilityMatches(lCodec, rCodec,
localCapabilities.codecs, remoteCapabilities.codecs)) {
continue;
}
}
rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy
// number of channels is the highest common number of channels
rCodec.numChannels = Math.min(lCodec.numChannels,
rCodec.numChannels);
// push rCodec so we reply with offerer payload type
commonCapabilities.codecs.push(rCodec);
// determine common feedback mechanisms
rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {
for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
if (lCodec.rtcpFeedback[j].type === fb.type &&
lCodec.rtcpFeedback[j].parameter === fb.parameter) {
return true;
}
}
return false;
});
// FIXME: also need to determine .parameters
// see https://github.com/openpeer/ortc/issues/569
break;
}
}
});
localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
for (var i = 0; i < remoteCapabilities.headerExtensions.length;
i++) {
var rHeaderExtension = remoteCapabilities.headerExtensions[i];
if (lHeaderExtension.uri === rHeaderExtension.uri) {
commonCapabilities.headerExtensions.push(rHeaderExtension);
break;
}
}
});
// FIXME: fecMechanisms
return commonCapabilities;
}
// is action=setLocalDescription with type allowed in signalingState
function isActionAllowedInSignalingState(action, type, signalingState) {
return {
offer: {
setLocalDescription: ['stable', 'have-local-offer'],
setRemoteDescription: ['stable', 'have-remote-offer']
},
answer: {
setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],
setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']
}
}[type][action].indexOf(signalingState) !== -1;
}
module.exports = function(window, edgeVersion) {
var RTCPeerConnection = function(config) {
var self = this;
var _eventTarget = document.createDocumentFragment();
['addEventListener', 'removeEventListener', 'dispatchEvent']
.forEach(function(method) {
self[method] = _eventTarget[method].bind(_eventTarget);
});
this.needNegotiation = false;
this.onicecandidate = null;
this.onaddstream = null;
this.ontrack = null;
this.onremovestream = null;
this.onsignalingstatechange = null;
this.oniceconnectionstatechange = null;
this.onicegatheringstatechange = null;
this.onnegotiationneeded = null;
this.ondatachannel = null;
this.canTrickleIceCandidates = null;
this.localStreams = [];
this.remoteStreams = [];
this.getLocalStreams = function() {
return self.localStreams;
};
this.getRemoteStreams = function() {
return self.remoteStreams;
};
this.localDescription = new window.RTCSessionDescription({
type: '',
sdp: ''
});
this.remoteDescription = new window.RTCSessionDescription({
type: '',
sdp: ''
});
this.signalingState = 'stable';
this.iceConnectionState = 'new';
this.iceGatheringState = 'new';
this.iceOptions = {
gatherPolicy: 'all',
iceServers: []
};
if (config && config.iceTransportPolicy) {
switch (config.iceTransportPolicy) {
case 'all':
case 'relay':
this.iceOptions.gatherPolicy = config.iceTransportPolicy;
break;
default:
// don't set iceTransportPolicy.
break;
}
}
this.usingBundle = config && config.bundlePolicy === 'max-bundle';
if (config && config.iceServers) {
this.iceOptions.iceServers = filterIceServers(config.iceServers,
edgeVersion);
}
this._config = config || {};
// per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
// everything that is needed to describe a SDP m-line.
this.transceivers = [];
// since the iceGatherer is currently created in createOffer but we
// must not emit candidates until after setLocalDescription we buffer
// them in this array.
this._localIceCandidatesBuffer = [];
this._sdpSessionId = SDPUtils.generateSessionId();
};
RTCPeerConnection.prototype._emitGatheringStateChange = function() {
var event = new Event('icegatheringstatechange');
this.dispatchEvent(event);
if (this.onicegatheringstatechange !== null) {
this.onicegatheringstatechange(event);
}
};
RTCPeerConnection.prototype._emitBufferedCandidates = function() {
var self = this;
var sections = SDPUtils.splitSections(self.localDescription.sdp);
// FIXME: need to apply ice candidates in a way which is async but
// in-order
this._localIceCandidatesBuffer.forEach(function(event) {
var end = !event.candidate || Object.keys(event.candidate).length === 0;
if (end) {
for (var j = 1; j < sections.length; j++) {
if (sections[j].indexOf('\r\na=end-of-candidates\r\n') === -1) {
sections[j] += 'a=end-of-candidates\r\n';
}
}
} else {
sections[event.candidate.sdpMLineIndex + 1] +=
'a=' + event.candidate.candidate + '\r\n';
}
self.localDescription.sdp = sections.join('');
self.dispatchEvent(event);
if (self.onicecandidate !== null) {
self.onicecandidate(event);
}
if (!event.candidate && self.iceGatheringState !== 'complete') {
var complete = self.transceivers.every(function(transceiver) {
return transceiver.iceGatherer &&
transceiver.iceGatherer.state === 'completed';
});
if (complete && self.iceGatheringStateChange !== 'complete') {
self.iceGatheringState = 'complete';
self._emitGatheringStateChange();
}
}
});
this._localIceCandidatesBuffer = [];
};
RTCPeerConnection.prototype.getConfiguration = function() {
return this._config;
};
// internal helper to create a transceiver object.
// (whih is not yet the same as the WebRTC 1.0 transceiver)
RTCPeerConnection.prototype._createTransceiver = function(kind) {
var hasBundleTransport = this.transceivers.length > 0;
var transceiver = {
track: null,
iceGatherer: null,
iceTransport: null,
dtlsTransport: null,
localCapabilities: null,
remoteCapabilities: null,
rtpSender: null,
rtpReceiver: null,
kind: kind,
mid: null,
sendEncodingParameters: null,
recvEncodingParameters: null,
stream: null,
wantReceive: true
};
if (this.usingBundle && hasBundleTransport) {
transceiver.iceTransport = this.transceivers[0].iceTransport;
transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;
} else {
var transports = this._createIceAndDtlsTransports();
transceiver.iceTransport = transports.iceTransport;
transceiver.dtlsTransport = transports.dtlsTransport;
}
this.transceivers.push(transceiver);
return transceiver;
};
RTCPeerConnection.prototype.addTrack = function(track, stream) {
var transceiver;
for (var i = 0; i < this.transceivers.length; i++) {
if (!this.transceivers[i].track &&
this.transceivers[i].kind === track.kind) {
transceiver = this.transceivers[i];
}
}
if (!transceiver) {
transceiver = this._createTransceiver(track.kind);
}
transceiver.track = track;
transceiver.stream = stream;
transceiver.rtpSender = new window.RTCRtpSender(track,
transceiver.dtlsTransport);
this._maybeFireNegotiationNeeded();
return transceiver.rtpSender;
};
RTCPeerConnection.prototype.addStream = function(stream) {
var self = this;
if (edgeVersion >= 15025) {
this.localStreams.push(stream);
stream.getTracks().forEach(function(track) {
self.addTrack(track, stream);
});
} else {
// Clone is necessary for local demos mostly, attaching directly
// to two different senders does not work (build 10547).
// Fixed in 15025 (or earlier)
var clonedStream = stream.clone();
stream.getTracks().forEach(function(track, idx) {
var clonedTrack = clonedStream.getTracks()[idx];
track.addEventListener('enabled', function(event) {
clonedTrack.enabled = event.enabled;
});
});
clonedStream.getTracks().forEach(function(track) {
self.addTrack(track, clonedStream);
});
this.localStreams.push(clonedStream);
}
this._maybeFireNegotiationNeeded();
};
RTCPeerConnection.prototype.removeStream = function(stream) {
var idx = this.localStreams.indexOf(stream);
if (idx > -1) {
this.localStreams.splice(idx, 1);
this._maybeFireNegotiationNeeded();
}
};
RTCPeerConnection.prototype.getSenders = function() {
return this.transceivers.filter(function(transceiver) {
return !!transceiver.rtpSender;
})
.map(function(transceiver) {
return transceiver.rtpSender;
});
};
RTCPeerConnection.prototype.getReceivers = function() {
return this.transceivers.filter(function(transceiver) {
return !!transceiver.rtpReceiver;
})
.map(function(transceiver) {
return transceiver.rtpReceiver;
});
};
// Create ICE gatherer and hook it up.
RTCPeerConnection.prototype._createIceGatherer = function(mid,
sdpMLineIndex) {
var self = this;
var iceGatherer = new window.RTCIceGatherer(self.iceOptions);
iceGatherer.onlocalcandidate = function(evt) {
var event = new Event('icecandidate');
event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
var cand = evt.candidate;
var end = !cand || Object.keys(cand).length === 0;
// Edge emits an empty object for RTCIceCandidateComplete‥
if (end) {
// polyfill since RTCIceGatherer.state is not implemented in
// Edge 10547 yet.
if (iceGatherer.state === undefined) {
iceGatherer.state = 'completed';
}
} else {
// RTCIceCandidate doesn't have a component, needs to be added
cand.component = 1;
event.candidate.candidate = SDPUtils.writeCandidate(cand);
}
// update local description.
var sections = SDPUtils.splitSections(self.localDescription.sdp);
if (!end) {
sections[event.candidate.sdpMLineIndex + 1] +=
'a=' + event.candidate.candidate + '\r\n';
} else {
sections[event.candidate.sdpMLineIndex + 1] +=
'a=end-of-candidates\r\n';
}
self.localDescription.sdp = sections.join('');
var transceivers = self._pendingOffer ? self._pendingOffer :
self.transceivers;
var complete = transceivers.every(function(transceiver) {
return transceiver.iceGatherer &&
transceiver.iceGatherer.state === 'completed';
});
// Emit candidate if localDescription is set.
// Also emits null candidate when all gatherers are complete.
switch (self.iceGatheringState) {
case 'new':
if (!end) {
self._localIceCandidatesBuffer.push(event);
}
if (end && complete) {
self._localIceCandidatesBuffer.push(
new Event('icecandidate'));
}
break;
case 'gathering':
self._emitBufferedCandidates();
if (!end) {
self.dispatchEvent(event);
if (self.onicecandidate !== null) {
self.onicecandidate(event);
}
}
if (complete) {
self.dispatchEvent(new Event('icecandidate'));
if (self.onicecandidate !== null) {
self.onicecandidate(new Event('icecandidate'));
}
self.iceGatheringState = 'complete';
self._emitGatheringStateChange();
}
break;
case 'complete':
// should not happen... currently!
break;
default: // no-op.
break;
}
};
return iceGatherer;
};
// Create ICE transport and DTLS transport.
RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {
var self = this;
var iceTransport = new window.RTCIceTransport(null);
iceTransport.onicestatechange = function() {
self._updateConnectionState();
};
var dtlsTransport = new window.RTCDtlsTransport(iceTransport);
dtlsTransport.ondtlsstatechange = function() {
self._updateConnectionState();
};
dtlsTransport.onerror = function() {
// onerror does not set state to failed by itself.
Object.defineProperty(dtlsTransport, 'state',
{value: 'failed', writable: true});
self._updateConnectionState();
};
return {
iceTransport: iceTransport,
dtlsTransport: dtlsTransport
};
};
// Destroy ICE gatherer, ICE transport and DTLS transport.
// Without triggering the callbacks.
RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(
sdpMLineIndex) {
var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
if (iceGatherer) {
delete iceGatherer.onlocalcandidate;
delete this.transceivers[sdpMLineIndex].iceGatherer;
}
var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;
if (iceTransport) {
delete iceTransport.onicestatechange;
delete this.transceivers[sdpMLineIndex].iceTransport;
}
var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;
if (dtlsTransport) {
delete dtlsTransport.ondtlssttatechange;
delete dtlsTransport.onerror;
delete this.transceivers[sdpMLineIndex].dtlsTransport;
}
};
// Start the RTP Sender and Receiver for a transceiver.
RTCPeerConnection.prototype._transceive = function(transceiver,
send, recv) {
var params = getCommonCapabilities(transceiver.localCapabilities,
transceiver.remoteCapabilities);
if (send && transceiver.rtpSender) {
params.encodings = transceiver.sendEncodingParameters;
params.rtcp = {
cname: SDPUtils.localCName,
compound: transceiver.rtcpParameters.compound
};
if (transceiver.recvEncodingParameters.length) {
params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
}
transceiver.rtpSender.send(params);
}
if (recv && transceiver.rtpReceiver) {
// remove RTX field in Edge 14942
if (transceiver.kind === 'video'
&& transceiver.recvEncodingParameters
&& edgeVersion < 15019) {
transceiver.recvEncodingParameters.forEach(function(p) {
delete p.rtx;
});
}
params.encodings = transceiver.recvEncodingParameters;
params.rtcp = {
cname: transceiver.rtcpParameters.cname,
compound: transceiver.rtcpParameters.compound
};
if (transceiver.sendEncodingParameters.length) {
params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
}
transceiver.rtpReceiver.receive(params);
}
};
RTCPeerConnection.prototype.setLocalDescription = function(description) {
var self = this;
if (!isActionAllowedInSignalingState('setLocalDescription',
description.type, this.signalingState)) {
var e = new Error('Can not set local ' + description.type +
' in state ' + this.signalingState);
e.name = 'InvalidStateError';
if (arguments.length > 2 && typeof arguments[2] === 'function') {
window.setTimeout(arguments[2], 0, e);
}
return Promise.reject(e);
}
var sections;
var sessionpart;
if (description.type === 'offer') {
// FIXME: What was the purpose of this empty if statement?
// if (!this._pendingOffer) {
// } else {
if (this._pendingOffer) {
// VERY limited support for SDP munging. Limited to:
// * changing the order of codecs
sections = SDPUtils.splitSections(description.sdp);
sessionpart = sections.shift();
sections.forEach(function(mediaSection, sdpMLineIndex) {
var caps = SDPUtils.parseRtpParameters(mediaSection);
self._pendingOffer[sdpMLineIndex].localCapabilities = caps;
});
this.transceivers = this._pendingOffer;
delete this._pendingOffer;
}
} else if (description.type === 'answer') {
sections = SDPUtils.splitSections(self.remoteDescription.sdp);
sessionpart = sections.shift();
var isIceLite = SDPUtils.matchPrefix(sessionpart,
'a=ice-lite').length > 0;
sections.forEach(function(mediaSection, sdpMLineIndex) {
var transceiver = self.transceivers[sdpMLineIndex];
var iceGatherer = transceiver.iceGatherer;
var iceTransport = transceiver.iceTransport;
var dtlsTransport = transceiver.dtlsTransport;
var localCapabilities = transceiver.localCapabilities;
var remoteCapabilities = transceiver.remoteCapabilities;
var rejected = SDPUtils.isRejected(mediaSection);
if (!rejected && !transceiver.isDatachannel) {
var remoteIceParameters = SDPUtils.getIceParameters(
mediaSection, sessionpart);
var remoteDtlsParameters = SDPUtils.getDtlsParameters(
mediaSection, sessionpart);
if (isIceLite) {
remoteDtlsParameters.role = 'server';
}
if (!self.usingBundle || sdpMLineIndex === 0) {
iceTransport.start(iceGatherer, remoteIceParameters,
isIceLite ? 'controlling' : 'controlled');
dtlsTransport.start(remoteDtlsParameters);
}
// Calculate intersection of capabilities.
var params = getCommonCapabilities(localCapabilities,
remoteCapabilities);
// Start the RTCRtpSender. The RTCRtpReceiver for this
// transceiver has already been started in setRemoteDescription.
self._transceive(transceiver,
params.codecs.length > 0,
false);
}
});
}
this.localDescription = {
type: description.type,
sdp: description.sdp
};
switch (description.type) {
case 'offer':
this._updateSignalingState('have-local-offer');
break;
case 'answer':
this._updateSignalingState('stable');
break;
default:
throw new TypeError('unsupported type "' + description.type +
'"');
}
// If a success callback was provided, emit ICE candidates after it
// has been executed. Otherwise, emit callback after the Promise is
// resolved.
var hasCallback = arguments.length > 1 &&
typeof arguments[1] === 'function';
if (hasCallback) {
var cb = arguments[1];
window.setTimeout(function() {
cb();
if (self.iceGatheringState === 'new') {
self.iceGatheringState = 'gathering';
self._emitGatheringStateChange();
}
self._emitBufferedCandidates();
}, 0);
}
var p = Promise.resolve();
p.then(function() {
if (!hasCallback) {
if (self.iceGatheringState === 'new') {
self.iceGatheringState = 'gathering';
self._emitGatheringStateChange();
}
// Usually candidates will be emitted earlier.
window.setTimeout(self._emitBufferedCandidates.bind(self), 500);
}
});
return p;
};
RTCPeerConnection.prototype.setRemoteDescription = function(description) {
var self = this;
if (!isActionAllowedInSignalingState('setRemoteDescription',
description.type, this.signalingState)) {
var e = new Error('Can not set remote ' + description.type +
' in state ' + this.signalingState);
e.name = 'InvalidStateError';
if (arguments.length > 2 && typeof arguments[2] === 'function') {
window.setTimeout(arguments[2], 0, e);
}
return Promise.reject(e);
}
var streams = {};
var receiverList = [];
var sections = SDPUtils.splitSections(description.sdp);
var sessionpart = sections.shift();
var isIceLite = SDPUtils.matchPrefix(sessionpart,
'a=ice-lite').length > 0;
var usingBundle = SDPUtils.matchPrefix(sessionpart,
'a=group:BUNDLE ').length > 0;
this.usingBundle = usingBundle;
var iceOptions = SDPUtils.matchPrefix(sessionpart,
'a=ice-options:')[0];
if (iceOptions) {
this.canTrickleIceCandidates = iceOptions.substr(14).split(' ')
.indexOf('trickle') >= 0;
} else {
this.canTrickleIceCandidates = false;
}
sections.forEach(function(mediaSection, sdpMLineIndex) {
var lines = SDPUtils.splitLines(mediaSection);
var kind = SDPUtils.getKind(mediaSection);
var rejected = SDPUtils.isRejected(mediaSection);
var protocol = lines[0].substr(2).split(' ')[2];
var direction = SDPUtils.getDirection(mediaSection, sessionpart);
var remoteMsid = SDPUtils.parseMsid(mediaSection);
var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier();
// Reject datachannels which are not implemented yet.
if (kind === 'application' && protocol === 'DTLS/SCTP') {
self.transceivers[sdpMLineIndex] = {
mid: mid,
isDatachannel: true
};
return;
}
var transceiver;
var iceGatherer;
var iceTransport;
var dtlsTransport;
var rtpReceiver;
var sendEncodingParameters;
var recvEncodingParameters;
var localCapabilities;
var track;
// FIXME: ensure the mediaSection has rtcp-mux set.
var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
var remoteIceParameters;
var remoteDtlsParameters;
if (!rejected) {
remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
sessionpart);
remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
sessionpart);
remoteDtlsParameters.role = 'client';
}
recvEncodingParameters =
SDPUtils.parseRtpEncodingParameters(mediaSection);
var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection);
var isComplete = SDPUtils.matchPrefix(mediaSection,
'a=end-of-candidates', sessionpart).length > 0;
var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')
.map(function(cand) {
return SDPUtils.parseCandidate(cand);
})
.filter(function(cand) {
return cand.component === '1' || cand.component === 1;
});
// Check if we can use BUNDLE and dispose transports.
if ((description.type === 'offer' || description.type === 'answer') &&
!rejected && usingBundle && sdpMLineIndex > 0 &&
self.transceivers[sdpMLineIndex]) {
self._disposeIceAndDtlsTransports(sdpMLineIndex);
self.transceivers[sdpMLineIndex].iceGatherer =
self.transceivers[0].iceGatherer;
self.transceivers[sdpMLineIndex].iceTransport =
self.transceivers[0].iceTransport;
self.transceivers[sdpMLineIndex].dtlsTransport =
self.transceivers[0].dtlsTransport;
if (self.transceivers[sdpMLineIndex].rtpSender) {
self.transceivers[sdpMLineIndex].rtpSender.setTransport(
self.transceivers[0].dtlsTransport);
}
if (self.transceivers[sdpMLineIndex].rtpReceiver) {
self.transceivers[sdpMLineIndex].rtpReceiver.setTransport(
self.transceivers[0].dtlsTransport);
}
}
if (description.type === 'offer' && !rejected) {
transceiver = self.transceivers[sdpMLineIndex] ||
self._createTransceiver(kind);
transceiver.mid = mid;
if (!transceiver.iceGatherer) {
transceiver.iceGatherer = usingBundle && sdpMLineIndex > 0 ?
self.transceivers[0].iceGatherer :
self._createIceGatherer(mid, sdpMLineIndex);
}
if (isComplete && (!usingBundle || sdpMLineIndex === 0)) {
transceiver.iceTransport.setRemoteCandidates(cands);
}
localCapabilities = window.RTCRtpReceiver.getCapabilities(kind);
// filter RTX until additional stuff needed for RTX is implemented
// in adapter.js
if (edgeVersion < 15019) {
localCapabilities.codecs = localCapabilities.codecs.filter(
function(codec) {
return codec.name !== 'rtx';
});
}
sendEncodingParameters = [{
ssrc: (2 * sdpMLineIndex + 2) * 1001
}];
if (direction === 'sendrecv' || direction === 'sendonly') {
rtpReceiver = new window.RTCRtpReceiver(transceiver.dtlsTransport,
kind);
track = rtpReceiver.track;
// FIXME: does not work with Plan B.
if (remoteMsid) {
if (!streams[remoteMsid.stream]) {
streams[remoteMsid.stream] = new window.MediaStream();
Object.defineProperty(streams[remoteMsid.stream], 'id', {
get: function() {
return remoteMsid.stream;
}
});
}
Object.defineProperty(track, 'id', {
get: function() {
return remoteMsid.track;
}
});
streams[remoteMsid.stream].addTrack(track);
receiverList.push([track, rtpReceiver,
streams[remoteMsid.stream]]);
} else {
if (!streams.default) {
streams.default = new window.MediaStream();
}
streams.default.addTrack(track);
receiverList.push([track, rtpReceiver, streams.default]);
}
}
transceiver.localCapabilities = localCapabilities;
transceiver.remoteCapabilities = remoteCapabilities;
transceiver.rtpReceiver = rtpReceiver;
transceiver.rtcpParameters = rtcpParameters;
transceiver.sendEncodingParameters = sendEncodingParameters;
transceiver.recvEncodingParameters = recvEncodingParameters;
// Start the RTCRtpReceiver now. The RTPSender is started in
// setLocalDescription.
self._transceive(self.transceivers[sdpMLineIndex],
false,
direction === 'sendrecv' || direction === 'sendonly');
} else if (description.type === 'answer' && !rejected) {
transceiver = self.transceivers[sdpMLineIndex];
iceGatherer = transceiver.iceGatherer;
iceTransport = transceiver.iceTransport;
dtlsTransport = transceiver.dtlsTransport;
rtpReceiver = transceiver.rtpReceiver;
sendEncodingParameters = transceiver.sendEncodingParameters;
localCapabilities = transceiver.localCapabilities;
self.transceivers[sdpMLineIndex].recvEncodingParameters =
recvEncodingParameters;
self.transceivers[sdpMLineIndex].remoteCapabilities =
remoteCapabilities;
self.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;
if ((isIceLite || isComplete) && cands.length) {
iceTransport.setRemoteCandidates(cands);
}
if (!usingBundle || sdpMLineIndex === 0) {
iceTransport.start(iceGatherer, remoteIceParameters,
'controlling');
dtlsTransport.start(remoteDtlsParameters);
}
self._transceive(transceiver,
direction === 'sendrecv' || direction === 'recvonly',
direction === 'sendrecv' || direction === 'sendonly');
if (rtpReceiver &&
(direction === 'sendrecv' || direction === 'sendonly')) {
track = rtpReceiver.track;
if (remoteMsid) {
if (!streams[remoteMsid.stream]) {
streams[remoteMsid.stream] = new window.MediaStream();
}
streams[remoteMsid.stream].addTrack(track);
receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);
} else {
if (!streams.default) {
streams.default = new window.MediaStream();
}
streams.default.addTrack(track);
receiverList.push([track, rtpReceiver, streams.default]);
}
} else {
// FIXME: actually the receiver should be created later.
delete transceiver.rtpReceiver;
}
}
});
this.remoteDescription = {
type: description.type,
sdp: description.sdp
};
switch (description.type) {
case 'offer':
this._updateSignalingState('have-remote-offer');
break;
case 'answer':
this._updateSignalingState('stable');
break;
default:
throw new TypeError('unsupported type "' + description.type +
'"');
}
Object.keys(streams).forEach(function(sid) {
var stream = streams[sid];
if (stream.getTracks().length) {
self.remoteStreams.push(stream);
var event = new Event('addstream');
event.stream = stream;
self.dispatchEvent(event);
if (self.onaddstream !== null) {
window.setTimeout(function() {
self.onaddstream(event);
}, 0);
}
receiverList.forEach(function(item) {
var track = item[0];
var receiver = item[1];
if (stream.id !== item[2].id) {
return;
}
var trackEvent = new Event('track');
trackEvent.track = track;
trackEvent.receiver = receiver;
trackEvent.streams = [stream];
self.dispatchEvent(trackEvent);
if (self.ontrack !== null) {
window.setTimeout(function() {
self.ontrack(trackEvent);
}, 0);
}
});
}
});
// check whether addIceCandidate({}) was called within four seconds after
// setRemoteDescription.
window.setTimeout(function() {
if (!(self && self.transceivers)) {
return;
}
self.transceivers.forEach(function(transceiver) {
if (transceiver.iceTransport &&
transceiver.iceTransport.state === 'new' &&
transceiver.iceTransport.getRemoteCandidates().length > 0) {
console.warn('Timeout for addRemoteCandidate. Consider sending ' +
'an end-of-candidates notification');
transceiver.iceTransport.addRemoteCandidate({});
}
});
}, 4000);
if (arguments.length > 1 && typeof arguments[1] === 'function') {
window.setTimeout(arguments[1], 0);
}
return Promise.resolve();
};
RTCPeerConnection.prototype.close = function() {
this.transceivers.forEach(function(transceiver) {
/* not yet
if (transceiver.iceGatherer) {
transceiver.iceGatherer.close();
}
*/
if (transceiver.iceTransport) {
transceiver.iceTransport.stop();
}
if (transceiver.dtlsTransport) {
transceiver.dtlsTransport.stop();
}
if (transceiver.rtpSender) {
transceiver.rtpSender.stop();
}
if (transceiver.rtpReceiver) {
transceiver.rtpReceiver.stop();
}
});
// FIXME: clean up tracks, local streams, remote streams, etc
this._updateSignalingState('closed');
};
// Update the signaling state.
RTCPeerConnection.prototype._updateSignalingState = function(newState) {
this.signalingState = newState;
var event = new Event('signalingstatechange');
this.dispatchEvent(event);
if (this.onsignalingstatechange !== null) {
this.onsignalingstatechange(event);
}
};
// Determine whether to fire the negotiationneeded event.
RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {
var self = this;
if (this.signalingState !== 'stable' || this.needNegotiation === true) {
return;
}
this.needNegotiation = true;
window.setTimeout(function() {
if (self.needNegotiation === false) {
return;
}
self.needNegotiation = false;
var event = new Event('negotiationneeded');
self.dispatchEvent(event);
if (self.onnegotiationneeded !== null) {
self.onnegotiationneeded(event);
}
}, 0);
};
// Update the connection state.
RTCPeerConnection.prototype._updateConnectionState = function() {
var self = this;
var newState;
var states = {
'new': 0,
closed: 0,
connecting: 0,
checking: 0,
connected: 0,
completed: 0,
disconnected: 0,
failed: 0
};
this.transceivers.forEach(function(transceiver) {
states[transceiver.iceTransport.state]++;
states[transceiver.dtlsTransport.state]++;
});
// ICETransport.completed and connected are the same for this purpose.
states.connected += states.completed;
newState = 'new';
if (states.failed > 0) {
newState = 'failed';
} else if (states.connecting > 0 || states.checking > 0) {
newState = 'connecting';
} else if (states.disconnected > 0) {
newState = 'disconnected';
} else if (states.new > 0) {
newState = 'new';
} else if (states.connected > 0 || states.completed > 0) {
newState = 'connected';
}
if (newState !== self.iceConnectionState) {
self.iceConnectionState = newState;
var event = new Event('iceconnectionstatechange');
this.dispatchEvent(event);
if (this.oniceconnectionstatechange !== null) {
this.oniceconnectionstatechange(event);
}
}
};
RTCPeerConnection.prototype.createOffer = function() {
var self = this;
if (this._pendingOffer) {
throw new Error('createOffer called while there is a pending offer.');
}
var offerOptions;
if (arguments.length === 1 && typeof arguments[0] !== 'function') {
offerOptions = arguments[0];
} else if (arguments.length === 3) {
offerOptions = arguments[2];
}
var numAudioTracks = this.transceivers.filter(function(t) {
return t.kind === 'audio';
}).length;
var numVideoTracks = this.transceivers.filter(function(t) {
return t.kind === 'video';
}).length;
// Determine number of audio and video tracks we need to send/recv.
if (offerOptions) {
// Reject Chrome legacy constraints.
if (offerOptions.mandatory || offerOptions.optional) {
throw new TypeError(
'Legacy mandatory/optional constraints not supported.');
}
if (offerOptions.offerToReceiveAudio !== undefined) {
if (offerOptions.offerToReceiveAudio === true) {
numAudioTracks = 1;
} else if (offerOptions.offerToReceiveAudio === false) {
numAudioTracks = 0;
} else {
numAudioTracks = offerOptions.offerToReceiveAudio;
}
}
if (offerOptions.offerToReceiveVideo !== undefined) {
if (offerOptions.offerToReceiveVideo === true) {
numVideoTracks = 1;
} else if (offerOptions.offerToReceiveVideo === false) {
numVideoTracks = 0;
} else {
numVideoTracks = offerOptions.offerToReceiveVideo;
}
}
}
this.transceivers.forEach(function(transceiver) {
if (transceiver.kind === 'audio') {
numAudioTracks--;
if (numAudioTracks < 0) {
transceiver.wantReceive = false;
}
} else if (transceiver.kind === 'video') {
numVideoTracks--;
if (numVideoTracks < 0) {
transceiver.wantReceive = false;
}
}
});
// Create M-lines for recvonly streams.
while (numAudioTracks > 0 || numVideoTracks > 0) {
if (numAudioTracks > 0) {
this._createTransceiver('audio');
numAudioTracks--;
}
if (numVideoTracks > 0) {
this._createTransceiver('video');
numVideoTracks--;
}
}
// reorder tracks
var transceivers = sortTracks(this.transceivers);
var sdp = SDPUtils.writeSessionBoilerplate(this._sdpSessionId);
transceivers.forEach(function(transceiver, sdpMLineIndex) {
// For each track, create an ice gatherer, ice transport,
// dtls transport, potentially rtpsender and rtpreceiver.
var track = transceiver.track;
var kind = transceiver.kind;
var mid = SDPUtils.generateIdentifier();
transceiver.mid = mid;
if (!transceiver.iceGatherer) {
transceiver.iceGatherer = self.usingBundle && sdpMLineIndex > 0 ?
transceivers[0].iceGatherer :
self._createIceGatherer(mid, sdpMLineIndex);
}
var localCapabilities = window.RTCRtpSender.getCapabilities(kind);
// filter RTX until additional stuff needed for RTX is implemented
// in adapter.js
if (edgeVersion < 15019) {
localCapabilities.codecs = localCapabilities.codecs.filter(
function(codec) {
return codec.name !== 'rtx';
});
}
localCapabilities.codecs.forEach(function(codec) {
// work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
// by adding level-asymmetry-allowed=1
if (codec.name === 'H264' &&
codec.parameters['level-asymmetry-allowed'] === undefined) {
codec.parameters['level-asymmetry-allowed'] = '1';
}
});
// generate an ssrc now, to be used later in rtpSender.send
var sendEncodingParameters = [{
ssrc: (2 * sdpMLineIndex + 1) * 1001
}];
if (track) {
// add RTX
if (edgeVersion >= 15019 && kind === 'video') {
sendEncodingParameters[0].rtx = {
ssrc: (2 * sdpMLineIndex + 1) * 1001 + 1
};
}
}
if (transceiver.wantReceive) {
transceiver.rtpReceiver = new window.RTCRtpReceiver(
transceiver.dtlsTransport,
kind
);
}
transceiver.localCapabilities = localCapabilities;
transceiver.sendEncodingParameters = sendEncodingParameters;
});
// always offer BUNDLE and dispose on return if not supported.
if (this._config.bundlePolicy !== 'max-compat') {
sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {
return t.mid;
}).join(' ') + '\r\n';
}
sdp += 'a=ice-options:trickle\r\n';
transceivers.forEach(function(transceiver, sdpMLineIndex) {
sdp += SDPUtils.writeMediaSection(transceiver,
transceiver.localCapabilities, 'offer', transceiver.stream);
sdp += 'a=rtcp-rsize\r\n';
});
this._pendingOffer = transceivers;
var desc = new window.RTCSessionDescription({
type: 'offer',
sdp: sdp
});
if (arguments.length && typeof arguments[0] === 'function') {
window.setTimeout(arguments[0], 0, desc);
}
return Promise.resolve(desc);
};
RTCPeerConnection.prototype.createAnswer = function() {
var sdp = SDPUtils.writeSessionBoilerplate(this._sdpSessionId);
if (this.usingBundle) {
sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {
return t.mid;
}).join(' ') + '\r\n';
}
this.transceivers.forEach(function(transceiver, sdpMLineIndex) {
if (transceiver.isDatachannel) {
sdp += 'm=application 0 DTLS/SCTP 5000\r\n' +
'c=IN IP4 0.0.0.0\r\n' +
'a=mid:' + transceiver.mid + '\r\n';
return;
}
// FIXME: look at direction.
if (transceiver.stream) {
var localTrack;
if (transceiver.kind === 'audio') {
localTrack = transceiver.stream.getAudioTracks()[0];
} else if (transceiver.kind === 'video') {
localTrack = transceiver.stream.getVideoTracks()[0];
}
if (localTrack) {
// add RTX
if (edgeVersion >= 15019 && transceiver.kind === 'video') {
transceiver.sendEncodingParameters[0].rtx = {
ssrc: (2 * sdpMLineIndex + 2) * 1001 + 1
};
}
}
}
// Calculate intersection of capabilities.
var commonCapabilities = getCommonCapabilities(
transceiver.localCapabilities,
transceiver.remoteCapabilities);
var hasRtx = commonCapabilities.codecs.filter(function(c) {
return c.name.toLowerCase() === 'rtx';
}).length;
if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
delete transceiver.sendEncodingParameters[0].rtx;
}
sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,
'answer', transceiver.stream);
if (transceiver.rtcpParameters &&
transceiver.rtcpParameters.reducedSize) {
sdp += 'a=rtcp-rsize\r\n';
}
});
var desc = new window.RTCSessionDescription({
type: 'answer',
sdp: sdp
});
if (arguments.length && typeof arguments[0] === 'function') {
window.setTimeout(arguments[0], 0, desc);
}
return Promise.resolve(desc);
};
RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
if (!candidate) {
for (var j = 0; j < this.transceivers.length; j++) {
this.transceivers[j].iceTransport.addRemoteCandidate({});
if (this.usingBundle) {
return Promise.resolve();
}
}
} else {
var mLineIndex = candidate.sdpMLineIndex;
if (candidate.sdpMid) {
for (var i = 0; i < this.transceivers.length; i++) {
if (this.transceivers[i].mid === candidate.sdpMid) {
mLineIndex = i;
break;
}
}
}
var transceiver = this.transceivers[mLineIndex];
if (transceiver) {
var cand = Object.keys(candidate.candidate).length > 0 ?
SDPUtils.parseCandidate(candidate.candidate) : {};
// Ignore Chrome's invalid candidates since Edge does not like them.
if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {
return Promise.resolve();
}
// Ignore RTCP candidates, we assume RTCP-MUX.
if (cand.component &&
!(cand.component === '1' || cand.component === 1)) {
return Promise.resolve();
}
transceiver.iceTransport.addRemoteCandidate(cand);
// update the remoteDescription.
var sections = SDPUtils.splitSections(this.remoteDescription.sdp);
sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()
: 'a=end-of-candidates') + '\r\n';
this.remoteDescription.sdp = sections.join('');
}
}
if (arguments.length > 1 && typeof arguments[1] === 'function') {
window.setTimeout(arguments[1], 0);
}
return Promise.resolve();
};
RTCPeerConnection.prototype.getStats = function() {
var promises = [];
this.transceivers.forEach(function(transceiver) {
['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
'dtlsTransport'].forEach(function(method) {
if (transceiver[method]) {
promises.push(transceiver[method].getStats());
}
});
});
var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&
arguments[1];
var fixStatsType = function(stat) {
return {
inboundrtp: 'inbound-rtp',
outboundrtp: 'outbound-rtp',
candidatepair: 'candidate-pair',
localcandidate: 'local-candidate',
remotecandidate: 'remote-candidate'
}[stat.type] || stat.type;
};
return new Promise(function(resolve) {
// shim getStats with maplike support
var results = new Map();
Promise.all(promises).then(function(res) {
res.forEach(function(result) {
Object.keys(result).forEach(function(id) {
result[id].type = fixStatsType(result[id]);
results.set(id, result[id]);
});
});
if (cb) {
window.setTimeout(cb, 0, results);
}
resolve(results);
});
});
};
return RTCPeerConnection;
};
},{"sdp":1}],9:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var utils = require('../utils');
var firefoxShim = {
shimOnTrack: function(window) {
if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
window.RTCPeerConnection.prototype)) {
Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
get: function() {
return this._ontrack;
},
set: function(f) {
if (this._ontrack) {
this.removeEventListener('track', this._ontrack);
this.removeEventListener('addstream', this._ontrackpoly);
}
this.addEventListener('track', this._ontrack = f);
this.addEventListener('addstream', this._ontrackpoly = function(e) {
e.stream.getTracks().forEach(function(track) {
var event = new Event('track');
event.track = track;
event.receiver = {track: track};
event.streams = [e.stream];
this.dispatchEvent(event);
}.bind(this));
}.bind(this));
}
});
}
},
shimSourceObject: function(window) {
// Firefox has supported mozSrcObject since FF22, unprefixed in 42.
if (typeof window === 'object') {
if (window.HTMLMediaElement &&
!('srcObject' in window.HTMLMediaElement.prototype)) {
// Shim the srcObject property, once, when HTMLMediaElement is found.
Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
get: function() {
return this.mozSrcObject;
},
set: function(stream) {
this.mozSrcObject = stream;
}
});
}
}
},
shimPeerConnection: function(window) {
var browserDetails = utils.detectBrowser(window);
if (typeof window !== 'object' || !(window.RTCPeerConnection ||
window.mozRTCPeerConnection)) {
return; // probably media.peerconnection.enabled=false in about:config
}
// The RTCPeerConnection object.
if (!window.RTCPeerConnection) {
window.RTCPeerConnection = function(pcConfig, pcConstraints) {
if (browserDetails.version < 38) {
// .urls is not supported in FF < 38.
// create RTCIceServers with a single url.
if (pcConfig && pcConfig.iceServers) {
var newIceServers = [];
for (var i = 0; i < pcConfig.iceServers.length; i++) {
var server = pcConfig.iceServers[i];
if (server.hasOwnProperty('urls')) {
for (var j = 0; j < server.urls.length; j++) {
var newServer = {
url: server.urls[j]
};
if (server.urls[j].indexOf('turn') === 0) {
newServer.username = server.username;
newServer.credential = server.credential;
}
newIceServers.push(newServer);
}
} else {
newIceServers.push(pcConfig.iceServers[i]);
}
}
pcConfig.iceServers = newIceServers;
}
}
return new window.mozRTCPeerConnection(pcConfig, pcConstraints);
};
window.RTCPeerConnection.prototype =
window.mozRTCPeerConnection.prototype;
// wrap static methods. Currently just generateCertificate.
if (window.mozRTCPeerConnection.generateCertificate) {
Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
get: function() {
return window.mozRTCPeerConnection.generateCertificate;
}
});
}
window.RTCSessionDescription = window.mozRTCSessionDescription;
window.RTCIceCandidate = window.mozRTCIceCandidate;
}
// shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
.forEach(function(method) {
var nativeMethod = window.RTCPeerConnection.prototype[method];
window.RTCPeerConnection.prototype[method] = function() {
arguments[0] = new ((method === 'addIceCandidate') ?
window.RTCIceCandidate :
window.RTCSessionDescription)(arguments[0]);
return nativeMethod.apply(this, arguments);
};
});
// support for addIceCandidate(null or undefined)
var nativeAddIceCandidate =
window.RTCPeerConnection.prototype.addIceCandidate;
window.RTCPeerConnection.prototype.addIceCandidate = function() {
if (!arguments[0]) {
if (arguments[1]) {
arguments[1].apply(null);
}
return Promise.resolve();
}
return nativeAddIceCandidate.apply(this, arguments);
};
// shim getStats with maplike support
var makeMapStats = function(stats) {
var map = new Map();
Object.keys(stats).forEach(function(key) {
map.set(key, stats[key]);
map[key] = stats[key];
});
return map;
};
var modernStatsTypes = {
inboundrtp: 'inbound-rtp',
outboundrtp: 'outbound-rtp',
candidatepair: 'candidate-pair',
localcandidate: 'local-candidate',
remotecandidate: 'remote-candidate'
};
var nativeGetStats = window.RTCPeerConnection.prototype.getStats;
window.RTCPeerConnection.prototype.getStats = function(
selector,
onSucc,
onErr
) {
return nativeGetStats.apply(this, [selector || null])
.then(function(stats) {
if (browserDetails.version < 48) {
stats = makeMapStats(stats);
}
if (browserDetails.version < 53 && !onSucc) {
// Shim only promise getStats with spec-hyphens in type names
// Leave callback version alone; misc old uses of forEach before Map
try {
stats.forEach(function(stat) {
stat.type = modernStatsTypes[stat.type] || stat.type;
});
} catch (e) {
if (e.name !== 'TypeError') {
throw e;
}
// Avoid TypeError: "type" is read-only, in old versions. 34-43ish
stats.forEach(function(stat, i) {
stats.set(i, Object.assign({}, stat, {
type: modernStatsTypes[stat.type] || stat.type
}));
});
}
}
return stats;
})
.then(onSucc, onErr);
};
}
};
// Expose public methods.
module.exports = {
shimOnTrack: firefoxShim.shimOnTrack,
shimSourceObject: firefoxShim.shimSourceObject,
shimPeerConnection: firefoxShim.shimPeerConnection,
shimGetUserMedia: require('./getusermedia')
};
},{"../utils":12,"./getusermedia":10}],10:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var utils = require('../utils');
var logging = utils.log;
// Expose public methods.
module.exports = function(window) {
var browserDetails = utils.detectBrowser(window);
var navigator = window && window.navigator;
var MediaStreamTrack = window && window.MediaStreamTrack;
var shimError_ = function(e) {
return {
name: {
InternalError: 'NotReadableError',
NotSupportedError: 'TypeError',
PermissionDeniedError: 'NotAllowedError',
SecurityError: 'NotAllowedError'
}[e.name] || e.name,
message: {
'The operation is insecure.': 'The request is not allowed by the ' +
'user agent or the platform in the current context.'
}[e.message] || e.message,
constraint: e.constraint,
toString: function() {
return this.name + (this.message && ': ') + this.message;
}
};
};
// getUserMedia constraints shim.
var getUserMedia_ = function(constraints, onSuccess, onError) {
var constraintsToFF37_ = function(c) {
if (typeof c !== 'object' || c.require) {
return c;
}
var require = [];
Object.keys(c).forEach(function(key) {
if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
return;
}
var r = c[key] = (typeof c[key] === 'object') ?
c[key] : {ideal: c[key]};
if (r.min !== undefined ||
r.max !== undefined || r.exact !== undefined) {
require.push(key);
}
if (r.exact !== undefined) {
if (typeof r.exact === 'number') {
r. min = r.max = r.exact;
} else {
c[key] = r.exact;
}
delete r.exact;
}
if (r.ideal !== undefined) {
c.advanced = c.advanced || [];
var oc = {};
if (typeof r.ideal === 'number') {
oc[key] = {min: r.ideal, max: r.ideal};
} else {
oc[key] = r.ideal;
}
c.advanced.push(oc);
delete r.ideal;
if (!Object.keys(r).length) {
delete c[key];
}
}
});
if (require.length) {
c.require = require;
}
return c;
};
constraints = JSON.parse(JSON.stringify(constraints));
if (browserDetails.version < 38) {
logging('spec: ' + JSON.stringify(constraints));
if (constraints.audio) {
constraints.audio = constraintsToFF37_(constraints.audio);
}
if (constraints.video) {
constraints.video = constraintsToFF37_(constraints.video);
}
logging('ff37: ' + JSON.stringify(constraints));
}
return navigator.mozGetUserMedia(constraints, onSuccess, function(e) {
onError(shimError_(e));
});
};
// Returns the result of getUserMedia as a Promise.
var getUserMediaPromise_ = function(constraints) {
return new Promise(function(resolve, reject) {
getUserMedia_(constraints, resolve, reject);
});
};
// Shim for mediaDevices on older versions.
if (!navigator.mediaDevices) {
navigator.mediaDevices = {getUserMedia: getUserMediaPromise_,
addEventListener: function() { },
removeEventListener: function() { }
};
}
navigator.mediaDevices.enumerateDevices =
navigator.mediaDevices.enumerateDevices || function() {
return new Promise(function(resolve) {
var infos = [
{kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
{kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
];
resolve(infos);
});
};
if (browserDetails.version < 41) {
// Work around http://bugzil.la/1169665
var orgEnumerateDevices =
navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
navigator.mediaDevices.enumerateDevices = function() {
return orgEnumerateDevices().then(undefined, function(e) {
if (e.name === 'NotFoundError') {
return [];
}
throw e;
});
};
}
if (browserDetails.version < 49) {
var origGetUserMedia = navigator.mediaDevices.getUserMedia.
bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function(c) {
return origGetUserMedia(c).then(function(stream) {
// Work around https://bugzil.la/802326
if (c.audio && !stream.getAudioTracks().length ||
c.video && !stream.getVideoTracks().length) {
stream.getTracks().forEach(function(track) {
track.stop();
});
throw new DOMException('The object can not be found here.',
'NotFoundError');
}
return stream;
}, function(e) {
return Promise.reject(shimError_(e));
});
};
}
if (!(browserDetails.version > 55 &&
'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
var remap = function(obj, a, b) {
if (a in obj && !(b in obj)) {
obj[b] = obj[a];
delete obj[a];
}
};
var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.
bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function(c) {
if (typeof c === 'object' && typeof c.audio === 'object') {
c = JSON.parse(JSON.stringify(c));
remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
}
return nativeGetUserMedia(c);
};
if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
var nativeGetSettings = MediaStreamTrack.prototype.getSettings;
MediaStreamTrack.prototype.getSettings = function() {
var obj = nativeGetSettings.apply(this, arguments);
remap(obj, 'mozAutoGainControl', 'autoGainControl');
remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
return obj;
};
}
if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints;
MediaStreamTrack.prototype.applyConstraints = function(c) {
if (this.kind === 'audio' && typeof c === 'object') {
c = JSON.parse(JSON.stringify(c));
remap(c, 'autoGainControl', 'mozAutoGainControl');
remap(c, 'noiseSuppression', 'mozNoiseSuppression');
}
return nativeApplyConstraints.apply(this, [c]);
};
}
}
navigator.getUserMedia = function(constraints, onSuccess, onError) {
if (browserDetails.version < 44) {
return getUserMedia_(constraints, onSuccess, onError);
}
// Replace Firefox 44+'s deprecation warning with unprefixed version.
console.warn('navigator.getUserMedia has been replaced by ' +
'navigator.mediaDevices.getUserMedia');
navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
};
};
},{"../utils":12}],11:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
var utils = require('../utils');
var safariShim = {
// TODO: DrAlex, should be here, double check against LayoutTests
// TODO: once the back-end for the mac port is done, add.
// TODO: check for webkitGTK+
// shimPeerConnection: function() { },
shimLocalStreamsAPI: function(window) {
if (typeof window !== 'object' || !window.RTCPeerConnection) {
return;
}
if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
window.RTCPeerConnection.prototype.getLocalStreams = function() {
if (!this._localStreams) {
this._localStreams = [];
}
return this._localStreams;
};
}
if (!('getStreamById' in window.RTCPeerConnection.prototype)) {
window.RTCPeerConnection.prototype.getStreamById = function(id) {
var result = null;
if (this._localStreams) {
this._localStreams.forEach(function(stream) {
if (stream.id === id) {
result = stream;
}
});
}
if (this._remoteStreams) {
this._remoteStreams.forEach(function(stream) {
if (stream.id === id) {
result = stream;
}
});
}
return result;
};
}
if (!('addStream' in window.RTCPeerConnection.prototype)) {
var _addTrack = window.RTCPeerConnection.prototype.addTrack;
window.RTCPeerConnection.prototype.addStream = function(stream) {
if (!this._localStreams) {
this._localStreams = [];
}
if (this._localStreams.indexOf(stream) === -1) {
this._localStreams.push(stream);
}
var self = this;
stream.getTracks().forEach(function(track) {
_addTrack.call(self, track, stream);
});
};
window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
if (stream) {
if (!this._localStreams) {
this._localStreams = [stream];
} else if (this._localStreams.indexOf(stream) === -1) {
this._localStreams.push(stream);
}
}
_addTrack.call(this, track, stream);
};
}
if (!('removeStream' in window.RTCPeerConnection.prototype)) {
window.RTCPeerConnection.prototype.removeStream = function(stream) {
if (!this._localStreams) {
this._localStreams = [];
}
var index = this._localStreams.indexOf(stream);
if (index === -1) {
return;
}
this._localStreams.splice(index, 1);
var self = this;
var tracks = stream.getTracks();
this.getSenders().forEach(function(sender) {
if (tracks.indexOf(sender.track) !== -1) {
self.removeTrack(sender);
}
});
};
}
},
shimRemoteStreamsAPI: function(window) {
if (typeof window !== 'object' || !window.RTCPeerConnection) {
return;
}
if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
window.RTCPeerConnection.prototype.getRemoteStreams = function() {
return this._remoteStreams ? this._remoteStreams : [];
};
}
if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
get: function() {
return this._onaddstream;
},
set: function(f) {
if (this._onaddstream) {
this.removeEventListener('addstream', this._onaddstream);
this.removeEventListener('track', this._onaddstreampoly);
}
this.addEventListener('addstream', this._onaddstream = f);
this.addEventListener('track', this._onaddstreampoly = function(e) {
var stream = e.streams[0];
if (!this._remoteStreams) {
this._remoteStreams = [];
}
if (this._remoteStreams.indexOf(stream) >= 0) {
return;
}
this._remoteStreams.push(stream);
var event = new Event('addstream');
event.stream = e.streams[0];
this.dispatchEvent(event);
}.bind(this));
}
});
}
},
shimCallbacksAPI: function(window) {
if (typeof window !== 'object' || !window.RTCPeerConnection) {
return;
}
var prototype = window.RTCPeerConnection.prototype;
var createOffer = prototype.createOffer;
var createAnswer = prototype.createAnswer;
var setLocalDescription = prototype.setLocalDescription;
var setRemoteDescription = prototype.setRemoteDescription;
var addIceCandidate = prototype.addIceCandidate;
prototype.createOffer = function(successCallback, failureCallback) {
var options = (arguments.length >= 2) ? arguments[2] : arguments[0];
var promise = createOffer.apply(this, [options]);
if (!failureCallback) {
return promise;
}
promise.then(successCallback, failureCallback);
return Promise.resolve();
};
prototype.createAnswer = function(successCallback, failureCallback) {
var options = (arguments.length >= 2) ? arguments[2] : arguments[0];
var promise = createAnswer.apply(this, [options]);
if (!failureCallback) {
return promise;
}
promise.then(successCallback, failureCallback);
return Promise.resolve();
};
var withCallback = function(description, successCallback, failureCallback) {
var promise = setLocalDescription.apply(this, [description]);
if (!failureCallback) {
return promise;
}
promise.then(successCallback, failureCallback);
return Promise.resolve();
};
prototype.setLocalDescription = withCallback;
withCallback = function(description, successCallback, failureCallback) {
var promise = setRemoteDescription.apply(this, [description]);
if (!failureCallback) {
return promise;
}
promise.then(successCallback, failureCallback);
return Promise.resolve();
};
prototype.setRemoteDescription = withCallback;
withCallback = function(candidate, successCallback, failureCallback) {
var promise = addIceCandidate.apply(this, [candidate]);
if (!failureCallback) {
return promise;
}
promise.then(successCallback, failureCallback);
return Promise.resolve();
};
prototype.addIceCandidate = withCallback;
},
shimGetUserMedia: function(window) {
var navigator = window && window.navigator;
if (!navigator.getUserMedia) {
if (navigator.webkitGetUserMedia) {
navigator.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
} else if (navigator.mediaDevices &&
navigator.mediaDevices.getUserMedia) {
navigator.getUserMedia = function(constraints, cb, errcb) {
navigator.mediaDevices.getUserMedia(constraints)
.then(cb, errcb);
}.bind(navigator);
}
}
},
shimRTCIceServerUrls: function(window) {
// migrate from non-spec RTCIceServer.url to RTCIceServer.urls
var OrigPeerConnection = window.RTCPeerConnection;
window.RTCPeerConnection = function(pcConfig, pcConstraints) {
if (pcConfig && pcConfig.iceServers) {
var newIceServers = [];
for (var i = 0; i < pcConfig.iceServers.length; i++) {
var server = pcConfig.iceServers[i];
if (!server.hasOwnProperty('urls') &&
server.hasOwnProperty('url')) {
utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
server = JSON.parse(JSON.stringify(server));
server.urls = server.url;
delete server.url;
newIceServers.push(server);
} else {
newIceServers.push(pcConfig.iceServers[i]);
}
}
pcConfig.iceServers = newIceServers;
}
return new OrigPeerConnection(pcConfig, pcConstraints);
};
window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
// wrap static methods. Currently just generateCertificate.
Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
get: function() {
return OrigPeerConnection.generateCertificate;
}
});
}
};
// Expose public methods.
module.exports = {
shimCallbacksAPI: safariShim.shimCallbacksAPI,
shimLocalStreamsAPI: safariShim.shimLocalStreamsAPI,
shimRemoteStreamsAPI: safariShim.shimRemoteStreamsAPI,
shimGetUserMedia: safariShim.shimGetUserMedia,
shimRTCIceServerUrls: safariShim.shimRTCIceServerUrls
// TODO
// shimPeerConnection: safariShim.shimPeerConnection
};
},{"../utils":12}],12:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var logDisabled_ = true;
var deprecationWarnings_ = true;
// Utility methods.
var utils = {
disableLog: function(bool) {
if (typeof bool !== 'boolean') {
return new Error('Argument type: ' + typeof bool +
'. Please use a boolean.');
}
logDisabled_ = bool;
return (bool) ? 'adapter.js logging disabled' :
'adapter.js logging enabled';
},
/**
* Disable or enable deprecation warnings
* @param {!boolean} bool set to true to disable warnings.
*/
disableWarnings: function(bool) {
if (typeof bool !== 'boolean') {
return new Error('Argument type: ' + typeof bool +
'. Please use a boolean.');
}
deprecationWarnings_ = !bool;
return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
},
log: function() {
if (typeof window === 'object') {
if (logDisabled_) {
return;
}
if (typeof console !== 'undefined' && typeof console.log === 'function') {
console.log.apply(console, arguments);
}
}
},
/**
* Shows a deprecation warning suggesting the modern and spec-compatible API.
*/
deprecated: function(oldMethod, newMethod) {
if (!deprecationWarnings_) {
return;
}
console.warn(oldMethod + ' is deprecated, please use ' + newMethod +
' instead.');
},
/**
* Extract browser version out of the provided user agent string.
*
* @param {!string} uastring userAgent string.
* @param {!string} expr Regular expression used as match criteria.
* @param {!number} pos position in the version string to be returned.
* @return {!number} browser version.
*/
extractVersion: function(uastring, expr, pos) {
var match = uastring.match(expr);
return match && match.length >= pos && parseInt(match[pos], 10);
},
/**
* Browser detector.
*
* @return {object} result containing browser and version
* properties.
*/
detectBrowser: function(window) {
var navigator = window && window.navigator;
// Returned result object.
var result = {};
result.browser = null;
result.version = null;
// Fail early if it's not a browser
if (typeof window === 'undefined' || !window.navigator) {
result.browser = 'Not a browser.';
return result;
}
// Firefox.
if (navigator.mozGetUserMedia) {
result.browser = 'firefox';
result.version = this.extractVersion(navigator.userAgent,
/Firefox\/(\d+)\./, 1);
} else if (navigator.webkitGetUserMedia) {
// Chrome, Chromium, Webview, Opera, all use the chrome shim for now
if (window.webkitRTCPeerConnection) {
result.browser = 'chrome';
result.version = this.extractVersion(navigator.userAgent,
/Chrom(e|ium)\/(\d+)\./, 2);
} else { // Safari (in an unpublished version) or unknown webkit-based.
if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
result.browser = 'safari';
result.version = this.extractVersion(navigator.userAgent,
/AppleWebKit\/(\d+)\./, 1);
} else { // unknown webkit-based browser.
result.browser = 'Unsupported webkit-based browser ' +
'with GUM support but no WebRTC support.';
return result;
}
}
} else if (navigator.mediaDevices &&
navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { // Edge.
result.browser = 'edge';
result.version = this.extractVersion(navigator.userAgent,
/Edge\/(\d+).(\d+)$/, 2);
} else if (navigator.mediaDevices &&
navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
// Safari, with webkitGetUserMedia removed.
result.browser = 'safari';
result.version = this.extractVersion(navigator.userAgent,
/AppleWebKit\/(\d+)\./, 1);
} else { // Default fallthrough: not supported.
result.browser = 'Not a supported browser.';
return result;
}
return result;
},
// shimCreateObjectURL must be called before shimSourceObject to avoid loop.
shimCreateObjectURL: function(window) {
var URL = window && window.URL;
if (!(typeof window === 'object' && window.HTMLMediaElement &&
'srcObject' in window.HTMLMediaElement.prototype)) {
// Only shim CreateObjectURL using srcObject if srcObject exists.
return undefined;
}
var nativeCreateObjectURL = URL.createObjectURL.bind(URL);
var nativeRevokeObjectURL = URL.revokeObjectURL.bind(URL);
var streams = new Map(), newId = 0;
URL.createObjectURL = function(stream) {
if ('getTracks' in stream) {
var url = 'polyblob:' + (++newId);
streams.set(url, stream);
utils.deprecated('URL.createObjectURL(stream)',
'elem.srcObject = stream');
return url;
}
return nativeCreateObjectURL(stream);
};
URL.revokeObjectURL = function(url) {
nativeRevokeObjectURL(url);
streams.delete(url);
};
var dsc = Object.getOwnPropertyDescriptor(window.HTMLMediaElement.prototype,
'src');
Object.defineProperty(window.HTMLMediaElement.prototype, 'src', {
get: function() {
return dsc.get.apply(this);
},
set: function(url) {
this.srcObject = streams.get(url) || null;
return dsc.set.apply(this, [url]);
}
});
var nativeSetAttribute = window.HTMLMediaElement.prototype.setAttribute;
window.HTMLMediaElement.prototype.setAttribute = function() {
if (arguments.length === 2 &&
('' + arguments[0]).toLowerCase() === 'src') {
this.srcObject = streams.get(arguments[1]) || null;
}
return nativeSetAttribute.apply(this, arguments);
};
}
};
// Export.
module.exports = {
log: utils.log,
deprecated: utils.deprecated,
disableLog: utils.disableLog,
disableWarnings: utils.disableWarnings,
extractVersion: utils.extractVersion,
shimCreateObjectURL: utils.shimCreateObjectURL,
detectBrowser: utils.detectBrowser.bind(utils)
};
},{}]},{},[2]);
/*
* Intel WebRTC SDK version 3.4.1
* Copyright (c) 2018 Intel <http://webrtc.purertc.com>
* Homepage: http://webrtc.purertc.com
*/
! function(a) {
function b(a) {
"use strict";
this.target = a.target, this.type = a.type, this.attributes = a.attributes
}
function c(a) {
"use strict";
var b = this,
c = new SignalingChannel;
this.onConnected = null, this.onDisconnected = null, this.onConnectFailed = null, this.onChatInvitation = null, this.onChatDenied =
null, this.onChatStopped = null, this.onChatAccepted = null, this.onChatSignal = null, this.onStreamType = null,
this.onAuthenticated = null, c.onMessage = function(a, c) {
var d = JSON.parse(a);
switch (d.type) {
case "chat-invitation":
b.onChatInvitation && b.onChatInvitation(c, d.ua);
break;
case "chat-accepted":
b.onChatAccepted && b.onChatAccepted(c, d.ua);
break;
case "chat-denied":
b.onChatDenied && b.onChatDenied(c);
break;
case "chat-closed":
b.onChatStopped && b.onChatStopped(c);
break;
case "stream-type":
b.onStreamType && b.onStreamType(d.data, c);
break;
case "chat-signal":
b.onChatSignal && b.onChatSignal(d.data, c);
break;
case "chat-negotiation-needed":
b.onNegotiationNeeded && b.onNegotiationNeeded(c);
break;
case "chat-negotiation-accepted":
b.onNegotiationAccepted && b.onNegotiationAccepted(c);
break;
default:
e.Logger.error("Received unkown message")
}
}, c.onServerDisconnected = function() {
b.onDisconnected && b.onDisconnected()
}, this.sendChatInvitation = function(a, b, d, e) {
var f = {
type: "chat-closed"
};
c.sendMessage(JSON.stringify(f), a), f = {
type: "chat-invitation",
ua: b
}, c.sendMessage(JSON.stringify(f), a, d, e)
}, this.sendChatAccepted = function(a, b, d, e) {
var f = {
type: "chat-accepted",
ua: b
};
c.sendMessage(JSON.stringify(f), a, d, e)
}, this.sendChatDenied = function(a, b, d) {
var e = {
type: "chat-denied"
};
c.sendMessage(JSON.stringify(e), a, b, d)
}, this.sendChatStopped = function(a, b, d) {
var e = {
type: "chat-closed"
};
c.sendMessage(JSON.stringify(e), a, b, d)
}, this.sendStreamType = function(a, b, d, e) {
var f = {
type: "stream-type",
data: b
};
c.sendMessage(JSON.stringify(f), a, d, e)
}, this.sendSignalMessage = function(a, b, d, e) {
var f = {
type: "chat-signal",
data: b
};
c.sendMessage(JSON.stringify(f), a, d, e)
}, this.sendNegotiationNeeded = function(a, b, d) {
var e = {
type: "chat-negotiation-needed"
};
c.sendMessage(JSON.stringify(e), a, b, d)
}, this.sendNegotiationAccepted = function(a, b, d) {
var e = {
type: "chat-negotiation-accepted"
};
c.sendMessage(JSON.stringify(e), a, b, d)
}, this.finalize = function() {
c.disconnect()
}, this.connect = function(a, d, e) {
c.connect(a, function(a) {
b.onConnected && b.onConnected(), b.onAuthenticated && b.onAuthenticated(a), d && d(a)
}, e)
}
}
var d = function() {
"use strict";
var a = {};
return Object.defineProperties(a, {
version: {
get: function() {
return "3.4.1"
}
},
name: {
get: function() {
return "Intel WebRTC SDK"
}
}
}), a
}(),
e = {},
f = {};
d.EventDispatcher = function(a) {
"use strict";
var b = {};
return a.dispatcher = {}, a.dispatcher.eventListeners = {}, b.addEventListener = function(b, c) {
void 0 === a.dispatcher.eventListeners[b] && (a.dispatcher.eventListeners[b] = []), a.dispatcher.eventListeners[b]
.push(c)
}, b.on = b.addEventListener, b.removeEventListener = function(b, c) {
if (a.dispatcher.eventListeners[b]) {
var d = a.dispatcher.eventListeners[b].indexOf(c); - 1 !== d && a.dispatcher.eventListeners[b].splice(d, 1)
}
}, b.clearEventListener = function(b) {
a.dispatcher.eventListeners[b] = []
}, b.dispatchEvent = function(b) {
a.dispatcher.eventListeners[b.type] && a.dispatcher.eventListeners[b.type].map(function(a) {
a(b)
})
}, b
}, d.StreamEvent = function(a) {
"use strict";
b.call(this, a), this.stream = a.stream, this.msg = a.msg
}, d.ClientEvent = function(a) {
"use strict";
b.call(this, a), this.user = a.user
}, d.MessageEvent = function(a) {
"use strict";
b.call(this, a), this.msg = a.msg
}, d.ChatEvent = function(a) {
"use strict";
b.call(this, a), this.type = a.type, this.senderId = a.senderId, this.peerId = a.peerId
}, d.DataEvent = function(a) {
"use strict";
b.call(this, a), this.type = a.type, this.senderId = a.senderId, this.data = a.data
}, d.RecorderEvent = function(a) {
"use strict";
b.call(this, a), this.recorderId = a.id
}, d.StreamEvent.prototype = Object.create(b.prototype), d.StreamEvent.prototype.constructor = d.StreamEvent, d.ClientEvent
.prototype = Object.create(b.prototype), d.ClientEvent.prototype.constructor = d.ClientEvent, d.MessageEvent.prototype =
Object.create(b.prototype), d.MessageEvent.prototype.constructor = d.MessageEvent, d.ChatEvent.prototype = Object.create(
b.prototype), d.ChatEvent.prototype.constructor = d.ChatEvent, d.DataEvent.prototype = Object.create(b.prototype), d
.DataEvent.prototype.constructor = d.DataEvent, d.RecorderEvent.prototype = Object.create(b.prototype), d.RecorderEvent
.prototype.constructor = d.RecorderEvent, d.Common = function() {
function a(a, c, d) {
return b(a, 0, -1, c, d)
}
function b(a, b, c, d, e) {
for (var f = -1 !== c ? c : a.length, g = b; g < f; ++g)
if (0 === a[g].indexOf(d) && (!e || -1 !== a[g].toLowerCase().indexOf(e.toLowerCase()))) return g;
return null
}
function c(b, c) {
var e = a(b, "a=rtpmap", c);
return e ? d(b[e]) : null
}
function d(a) {
var b = new RegExp("a=rtpmap:(\\d+) [a-zA-Z0-9-]+\\/\\d+", "i"),
c = a.match(b);
return c && 2 === c.length ? c[1] : null
}
function f(a, b) {
var c = a.split(" "),
d = c.slice(0, 3);
d.push(b);
for (var e = 3; e < c.length; e++) c[e] !== b && d.push(c[e]);
return d.join(" ")
}
function g(c, d, f) {
var g = c.split("\r\n"),
h = a(g, "m=", d);
if (null === h) return e.Logger.debug("Failed to add bandwidth line to sdp, as no m-line found"), c;
var i = b(g, h + 1, -1, "m=");
null === i && (i = g.length);
var j = b(g, h + 1, i, "c=");
if (null === j) return e.Logger.debug("Failed to add bandwidth line to sdp, as no c-line found"), c;
var k = b(g, j + 1, i, "b=AS");
k && g.splice(k, 1);
var l = "b=AS:" + f;
return g.splice(j + 1, 0, l), c = g.join("\r\n")
}
return {
parseStats: function(a) {
"use strict";
var b = 0,
c = [];
return navigator.mozGetUserMedia ? a.forEach(function(d, e) {
var f, g = !1;
e.indexOf("outbound_rtp_audio_") >= 0 ? (g = !0, f = {
type: "ssrc_audio_send",
id: d.id,
stats: {
bytes_sent: d.bytesSent,
codec_name: "",
packets_sent: d.packetsSent,
packets_lost: a.get("outbound_rtcp_audio_" + e.slice(19)).packetsLost,
rtt_ms: a.get("outbound_rtcp_audio_" + e.slice(19)).mozRtt
}
}) : e.indexOf("outbound_rtp_video_") >= 0 ? (g = !0, f = {
type: "ssrc_video_send",
id: d.id,
stats: {
bytes_sent: d.bytesSent,
codec_name: "",
packets_sent: d.packetsSent,
packets_lost: a.get("outbound_rtcp_video_" + e.slice(19)).packetsLost,
firs_rcvd: -1,
plis_rcvd: -1,
nacks_rcvd: -1,
send_frame_width: -1,
send_frame_height: -1,
adapt_reason: -1,
adapt_changes: -1,
framerate_sent: d.framerateMean,
rtt_ms: a.get("outbound_rtcp_video_" + e.slice(19)).mozRtt
}
}) : e.indexOf("inbound_rtp_audio_") >= 0 ? (g = !0, f = {
type: "ssrc_audio_recv",
id: d.id,
stats: {
bytes_rcvd: d.bytesReceived,
delay_estimated_ms: -1,
packets_rcvd: d.packetsReceived,
packets_lost: d.packetsLost,
codec_name: ""
}
}) : e.indexOf("inbound_rtp_video_") >= 0 && (g = !0, f = {
type: "ssrc_video_recv",
id: d.id,
stats: {
bytes_rcvd: d.bytesReceived,
packets_rcvd: d.packetsReceived,
packets_lost: d.packetsLost,
firs_sent: -1,
nacks_sent: -1,
plis_sent: -1,
frame_width: -1,
frame_height: -1,
framerate_rcvd: d.framerateMean,
framerate_output: -1,
current_delay_ms: -1,
codec_name: ""
}
}), g && (c[b] = f, b++)
}) : a.forEach(function(a) {
var d, e = !1;
if ("ssrc" === a.type)
if (e = !0, a.bytesSent)
if (a.googFrameHeightSent) {
var f;
f = !0 === a.googCpuLimitedResolution ? 1 : !0 === a.googBandwidthLimitedResolution ? 2 : !0 === a.googViewLimitedResolution ?
3 : 99, d = {
type: "ssrc_video_send",
id: a.id,
stats: {
bytes_sent: a.bytesSent,
codec_name: a.googCodecName,
packets_sent: a.packetsSent,
packets_lost: a.packetsLost,
firs_rcvd: a.googFirsReceived,
plis_rcvd: a.googPlisReceived,
nacks_rcvd: a.googNacksReceived,
send_frame_width: a.googFrameWidthSent,
send_frame_height: a.googFrameHeightSent,
adapt_reason: f,
adapt_changes: a.googAdaptationChanges,
framerate_sent: a.googFrameRateSent,
rtt_ms: a.googRtt
}
}
} else d = {
type: "ssrc_audio_send",
id: a.id,
stats: {
bytes_sent: a.bytesSent,
codec_name: a.googCodecName,
packets_sent: a.packetsSent,
packets_lost: a.packetsLost,
rtt_ms: a.googRtt
}
};
else d = a.googFrameHeightReceived ? {
type: "ssrc_video_recv",
id: a.id,
stats: {
bytes_rcvd: a.bytesReceived,
packets_rcvd: a.packetsReceived,
packets_lost: a.packetsLost,
firs_sent: a.googFirsSent,
nacks_sent: a.googNacksSent,
plis_sent: a.googPlisSent,
frame_width: a.googFrameWidthReceived,
frame_height: a.googFrameHeightReceived,
framerate_rcvd: a.googFrameRateReceived,
framerate_output: a.googFrameRateDecoded,
current_delay_ms: a.googCurrentDelayMs,
codec_name: a.googCodecName
}
} : {
type: "ssrc_audio_recv",
id: a.id,
stats: {
bytes_rcvd: a.bytesReceived,
delay_estimated_ms: a.googCurrentDelayMs,
packets_rcvd: a.packetsReceived,
packets_lost: a.packetsLost,
codec_name: a.googCodecName
}
};
else "VideoBwe" === a.type && (e = !0, d = {
type: "VideoBWE",
id: "",
stats: {
available_send_bandwidth: a.googAvailableSendBandwidth,
available_receive_bandwidth: a.googAvailableReceiveBandwidth,
transmit_bitrate: a.googTransmitBitrate,
retransmit_bitrate: a.googRetransmitBitrate
}
});
e && (c[b] = d, b++)
}), c
},
parseAudioLevel: function(a) {
for (var b = 0, c = 0, d = {}, e = [], f = [], g = !1, h = a.result(), i = 0; i < h.length; i++) {
var j = h[i];
if ("ssrc" === j.type)
if (j.stat("bytesSent"))
if (j.stat("googFrameHeightSent"));
else {
g = !0;
var k = {};
k.ssrc = j.id, k.level = j.stat("audioInputLevel"), e[b] = k, b++
}
else if (j.stat("googFrameHeightReceived"));
else {
g = !0;
var k = {};
k.ssrc = j.id, k.level = j.stat("audioOutputLevel"), f[c] = k, c++
}
}
return g && (b > 0 && (d.audioInputLevels = e), c > 0 && (d.audioOutputLevels = f)), d
},
setPreferredCodec: function(b, d, g) {
if (!d || !g) return e.Logger.warning("Media type or codec name is not provided."), b;
var h = b.split("\r\n"),
i = a(h, "m=", d);
if (null === i) return b;
var j = c(h, g);
return j && (h[i] = f(h[i], j)), b = h.join("\r\n")
},
setPreferredBitrate: g,
sysInfo: function() {
var a = Object.create({});
a.sdk = {
version: "3.4.1",
type: "JavaScript"
};
var b = navigator.userAgent,
c = /Firefox\/([0-9\.]+)/,
d = /Chrome\/([0-9\.]+)/,
e = /Edge\/([0-9\.]+)/,
f = d.exec(b);
f ? a.runtime = {
name: "Chrome",
version: f[1]
} : (f = c.exec(b)) ? a.runtime = {
name: "FireFox",
version: f[1]
} : (f = e.exec(b)) ? a.runtime = {
name: "Edge",
version: f[1]
} : a.runtime = {
name: "Unknown",
version: "Unknown"
};
var g = /Windows NT ([0-9\.]+)/,
h = /Intel Mac OS X ([0-9_\.]+)/,
i = /iPhone OS ([0-9_\.]+)/,
j = /X11; Linux/,
k = /Android( ([0-9\.]+))?/,
l = /CrOS/;
return (f = g.exec(b)) ? a.os = {
name: "Windows NT",
version: f[1]
} : (f = h.exec(b)) ? a.os = {
name: "Mac OS X",
version: f[1].replace(/_/g, ".")
} : (f = i.exec(b)) ? a.os = {
name: "iPhone OS",
version: f[1].replace(/_/g, ".")
} : (f = j.exec(b)) ? a.os = {
name: "Linux",
version: "Unknown"
} : (f = k.exec(b)) ? a.os = {
name: "Android",
version: f[1] || "Unknown"
} : (f = l.exec(b)) ? a.os = {
name: "Chrome OS",
version: "Unknown"
} : a.os = {
name: "Unknown",
version: "Unknown"
}, a
}
}
}(), e.Base64 = function() {
"use strict";
var a, b, c, d, e, f, g, h, i, j, k, l;
for (a = -1, b = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q",
"r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/"
], c = [], f = 0; f < b.length; f += 1) c[b[f]] = f;
return g = function(a) {
d = a, e = 0
}, h = function() {
var b;
return d ? e >= d.length ? a : (b = 255 & d.charCodeAt(e), e += 1, b) : a
}, i = function(c) {
var d, e, f;
for (g(c), d = "", e = new Array(3), f = !1; !f && (e[0] = h()) !== a;) e[1] = h(), e[2] = h(), d += b[e[0] >> 2],
e[1] !== a ? (d += b[e[0] << 4 & 48 | e[1] >> 4], e[2] !== a ? (d += b[e[1] << 2 & 60 | e[2] >> 6], d += b[63 & e[
2]]) : (d += b[e[1] << 2 & 60], d += "=", f = !0)) : (d += b[e[0] << 4 & 48], d += "=", d += "=", f = !0);
return d
}, j = function() {
if (!d) return a;
for (;;) {
if (e >= d.length) return a;
var b = d.charAt(e);
if (e += 1, c[b]) return c[b];
if ("A" === b) return 0
}
}, k = function(a) {
return a = a.toString(16), 1 === a.length && (a = "0" + a), a = "%" + a, unescape(a)
}, l = function(b) {
var c, d, e;
for (g(b), c = "", d = new Array(4), e = !1; !e && (d[0] = j()) !== a && (d[1] = j()) !== a;) d[2] = j(), d[3] = j(),
c += k(d[0] << 2 & 255 | d[1] >> 4), d[2] !== a ? (c += k(d[1] << 4 & 255 | d[2] >> 2), d[3] !== a ? c += k(d[2] <<
6 & 255 | d[3]) : e = !0) : e = !0;
return c
}, {
encodeBase64: i,
decodeBase64: l
}
}(), e.Logger = function() {
"use strict";
var b = 0,
c = 1,
d = 2,
e = 3,
f = 4,
g = 5,
h = function() {},
i = {
DEBUG: b,
TRACE: c,
INFO: d,
WARNING: e,
ERROR: f,
NONE: g
};
i.log = a.console.log.bind(a.console);
var j = function(b) {
return "function" == typeof a.console[b] ? a.console[b].bind(a.console) : a.console.log.bind(a.console)
},
k = function(a) {
i.debug = a <= b ? j("debug") : h, i.trace = a <= c ? j("trace") : h, i.info = a <= d ? j("info") : h, i.warning =
a <= e ? j("warn") : h, i.error = a <= f ? j("error") : h
};
return k(b), i.setLogLevel = k, i
}(),
function() {
"use strict";
function b(a) {
this.mediaStream = a.mediaStream, a.attributes = a.attributes || {}, this.url = function() {
if ("string" == typeof a.url && "" !== a.url) return a.url
}, this.hasVideo = function() {
return !!a.video
}, this.hasAudio = function() {
return !!a.audio
}, this.attributes = function() {
return a.attributes
}, this.attr = function(b, c) {
return arguments.length > 1 && (a.attributes[b] = c), a.attributes[b]
}, this.id = function() {
return a.id || null
}, this.isScreen = function() {
return !!a.video && "screen" === a.video.device
}, this.bitRate = {
maxVideoBW: void 0,
maxAudioBW: void 0
}, this.toJson = function() {
return {
id: this.id(),
audio: !!this.hasAudio() && a.audio,
video: !!this.hasVideo() && a.video,
attributes: a.attributes
}
}
}
function c(a) {
b.call(this, a), this.hasAudio = function() {
return this.mediaStream ? !!this.mediaStream.getAudioTracks().length : !!a.hasAudio
}, this.hasVideo = function() {
return this.mediaStream ? !!this.mediaStream.getVideoTracks().length : !!a.hasVideo
}
}
function f(a) {
b.call(this, a), this.isMixed = function() {
return !1
}, this.from = a.from;
var c = {},
d = this;
Object.defineProperties(this, {
on: {
get: function() {
return function(a, b) {
return c[a] = c[a] || [], c[a].push(b), d
}
}
},
emit: {
get: function() {
return function(a) {
if (c[a]) {
var b = [].slice.call(arguments, 1);
c[a].map(function(a) {
a.apply(d, b)
})
}
return d
}
}
},
removeListener: {
get: function() {
return function(a, b) {
return void 0 === b ? c[a] = [] : c[a] && c[a].map(function(d, e) {
d === b && c[a].splice(e, 1)
}), d
}
}
},
clearListeners: {
get: function() {
return function() {
return c = {}, d
}
}
}
})
}
function g(a) {
f.call(this, a), this.resolutions = function() {
return a.video.resolutions instanceof Array ? a.video.resolutions.map(function(a) {
return a
}) : []
}, this.isMixed = function() {
return !0
}, this.viewport = function() {
return a.viewport
}
}
function h(a) {
this.url = function() {
if ("string" == typeof a.url && "" !== a.url) return a.url
}, this.id = function() {
return a.id || null
}, this.hasVideo = function() {
return !!a.video
}, this.hasAudio = function() {
return !!a.audio
}, this.toJson = function() {
var b;
return !0 === a.video ? b = {
device: "camera"
} : !1 === a.video ? b = a.video : "object" == typeof a.video && (b = a.video, b.device = a.video.device ||
"camera"), {
id: this.id(),
audio: a.audio,
video: b,
url: this.url()
}
}
}
function i() {
return null !== a.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./) && a.navigator.appVersion.match(
/Chrome\/([\w\W]*?)\./)[1] <= 35
}
function j() {
return a.navigator.appVersion.indexOf("Trident") > -1
}
function k() {
return null !== a.navigator.userAgent.match("Firefox")
}
function l() {
return k() || null !== a.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./) && a.navigator.appVersion.match(
/Chrome\/([\w\W]*?)\./)[1] >= 34
}
function m(a, b) {
return {
width: a,
height: b
}
}
function n(a, b) {
if ("object" == typeof(a = JSON.parse(JSON.stringify(a))) && null !== a && void 0 !== a.url) {
var c = "URL for LocalStream is deprecated, please use ExternalStream instead.";
"function" == typeof console.warn ? console.warn(c) : e.Logger.warning(c);
var f = new d.LocalStream(a);
return void("function" == typeof b && b(null, f))
}
if ("function" != typeof q && !j()) return void("function" == typeof b && b({
code: 1100,
msg: "webrtc support not available"
}));
var g = arguments[3];
void 0 === g && (g = 2);
var h = {};
if (null !== a && "object" == typeof a) {
if (!a.audio && !a.video) return void("function" == typeof b && b({
code: 1107,
msg: "At least one of audio and video must be requested."
}));
if (a.video) {
if ("object" != typeof a.video && a.video && (a.video = Object.create({})), "string" != typeof a.video.device &&
(a.video.device = "camera"), "screen" === a.video.device && !l() && "function" == typeof b) return void b({
code: 1103,
msg: "browser screen sharing not supported"
});
"object" == typeof a.video.resolution && void 0 !== a.video.resolution.width && void 0 !== a.video.resolution.height ?
h.video = JSON.parse(JSON.stringify(m(a.video.resolution.width, a.video.resolution.height))) : h.video = JSON.parse(
JSON.stringify(p[a.video.resolution] || p.unspecified)), "string" == typeof a.video.deviceId && (h.video.deviceId =
a.video.deviceId), j() || i() || (a.video.frameRate instanceof Array && a.video.frameRate.length >= 2 ? h.video
.frameRate = {
min: a.video.frameRate[0],
max: a.video.frameRate[1]
} : "number" == typeof a.video.frameRate ? h.video.frameRate = a.video.frameRate : e.Logger.warning(
"Invalid frame rate value, ignored."))
}
"object" == typeof a.audio ? h.audio = a.audio : !0 === a.audio && (h.audio = !0)
} else if ("function" == typeof b) return void b({
code: 1107,
msg: "USER_INPUT_INVALID"
});
var o = function(c) {
if (("object" != typeof a.video || "screen" !== a.video.device) && (a.audio && 0 === c.getAudioTracks().length ||
a.video && 0 === c.getVideoTracks().length)) {
for (var e = 0; e < c.getTracks().length; e++) c.getTracks()[e].stop();
return void b({
code: 1104,
msg: "Not all device requests are satisfied."
})
}
a.mediaStream = c, a.id = c.id;
var f = new d.LocalStream(a);
if (a.video && "screen" === a.video.device) {
var g = c.getVideoTracks();
g.length > 0 && (g[0].onended = function() {
f.close()
})
}
if (h.video) switch (h.video.width) {
case 320:
f.bitRate.maxVideoBW = 512;
break;
case 640:
f.bitRate.maxVideoBW = 1024;
break;
case 1280:
f.bitRate.maxVideoBW = 2048
}
"function" == typeof b && b(null, f)
},
r = function(c) {
var d = {
code: 1100,
msg: c.name || c
};
switch (d.msg) {
case "Starting video failed":
case "TrackStartError":
if (a.video = {
device: a.video.device,
extensionId: a.video.extensionId
}, g > 0) return void setTimeout(function() {
n(a, b, g - 1)
}, 1);
d.msg = "MEDIA_OPTION_INVALID", d.code = 1104;
break;
case "DevicesNotFoundError":
d.msg = "DEVICES_NOT_FOUND", d.code = 1102;
break;
case "NotSupportedError":
d.msg = "NOT_SUPPORTED", d.code = 1105;
break;
case "PermissionDeniedError":
d.msg = "PERMISSION_DENIED", d.code = 1101;
break;
case "PERMISSION_DENIED":
d.code = 1101;
break;
case "ConstraintNotSatisfiedError":
d.msg = "CONSTRAINT_NOT_SATISFIED", d.code = 1106;
break;
default:
d.msg || (d.msg = "UNDEFINED")
}
"function" == typeof b && b(d)
};
if (a.video && "screen" === a.video.device) {
if (k()) return void 0 !== h.video ? h.video.mediaSource = "window" : h.video = {
mediaSource: "window"
}, void q.apply(navigator, [h, o, r]);
var s = a.video.extensionId || "pndohhifhheefbpeljcmnhnkphepimhe",
t = ["screen", "window", "tab"];
a.audio && t.push("audio");
try {
chrome.runtime.sendMessage(s, {
getStream: t
}, function(a) {
if (void 0 === a) return void("function" == typeof b && b({
code: 1103,
msg: "screen sharing plugin inaccessible"
}));
h.audio = {
mandatory: {
chromeMediaSource: "desktop",
chromeMediaSourceId: a.streamId
}
}, h.video.mandatory = h.video.mandatory || {}, h.video.mandatory.chromeMediaSource = "desktop", h.video.mandatory
.chromeMediaSourceId = a.streamId, h.video.height && (h.video.mandatory.maxHeight = h.video.mandatory.minHeight =
h.video.height, delete h.video.height), h.video.width && (h.video.mandatory.maxWidth = h.video.mandatory.minWidth =
h.video.width, delete h.video.width), h.video.frameRate && ("object" == typeof h.video.frameRate ? (h.video.mandatory
.minFrameRate = h.video.frameRate.min, h.video.mandatory.maxFrameRate = h.video.frameRate.max) : "number" ==
typeof h.video.frameRate ? (h.video.mandatory.minFrameRate = h.video.frameRate, h.video.mandatory.maxFrameRate =
h.video.frameRate) : e.Logger.warning("Invalid frame rate value for screen sharing."), delete h.video.frameRate
), q.apply(navigator, [h, o, r])
})
} catch (u) {
"function" == typeof b && b({
code: 1103,
msg: "screen sharing plugin inaccessible",
err: u
})
}
} else j() ? navigator.getUserMedia(h, o, r) : q.apply(navigator, [h, o, r])
}
function o(a, b) {
if ("object" != typeof a || !a.url) return void("function" == typeof b && b({
code: 1107,
msg: "External stream must have url property"
}));
if (!a.audio && !a.video) return void("function" == typeof b && b({
code: 1107,
msg: "External stream must have video or audio"
}));
var c = new d.ExternalStream(a);
"function" == typeof b && b(null, c)
}
b.prototype.close = function() {
"function" == typeof this.hide && this.hide(), this.mediaStream && this.mediaStream.getTracks().map(function(a) {
"function" == typeof a.stop && a.stop()
}), this.mediaStream = null, "function" == typeof this.unpublish && this.unpublish(), this.channel && "function" ==
typeof this.channel.close && this.channel.close()
}, b.prototype.createObjectURL = function() {
return this.mediaStream ? (a.URL || webkitURL).createObjectURL(this.mediaStream) : ""
}, b.prototype.disableAudio = function(a) {
var b = this;
if (b.hasAudio() && b.mediaStream) {
if (void 0 === a && (a = 0), -1 === a) return b.mediaStream.getAudioTracks().map(function(a) {
return !!a.enabled && (a.enabled = !1, !0)
});
var c = b.mediaStream.getAudioTracks();
if (c && c[a] && c[a].enabled) return c[a].enabled = !1, !0
}
return !1
}, b.prototype.enableAudio = function(a) {
var b = this;
if (b.hasAudio() && b.mediaStream) {
if (void 0 === a && (a = 0), -1 === a) return b.mediaStream.getAudioTracks().map(function(a) {
return !0 !== a.enabled && (a.enabled = !0, !0)
});
var c = b.mediaStream.getAudioTracks();
if (c && c[a] && !0 !== c[a].enabled) return c[a].enabled = !0, !0
}
return !1
}, b.prototype.disableVideo = function(a) {
var b = this;
if (b.hasVideo() && b.mediaStream) {
if (void 0 === a && (a = 0), -1 === a) return b.mediaStream.getVideoTracks().map(function(a) {
return !!a.enabled && (a.enabled = !1, !0)
});
var c = b.mediaStream.getVideoTracks();
if (c && c[a] && c[a].enabled) return c[a].enabled = !1, !0
}
return !1
}, b.prototype.enableVideo = function(a) {
var b = this;
if (b.hasVideo() && b.mediaStream) {
if (void 0 === a && (a = 0), -1 === a) return b.mediaStream.getVideoTracks().map(function(a) {
return !0 !== a.enabled && (a.enabled = !0, !0)
});
var c = b.mediaStream.getVideoTracks();
if (c && c[a] && !0 !== c[a].enabled) return c[a].enabled = !0, !0
}
return !1
}, b.prototype.updateConfiguration = function(a, b) {
if (void 0 !== a) return this.channel ? void this.channel.updateSpec(a, b) :
"This stream has not been published, ignoring"
}, c.prototype = Object.create(b.prototype), f.prototype = Object.create(b.prototype), g.prototype = Object.create(
f.prototype), h.prototype = Object.create({}), c.prototype.constructor = c, f.prototype.constructor = f, g.prototype
.constructor = g, h.prototype.constructor = h;
var p = {
true: {},
unspecified: {},
sif: m(320, 240),
vga: m(640, 480),
hd720p: m(1280, 720),
hd1080p: m(1920, 1080)
},
q = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
c.create = function() {
n.apply(this, arguments)
}, h.create = function() {
o.apply(this, arguments)
}, d.Stream = b, d.LocalStream = c, d.RemoteStream = f, d.RemoteMixedStream = g, d.ExternalStream = h
}(),
function() {
function b() {
var a = arguments[0];
if ("function" == typeof a) {
var b = Array.prototype.slice.call(arguments, 1);
a.apply(null, b)
}
}
function c(a) {
a.session_id = d.sessionId += 1;
var b = {};
if (b.browser = m(), "mozilla" === b.browser) e.Logger.debug("Firefox Stack"), b = f.FirefoxStack(a);
else if ("bowser" === b.browser) e.Logger.debug("Bowser Stack"), b = f.BowserStack(a);
else if ("chrome-stable" === b.browser) e.Logger.debug("Stable!"), b = f.ChromeStableStack(a);
else {
if ("edge" !== b.browser) throw e.Logger.debug("None!"), "WebRTC stack not available";
e.Logger.debug("Edge Stack"), b = f.EdgeORTCStack(a)
}
return b.updateSpec || (b.updateSpec = function(a, b) {
e.Logger.error("Update Configuration not implemented in this browser"), b && b("unimplemented")
}), b
}
function g(a) {
if (!a.video) return new d.RemoteStream(a);
switch (a.video.device) {
case "mcu":
return new d.RemoteMixedStream(a);
default:
return new d.RemoteStream(a)
}
}
function h(a, b, c) {
if (!a || !a.connected) return c("socket not ready");
try {
a.emit(b, function(a, b) {
return "success" === a ? c(null, b) : c(b || "response error")
})
} catch (d) {
c("socket emit error")
}
}
function i(a, b, c, d) {
if (!a || !a.connected) return d("socket not ready");
try {
a.emit(b, c, function(a, b) {
return "success" === a ? d(null, b) : d(b || "response error")
})
} catch (e) {
d("socket emit error")
}
}
function j(a, b, c, d, e) {
if (!a || !a.connected) return e("error", "socket not ready");
try {
a.emit(b, c, d, function(a, b) {
e(a, b)
})
} catch (f) {
e("error", "socket emit error")
}
}
function k(a, c, d, e, f) {
i(a, "customMessage", {
type: "control",
payload: {
action: c,
streamId: d
}
}, function(a, c) {
if (a) return b(f, a);
b(e, c)
})
}
function l(a, c, e, f, g, h) {
if (!(e instanceof d.Stream || e instanceof d.ExternalStream)) return b(h, "Invalid stream");
if (!Array.isArray(f)) return b(h, "Target streams is not a list");
var j, k, l = [];
for (j = 0; j < f.length; j++) {
if (!((k = f[j]) instanceof d.RemoteMixedStream)) return b(h, "Invalid stream found in targetStreams.");
l.push(k.id())
}
i(c, a, {
streamId: e.id(),
mixStreams: l
}, function(a) {
if (a) return b(h, a);
b(g, null)
})
}
d.sessionId = 103;
var m = function() {
var b = "none";
return null !== a.navigator.userAgent.match("Firefox") ? b = "mozilla" : null !== a.navigator.userAgent.match(
"Bowser") ? b = "bowser" : null !== a.navigator.userAgent.match(/Edge\/(\d+).(\d+)$/) ? b = "edge" : null !== a.navigator
.userAgent.match("Chrome") ? a.navigator.appVersion.match(/Chrome\/([\w\W]*?)\./)[1] >= 26 && (b =
"chrome-stable") : null !== a.navigator.userAgent.match("Safari") ? b = "bowser" : null !== a.navigator.userAgent
.match("WebKit") && (b = "bowser"), b
},
n = 0,
o = function(a) {
this.spec = a || {}, this.remoteStreams = {}, this.localStreams = {}, this.state = n, this.conferenceId = ""
};
o.prototype = d.EventDispatcher({}), o.prototype.setIceServers = function() {
var a = this.spec;
return a.userSetIceServers = [], Array.prototype.slice.call(arguments, 0).map(function(b) {
b instanceof Array ? b.map(function(b) {
"object" == typeof b && null !== b ? "string" == typeof b.urls && "" !== b.urls || b.urls instanceof Array ?
a.userSetIceServers.push(b) : "string" == typeof b.url && "" !== b.url && (b.urls = b.url, delete b.url, a.userSetIceServers
.push(b)) : "string" == typeof b && "" !== b && a.userSetIceServers.push({
urls: b
})
}) : "object" == typeof b && null !== b ? "string" == typeof b.urls && "" !== b.urls || b.urls instanceof Array ?
a.userSetIceServers.push(b) : "string" == typeof b.url && "" !== b.url && (b.urls = b.url, delete b.url, a.userSetIceServers
.push(b)) : "string" == typeof b && "" !== b && a.userSetIceServers.push({
urls: b
})
}), a.userSetIceServers
}, o.prototype.getIceServers = function() {
return this.spec.userSetIceServers
}, o.prototype.join = function(a, c, f) {
var h;
try {
h = JSON.parse(e.Base64.decodeBase64(a))
} catch (m) {
return b(f, "invalid token")
}
var i = this,
j = !0 === h.secure,
k = h.host;
if ("string" != typeof k) return b(f, "invalid host");
if (-1 === k.indexOf("http") && (k = j ? "https://" + k : "http://" + k), i.state !== n) return b(f,
"connection state invalid");
i.state = 1, void 0 !== i.socket ? i.socket.connect() : (i.socket = io.connect(k, {
reconnect: !1,
secure: j,
"force new connection": !0
}), i.socket.on("add_stream", function(a) {
if (void 0 !== i.remoteStreams[a.id]) return void e.Logger.warning("stream already added:", a.id);
var b = g({
video: a.video,
audio: a.audio,
id: a.id,
from: a.from,
attributes: a.attributes,
viewport: a.view
}),
c = new d.StreamEvent({
target: i,
type: "stream-added",
stream: b
});
i.remoteStreams[a.id] = b, i.dispatchEvent(c)
}), i.socket.on("update_stream", function(a) {
var b = i.remoteStreams[a.id];
b && b.emit(a.event, a.data)
}), i.socket.on("remove_stream", function(a) {
var b = i.remoteStreams[a.id];
if (b) {
b.close(), delete i.remoteStreams[a.id];
var c = new d.StreamEvent({
target: i,
type: "stream-removed",
stream: b
});
i.dispatchEvent(c)
}
}), i.socket.on("signaling_message_erizo", function(a) {
var b;
(b = a.peerId ? i.remoteStreams[a.peerId] : i.localStreams[a.streamId]) && b.channel && b.channel.processSignalingMessage(
a.mess)
}), i.socket.on("add_recorder", function(a) {
var b = new d.RecorderEvent({
target: i,
type: "recorder-added",
id: a.id
});
i.dispatchEvent(b)
}), i.socket.on("reuse_recorder", function(a) {
var b = new d.RecorderEvent({
target: i,
type: "recorder-continued",
id: a.id
});
i.dispatchEvent(b)
}), i.socket.on("remove_recorder", function(a) {
var b = new d.RecorderEvent({
type: "recorder-removed",
id: a.id
});
i.dispatchEvent(b)
}), i.socket.on("disconnect", function() {
var a = !1;
i.state !== n ? (a = !0, e.Logger.info("Will trigger server-disconnect")) : e.Logger.info(
"Will not trigger server-disconnect"), i.state = n, i.myId = null;
var b, c;
for (b in i.remoteStreams) i.remoteStreams.hasOwnProperty(b) && (c = i.remoteStreams[b], c.close(), delete i.remoteStreams[
b]);
for (b in i.localStreams) i.localStreams.hasOwnProperty(b) && (c = i.localStreams[b], c.channel && "function" ==
typeof c.channel.close && c.channel.close(), delete i.localStreams[b]);
try {
i.socket.disconnect()
} catch (m) {}
if (a) {
var f = new d.ClientEvent({
target: i,
type: "server-disconnected"
});
i.dispatchEvent(f)
}
}), i.socket.on("user_join", function(a) {
var b = new d.ClientEvent({
target: i,
type: "user-joined",
user: a.user
});
i.dispatchEvent(b)
}), i.socket.on("user_leave", function(a) {
var b = new d.ClientEvent({
target: i,
type: "user-left",
user: a.user
});
i.dispatchEvent(b)
}), i.socket.on("custom_message", function(a) {
var b = new d.MessageEvent({
target: i,
type: "message-received",
msg: a
});
i.dispatchEvent(b)
}), i.socket.on("connect_failed", function(a) {
b(f, a || "connection_failed")
}), i.socket.on("error", function(a) {
b(f, a || "connection_error")
}), i.socket.on("connection_failed", function(a) {
if (e.Logger.error("MCU reports connection failed for stream: " + a.streamId), void 0 !== i.localStreams[a.streamId]) {
var b = i.localStreams[a.streamId];
i.unpublish(b), delete i.localStreams[a.streamId]
} else i.unsubscribe(i.remoteStreams[a.streamId]);
if (i.state !== n) {
var c = new d.StreamEvent({
type: "stream-failed"
});
i.dispatchEvent(c)
}
}), i.socket.on("stream-publish", function(a) {
var b = i.localStreams[a.id];
b && (console.log("Stream published"), i.dispatchEvent(new d.StreamEvent({
target: i,
type: "stream-published",
stream: b
})))
}));
try {
var l = {
token: a,
userAgent: d.Common.sysInfo()
};
i.socket.emit("login", l, function(a, d) {
if ("success" === a) {
i.myId = d.clientId, i.conferenceId = d.id, i.state = 2;
var e = [];
void 0 !== d.streams && (e = d.streams.map(function(a) {
return a.viewport = a.view, i.remoteStreams[a.id] = g(a), i.remoteStreams[a.id]
}));
var h;
if (void 0 !== d.users)
for (var j = 0; j < d.users.length; j++)
if (d.users[j].id === d.clientId) {
h = d.users[j];
break
} return b(c, {
streams: e,
users: d.users,
self: h
})
}
return b(f, d || "response error")
})
} catch (o) {
b(f, "socket emit error")
}
}, o.prototype.publish = function(a, f, g, h) {
var l = this;
if (a = a || {}, "function" == typeof f ? (h = g, g = f) : "object" == typeof f && null !== f || (f = {}), !(a instanceof d
.LocalStream || a instanceof d.ExternalStream) || ("object" != typeof a.mediaStream || null === a.mediaStream) &&
void 0 === a.url()) return b(h, "invalid stream");
if (f.videoCodec = f.videoCodec || "h264", void 0 !== l.localStreams[a.id()]) return b(h, "already published");
var m = a.toJson();
if (!0 === f.unmix && (m.unmix = !0), void 0 !== a.url()) return m.state = "url", m.transport = f.transport, m.bufferSize =
f.bufferSize, void j(l.socket, "publish", m, a.url(), function(c, d) {
if ("success" !== c) return b(h, "error" === c ? d : c);
a.id = function() {
return d
}, a.unpublish = function(b, c) {
l.unpublish(a, b, c)
}, l.localStreams[d] = a, b(g, a)
});
m.state = "erizo", j(l.socket, "publish", m, void 0, function(d, m) {
if ("error" === d) return b(h, m);
if ("timeout" === d) return b(h, d);
a.id = function() {
return m
}, l.localStreams[m] = a, a.channel = c({
callback: function(a) {
console.log("Sending message", a), j(l.socket, "signaling_message", {
streamId: m,
msg: a
}, void 0, function() {})
},
video: a.hasVideo(),
audio: a.hasAudio(),
iceServers: l.getIceServers(),
iceTransportPolicy: f.iceTransportPolicy,
pcConstraints: f.pcConstraints,
maxAudioBW: f.maxAudioBW,
maxVideoBW: f.maxVideoBW,
audioCodec: f.audioCodec,
videoCodec: f.videoCodec
});
var n = function() {
a.signalOnPlayAudio = function(a, b) {
k(l.socket, "audio-out-on", m, a, b)
}, a.signalOnPauseAudio = function(a, b) {
k(l.socket, "audio-out-off", m, a, b)
}, a.signalOnPlayVideo = function(a, b) {
k(l.socket, "video-out-on", m, a, b)
}, a.signalOnPauseVideo = function(a, b) {
k(l.socket, "video-out-off", m, a, b)
}, a.unpublish = function(b, c) {
l.unpublish(a, b, c)
}, b(g, a), h = function() {}, n = function() {}
},
o = function() {
i(l.socket, "unpublish", m, function() {}, function() {}), a.channel.close(), a.channel = void 0, b(h,
"peer connection failed"), n = function() {}, o = function() {}
};
a.channel.oniceconnectionstatechange = function(a) {
switch (a) {
case "completed":
case "connected":
n();
break;
case "checking":
case "closed":
break;
case "failed":
o();
break;
default:
e.Logger.warning("unknown ice connection state:", a)
}
}, a.channel.addStream(a.mediaStream), a.channel.createOffer(!1)
})
}, o.prototype.unpublish = function(a, c, e) {
var f = this;
if (!(a instanceof d.LocalStream || a instanceof d.ExternalStream)) return b(e, "invalid stream");
i(f.socket, "unpublish", a.id(), function(d) {
if (a.channel && "function" == typeof a.channel.close && (a.channel.close(), a.channel = null), delete f.localStreams[
a.id()], a.id = function() {
return null
}, a.signalOnPlayAudio = void 0, a.signalOnPauseAudio = void 0, a.signalOnPlayVideo = void 0, a.signalOnPauseVideo =
void 0, delete a.unpublish, d) return b(e, d);
b(c, null)
})
}, o.prototype.subscribe = function(a, f, g, h) {
var l = this,
n = !1,
o = !1;
return "function" == typeof f ? (h = g, g = f, f = {}) : "object" == typeof f && null !== f || (f = {}), a instanceof d
.RemoteStream ? !1 === f.audio && !1 === f.video ? b(h, "no audio or video to subscribe.") : (f.videoCodec = f.videoCodec ||
"h264", a.isMixed() || "object" != typeof f.video || !f.video.resolution && !f.video.qualityLevel ? ("object" ==
typeof f.video && f.video.qualityLevel && (f.video.quality_level = f.video.qualityLevel, delete f.video.qualityLevel),
void j(l.socket, "subscribe", {
streamId: a.id(),
audio: a.hasAudio() && !1 !== f.audio,
video: a.hasVideo() && f.video,
browser: m()
}, void 0, function(d, m) {
if ("error" === d || "timeout" === d) return b(h, m || d);
a.channel = c({
callback: function(b) {
j(l.socket, "signaling_message", {
streamId: a.id(),
msg: b,
browser: a.channel.browser
}, void 0, function() {})
},
audio: a.hasAudio() && !1 !== f.audio,
video: a.hasVideo() && !1 !== f.video,
iceServers: l.getIceServers(),
iceTransportPolicy: f.iceTransportPolicy,
videoCodec: f.videoCodec
}), a.channel.onaddstream = function(c) {
a.mediaStream = c.stream, o && !1 === n ? (n = !0, b(g, a)) : n = !0
};
var p = function() {
a.signalOnPlayAudio = function(b, c) {
k(l.socket, "audio-in-on", a.id(), b, c)
}, a.signalOnPauseAudio = function(b, c) {
k(l.socket, "audio-in-off", a.id(), b, c)
}, a.signalOnPlayVideo = function(b, c) {
k(l.socket, "video-in-on", a.id(), b, c)
}, a.signalOnPauseVideo = function(b, c) {
k(l.socket, "video-in-off", a.id(), b, c)
}, n && !1 === o ? (o = !0, b(g, a)) : o = !0, h = function() {}, p = function() {}
},
q = function() {
i(l.socket, "unsubscribe", a.id(), function() {}, function() {}), a.close(), a.signalOnPlayAudio = void 0,
a.signalOnPauseAudio = void 0, a.signalOnPlayVideo = void 0, a.signalOnPauseVideo = void 0, b(h,
"peer connection failed"), p = function() {}, q = function() {}
};
a.channel.oniceconnectionstatechange = function(a) {
switch (a) {
case "completed":
case "connected":
p();
break;
case "checking":
case "closed":
break;
case "failed":
q();
break;
default:
e.Logger.warning("unknown ice connection state:", a)
}
}, a.channel.createOffer(!0)
})) : b(h, "Resolution and quality level settings are not available for non-mixed stream.")) : b(h,
"invalid stream")
}, o.prototype.unsubscribe = function(a, c, e) {
var f = this;
if (!(a instanceof d.RemoteStream)) return b(e, "invalid stream");
i(f.socket, "unsubscribe", a.id(), function(d, f) {
if (d) return b(e, d);
a.close(), a.signalOnPlayAudio = void 0, a.signalOnPauseAudio = void 0, a.signalOnPlayVideo = void 0, a.signalOnPauseVideo =
void 0, b(c, f)
})
}, o.prototype.onMessage = function(a) {
"function" == typeof a && this.on("message-received", a)
}, d.ConferenceClient = function() {
"use strict";
var a = function(a) {
o.call(this, a), this.join = function(a, b, c) {
o.prototype.join.call(this, a, b, c)
}, this.leave = function() {
h(this.socket, "logout", function(a) {
a && e.Logger.warning("Server returns error for logout event")
}), this.socket.disconnect()
}, this.send = function(a, c, d, e) {
if (void 0 === a || null === a || "function" == typeof a) return b(e, "nothing to send");
if (void 0 === c) c = "all";
else if ("string" == typeof c);
else {
if ("function" != typeof c) return b(e, "invalid receiver");
e = d, d = c, c = "all"
}
i(this.socket, "customMessage", {
type: "data",
data: a,
receiver: c
}, function(a, c) {
if (a) return b(e, a);
b(d, c)
})
}, this.mix = function(a, b, c, d) {
return l("mix", this.socket, a, b, c, d)
}, this.unmix = function(a, b, c, d) {
return l("unmix", this.socket, a, b, c, d)
}, this.shareScreen = function(a, c, f) {
e.Logger.warning(
"shareScreen is deprecated, please create a LocalStream and publish it to specific conference.");
var g = this;
"function" == typeof a && (f = c, c = a, a = {}), a = a || {}, d.LocalStream.create({
video: {
device: "screen",
extensionId: a.extensionId,
resolution: a.resolution ? a.resolution : {
width: screen.width,
height: screen.height
},
frameRate: a.frameRate
},
audio: !1
}, function(d, e) {
if (d) return b(f, d);
g.publish(e, {
maxVideoBW: a.maxVideoBW,
videoCodec: a.videoCodec
}, function(a) {
b(c, a)
}, function(a) {
b(f, a)
})
})
}, this.playAudio = function(a, b, c) {
if (a instanceof d.Stream && a.hasAudio() && "function" == typeof a.signalOnPlayAudio) return a.signalOnPlayAudio(
b, c);
"function" == typeof c && c("unable to call playAudio")
}, this.pauseAudio = function(a, b, c) {
if (a instanceof d.Stream && a.hasAudio() && "function" == typeof a.signalOnPauseAudio) return a.signalOnPauseAudio(
b, c);
"function" == typeof c && c("unable to call pauseAudio")
}, this.playVideo = function(a, b, c) {
if (a instanceof d.Stream && a.hasVideo() && "function" == typeof a.signalOnPlayVideo) return a.signalOnPlayVideo(
b, c);
"function" == typeof c && c("unable to call playVideo")
}, this.pauseVideo = function(a, b, c) {
if (a instanceof d.Stream && a.hasVideo() && "function" == typeof a.signalOnPauseVideo) return a.signalOnPauseVideo(
b, c);
"function" == typeof c && c("unable to call pauseVideo")
}, this.addExternalOutput = function(a, c, d, e) {
var f = this;
"function" == typeof c ? (e = d, d = c, c = {}) : "object" == typeof c && null !== c || (c = {}), c.url = a, c
.video && c.video.resolution && (c.resolution = c.video.resolution), i(f.socket, "addExternalOutput", c,
function(a) {
if (a) return b(e, a);
b(d)
})
}, this.updateExternalOutput = function(a, c, d, e) {
var f = this;
"function" == typeof c ? (e = d, d = c, c = {}) : "object" == typeof c && null !== c || (c = {}), c.url = a, c
.video && c.video.resolution && (c.resolution = c.video.resolution), i(f.socket, "updateExternalOutput", c,
function(a) {
if (a) return b(e, a);
b(d)
})
}, this.removeExternalOutput = function(a, c, d) {
var e = this;
if ("string" != typeof a) return void b(d, "URL should be string.");
i(e.socket, "removeExternalOutput", {
url: a
}, function(a) {
if (a) return b(d, a);
b(c)
})
}, this.startRecorder = function(a, c, d) {
var e = this;
"function" == typeof a ? (d = c, c = a, a = {}) : "object" == typeof a && null !== a || (a = {}), i(e.socket,
"startRecorder", a,
function(a, e) {
if (a) return b(d, a);
b(c, e)
})
}, this.stopRecorder = function(a, c, d) {
var e = this;
"function" == typeof a ? (d = c, c = a, a = {}) : "object" == typeof a && null !== a || (a = {}), i(e.socket,
"stopRecorder", a,
function(a, e) {
if (a) return b(d, a);
b(c, e)
})
}, this.getRegion = function(a, c, d) {
var e = this;
if ("object" != typeof a || null === a || "string" != typeof a.id || "" === a.id) return b(d,
"invalid options");
var f = {
id: a.id,
mixStreamId: a.mixedStreamId
};
i(e.socket, "getRegion", f, function(a, e) {
if (a) return b(d, a);
b(c, e)
})
}, this.setRegion = function(a, c, d) {
var e = this;
if ("object" != typeof a || null === a || "string" != typeof a.id || "" === a.id || "string" != typeof a.region ||
"" === a.region) return b(d, "invalid options");
var f = {
id: a.id,
region: a.region,
mixStreamId: a.mixedStreamId
};
i(e.socket, "setRegion", f, function(a, e) {
if (a) return b(d, a);
b(c, e)
})
}, this.setVideoBitrate = function(a, c, d) {
var e = this;
"function" == typeof a ? (d = c, c = a, a = {}) : "object" == typeof a && null !== a || (a = {}), i(e.socket,
"setVideoBitrate", a,
function(a, e) {
if (a) return b(d, a);
b(c, e)
})
}, this.getConnectionStats = function(a, c, d) {
a && a.channel && "function" == typeof a.channel.getConnectionStats ? a.channel.getConnectionStats(function(a) {
b(c, a)
}, function(a) {
b(d, a)
}) : b(d, "invalid stream.")
}, this.mute = function(a, c, e, f) {
a instanceof d.Stream || b(f, "Invalid stream"), void 0 !== c && "audio" !== c && "video" !== c && b(f,
"Invalid track kind.");
var g = c || "av";
i(this.socket, "mute", {
streamId: a.id(),
track: g
}, function(a) {
if (a) return b(f, a);
b(e)
})
}, this.unmute = function(a, c, e, f) {
a instanceof d.Stream || b(f, "Invalid stream"), void 0 !== c && "audio" !== c && "video" !== c && b(f,
"Invalid track kind.");
var g = c || "av";
i(this.socket, "unmute", {
streamId: a.id(),
track: g
}, function(a) {
if (a) return b(f, a);
b(e)
})
}
};
return a.prototype = Object.create(o.prototype), a.prototype.constructor = a, a.create = function(b) {
return new a(b)
}, a
}()
}(), f.ChromeStableStack = function(a) {
"use strict";
var b = {},
c = RTCPeerConnection;
if (b.pc_config = {
iceServers: []
}, b.con = {
optional: [{
DtlsSrtpKeyAgreement: !0
}]
}, a.iceServers instanceof Array && (b.pc_config.iceServers = a.iceServers), a.iceTransportPolicy && (b.pc_config.iceTransportPolicy =
a.iceTransportPolicy), void 0 !== a.pcConstraints && void 0 !== a.pcConstraints.optional && a.pcConstraints.optional instanceof Array)
for (var f in a.pcConstraints.optional) b.con.optional.push(a.pcConstraints.optional[f]);
void 0 === a.audio && (a.audio = !0), void 0 === a.video && (a.video = !0), b.mediaConstraints = {
mandatory: {
OfferToReceiveVideo: a.video,
OfferToReceiveAudio: a.audio
}
};
var g = function(a) {
console.log("Error in Stack ", a)
};
b.peerConnection = new c(b.pc_config, b.con);
var h = function(b) {
var c, d;
return a.video && a.maxVideoBW && (b = b.replace(/b=AS:.*\r\n/g, ""), c = b.match(/m=video.*\r\n/), null == c && (
c = b.match(/m=video.*\n/)), c && c.length > 0 && (d = c[0] + "b=AS:" + a.maxVideoBW + "\r\n", b = b.replace(c[
0], d))), a.audio && a.maxAudioBW && (c = b.match(/m=audio.*\r\n/), null == c && (c = b.match(/m=audio.*\n/)), c &&
c.length > 0 && (d = c[0] + "b=AS:" + a.maxAudioBW + "\r\n", b = b.replace(c[0], d))), b
},
i = function(b) {
return a.audioCodec ? d.Common.setPreferredCodec(b, "audio", a.audioCodec) : b
},
j = function(b) {
return a.videoCodec ? d.Common.setPreferredCodec(b, "video", a.videoCodec) : b
},
k = function(a) {
var b = i(a);
return b = j(b)
};
b.close = function() {
b.state = "closed", "closed" !== b.peerConnection.signalingState && b.peerConnection.close()
}, a.localCandidates = [], b.peerConnection.onicecandidate = function(b) {
if (b.candidate) {
b.candidate.candidate.match(/a=/) || (b.candidate.candidate = "a=" + b.candidate.candidate);
var c = {
sdpMLineIndex: b.candidate.sdpMLineIndex,
sdpMid: b.candidate.sdpMid,
candidate: b.candidate.candidate
};
a.remoteDescriptionSet ? a.callback({
type: "candidate",
candidate: c
}) : (a.localCandidates.push(c), e.Logger.info("Storing candidate: ", a.localCandidates.length, c))
} else console.log("End of candidates.")
}, b.peerConnection.onaddstream = function(a) {
b.onaddstream && b.onaddstream(a)
}, b.peerConnection.onremovestream = function(a) {
b.onremovestream && b.onremovestream(a)
}, b.peerConnection.oniceconnectionstatechange = function(a) {
b.oniceconnectionstatechange && b.oniceconnectionstatechange(a.currentTarget.iceConnectionState)
};
var l, m, n = function(c) {
c.sdp = h(c.sdp), c.sdp = k(c.sdp.replace(/a=ice-options:google-ice\r\n/g, "")), a.callback({
type: c.type,
sdp: c.sdp
}), l = c, b.peerConnection.setLocalDescription(c)
},
o = function(c) {
c.sdp = h(c.sdp), a.callback({
type: c.type,
sdp: c.sdp
}), l = c, b.peerConnection.setLocalDescription(c)
};
return b.updateSpec = function(c, d) {
(c.maxVideoBW || c.maxAudioBW) && (c.maxVideoBW && (console.log("Maxvideo Requested", c.maxVideoBW), a.maxVideoBW =
c.maxVideoBW), c.maxAudioBW && (console.log("Maxaudio Requested", c.maxAudioBW), a.maxAudioBW = c.maxAudioBW), l
.sdp = h(l.sdp), b.peerConnection.setLocalDescription(l, function() {
m.sdp = h(m.sdp), b.peerConnection.setRemoteDescription(new RTCSessionDescription(m), function() {
a.remoteDescriptionSet = !0, d && d("success")
})
}))
}, b.createOffer = function(a) {
!0 === a ? b.peerConnection.createOffer(n, g, b.mediaConstraints) : b.peerConnection.createOffer(n, g)
}, b.iceRestart = function() {
var c = {
offerToReceiveAudio: a.audio,
offerToReceiveVideo: a.video,
iceRestart: !0
};
b.peerConnection.createOffer(n, g, c)
}, b.addStream = function(a) {
b.peerConnection.addStream(a)
}, a.remoteCandidates = [], a.remoteDescriptionSet = !1, b.processSignalingMessage = function(c) {
if ("offer" === c.type) c.sdp = h(c.sdp), b.peerConnection.setRemoteDescription(new RTCSessionDescription(c),
function() {
b.peerConnection.createAnswer(o, function(a) {
e.Logger.error("Error: ", a)
}, b.mediaConstraints), a.remoteDescriptionSet = !0
},
function(a) {
e.Logger.error("Error setting Remote Description", a)
});
else if ("answer" === c.type) console.log("Set remote and local description", c.sdp), c.sdp = h(c.sdp), m = c, b.peerConnection
.setRemoteDescription(new RTCSessionDescription(c), function() {
for (a.remoteDescriptionSet = !0, console.log("Candidates to be added: ", a.remoteCandidates.length, a.remoteCandidates); a
.remoteCandidates.length > 0;) b.peerConnection.addIceCandidate(a.remoteCandidates.shift());
for (console.log("Local candidates to send:", a.localCandidates.length); a.localCandidates.length > 0;) a.callback({
type: "candidate",
candidate: a.localCandidates.shift()
})
});
else if ("candidate" === c.type) try {
var d;
d = "object" == typeof c.candidate ? c.candidate : JSON.parse(c.candidate), d.candidate = d.candidate.replace(
/a=/g, ""), d.sdpMLineIndex = parseInt(d.sdpMLineIndex, 10);
var f = new RTCIceCandidate(d);
a.remoteDescriptionSet ? b.peerConnection.addIceCandidate(f) : (a.remoteCandidates.push(f), console.log(
"Candidates stored: ", a.remoteCandidates.length, a.remoteCandidates))
} catch (g) {
e.Logger.error("Error parsing candidate", c.candidate)
}
}, b.getConnectionStats = function(a, c) {
b.peerConnection.getStats(null, function(b) {
a(d.Common.parseStats(b))
}, c)
}, b
}, f.FirefoxStack = function(a) {
"use strict";
function b(a) {
var b = a.split("\n");
return {
indexAudio: b.findIndex(function(a) {
return /^m=audio/.test(a)
}),
indexVideo: b.findIndex(function(a) {
return /^m=video/.test(a)
})
}
}
function c(a) {
var b = a.split("\n"),
c = b.findIndex(function(a) {
return /^m=audio/.test(a)
}),
d = b.findIndex(function(a) {
return /^m=video/.test(a)
});
if (e.Logger.debug("indexAudio: ", c, " indexVideo: ", d), c < d) {
var f = b.slice(c, d),
g = b.slice(d, b.length - 1);
a = b.slice(0, c).join("\n") + "\n" + g.join("\n") + "\n" + f.join("\n")
}
return a
}
var f = {},
g = mozRTCPeerConnection,
h = mozRTCSessionDescription,
i = mozRTCIceCandidate;
f.pc_config = {
iceServers: []
}, a.iceServers instanceof Array && (f.pc_config.iceServers = a.iceServers), void 0 === a.audio && (a.audio = !0),
void 0 === a.video && (a.video = !0), f.mediaConstraints = {
offerToReceiveAudio: a.audio,
offerToReceiveVideo: a.video,
mozDontOfferDataChannel: !0
};
var j = function(a) {
e.Logger.error("Error in Stack ", a)
},
k = !1;
f.peerConnection = new g(f.pc_config), a.localCandidates = [], f.peerConnection.onicecandidate = function(b) {
b.candidate ? (k = !0, b.candidate.candidate.match(/a=/) || (b.candidate.candidate = "a=" + b.candidate.candidate),
a.remoteDescriptionSet ? a.callback({
type: "candidate",
candidate: b.candidate
}) : (a.localCandidates.push(b.candidate), console.log("Local Candidates stored: ", a.localCandidates.length, a.localCandidates))
) : console.log("End of candidates.")
};
var l = function(b) {
return a.audioCodec ? d.Common.setPreferredCodec(b, "audio", a.audioCodec) : b
},
m = function(b) {
return a.videoCodec ? d.Common.setPreferredCodec(b, "video", a.videoCodec) : b
},
n = function(a) {
var b = l(a);
return b = m(b)
};
f.peerConnection.onaddstream = function(a) {
f.onaddstream && f.onaddstream(a)
}, f.peerConnection.onremovestream = function(a) {
f.onremovestream && f.onremovestream(a)
}, f.peerConnection.oniceconnectionstatechange = function(a) {
f.oniceconnectionstatechange && f.oniceconnectionstatechange(a.currentTarget.iceConnectionState)
};
var o, p = function(b) {
var c, d;
return a.video && a.maxVideoBW && (c = b.match(/m=video.*\r\n/), null == c && (c = b.match(/m=video.*\n/)), c && c
.length > 0 && (d = c[0] + "b=AS:" + a.maxVideoBW + "\r\n", b = b.replace(c[0], d))), a.audio && a.maxAudioBW &&
(c = b.match(/m=audio.*\r\n/), null == c && (c = b.match(/m=audio.*\n/)), c && c.length > 0 && (d = c[0] +
"b=AS:" + a.maxAudioBW + "\r\n", b = b.replace(c[0], d))), b
},
q = function(b) {
b.sdp = p(b.sdp), b.sdp = n(b.sdp.replace(/a=ice-options:google-ice\r\n/g, "")), a.callback(b), o = b
},
r = function(b) {
b.sdp = p(b.sdp), b.sdp = b.sdp.replace(/a=ice-options:google-ice\r\n/g, ""), a.callback(b), o = b, f.peerConnection
.setLocalDescription(o)
};
return f.createOffer = function(a) {
!0 === a ? f.peerConnection.createOffer(q, j, f.mediaConstraints) : f.peerConnection.createOffer(q, j)
}, f.addStream = function(a) {
f.peerConnection.addStream(a)
}, a.remoteCandidates = [], a.remoteDescriptionSet = !1, f.close = function() {
f.state = "closed", "closed" !== f.peerConnection.signalingState && f.peerConnection.close()
}, f.iceRestart = function() {
var b = {
offerToReceiveAudio: a.audio,
offerToReceiveVideo: a.video,
iceRestart: !0
};
f.peerConnection.createOffer(q, j, b)
}, f.processSignalingMessage = function(d) {
if ("offer" === d.type) {
d.sdp = p(d.sdp);
var g = b(r.sdp);
e.Logger.debug("Offer: local sdp", g), g.indexAudio > g.indexVideo && (d.sdp = c(d.sdp)), f.peerConnection.setRemoteDescription(
new h(d),
function() {
f.peerConnection.createAnswer(r, function(a) {
e.Logger.error("Error", a)
}, f.mediaConstraints), a.remoteDescriptionSet = !0
},
function(a) {
e.Logger.error("Error setting Remote Description", a, "sdp: ", d.sdp)
})
} else if ("answer" === d.type) {
console.log("Set remote and local description , remote sdp", d.sdp), d.sdp = p(d.sdp);
var j = b(o.sdp);
e.Logger.debug("Answer: local sdp", j), j.indexAudio > j.indexVideo && (d.sdp = c(d.sdp)), f.peerConnection.setLocalDescription(
o,
function() {
f.peerConnection.setRemoteDescription(new h(d), function() {
for (a.remoteDescriptionSet = !0, e.Logger.info("Remote Description successfully set"); a.remoteCandidates.length >
0 && k;) e.Logger.info("Setting stored remote candidates"), f.peerConnection.addIceCandidate(a.remoteCandidates
.shift());
for (; a.localCandidates.length > 0;) e.Logger.info("Sending Candidate from list"), a.callback({
type: "candidate",
candidate: a.localCandidates.shift()
})
}, function(a) {
e.Logger.error("Error Setting Remote Description", a, "sdp: ", d.sdp, "Loacal sdp: ", o)
})
},
function(a) {
e.Logger.error("Failure setting Local Description", a, "sdp: ", d.sdp, "Loacal sdp: ", o)
})
} else if ("candidate" === d.type) try {
var l;
l = "object" == typeof d.candidate ? d.candidate : JSON.parse(d.candidate), l.candidate = l.candidate.replace(
/ generation 0/g, ""), l.candidate = l.candidate.replace(/ udp /g, " UDP "), l.sdpMLineIndex = parseInt(l.sdpMLineIndex,
10);
var m = new i(l);
if (a.remoteDescriptionSet && k)
for (f.peerConnection.addIceCandidate(m); a.remoteCandidates.length > 0;) e.Logger.info(
"Setting stored remote candidates"), f.peerConnection.addIceCandidate(a.remoteCandidates.shift());
else a.remoteCandidates.push(m)
} catch (n) {
e.Logger.error("Error parsing candidate", d.candidate, n)
}
}, f.getConnectionStats = function(a, b) {
f.peerConnection.getStats(null, function(b) {
a(d.Common.parseStats(b))
}, b)
}, f
}, f.EdgeORTCStack = function(b) {
"use strict";
var c = {};
c.generateIdentifier = function() {
return Math.random().toString(36).substr(2, 10)
}, c.localCName = c.generateIdentifier(), c.splitLines = function(a) {
return a.trim().split("\n").map(function(a) {
return a.trim()
})
}, c.splitSections = function(a) {
return a.split("\nm=").map(function(a, b) {
return (b > 0 ? "m=" + a : a).trim() + "\r\n"
})
}, c.matchPrefix = function(a, b) {
return c.splitLines(a).filter(function(a) {
return 0 === a.indexOf(b)
})
}, c.parseCandidate = function(a) {
var b;
b = 0 === a.indexOf("a=candidate:") ? a.substring(12).split(" ") : a.substring(10).split(" ");
for (var c = {
foundation: b[0],
component: b[1],
protocol: b[2].toLowerCase(),
priority: parseInt(b[3], 10),
ip: b[4],
port: parseInt(b[5], 10),
type: b[7]
}, d = 8; d < b.length; d += 2) switch (b[d]) {
case "raddr":
c.relatedAddress = b[d + 1];
break;
case "rport":
c.relatedPort = parseInt(b[d + 1], 10);
break;
case "tcptype":
c.tcpType = b[d + 1]
}
return c
}, c.writeCandidate = function(a) {
var b = [];
b.push(a.foundation), b.push(a.component), b.push(a.protocol.toUpperCase()), b.push(a.priority), b.push(a.ip), b.push(
a.port);
var c = a.type;
return b.push("typ"), b.push(c), "host" !== c && a.relatedAddress && a.relatedPort && (b.push("raddr"), b.push(a.relatedAddress),
b.push("rport"), b.push(a.relatedPort)), a.tcpType && "tcp" === a.protocol.toLowerCase() && (b.push("tcptype"),
b.push(a.tcpType)), "candidate:" + b.join(" ")
}, c.parseRtpMap = function(a) {
var b = a.substr(9).split(" "),
c = {
payloadType: parseInt(b.shift(), 10)
};
return b = b[0].split("/"), c.name = b[0], c.clockRate = parseInt(b[1], 10), c.numChannels = 3 === b.length ?
parseInt(b[2], 10) : 1, c
}, c.writeRtpMap = function(a) {
var b = a.payloadType;
return void 0 !== a.preferredPayloadType && (b = a.preferredPayloadType), "a=rtpmap:" + b + " " + a.name + "/" + a
.clockRate + (1 !== a.numChannels ? "/" + a.numChannels : "") + "\r\n"
}, c.parseExtmap = function(a) {
var b = a.substr(9).split(" ");
return {
id: parseInt(b[0], 10),
uri: b[1]
}
}, c.writeExtmap = function(a) {
return "a=extmap:" + (a.id || a.preferredId) + " " + a.uri + "\r\n"
}, c.parseFmtp = function(a) {
for (var b, c = {}, d = a.substr(a.indexOf(" ") + 1).split(";"), e = 0; e < d.length; e++) b = d[e].trim().split(
"="), c[b[0].trim()] = b[1];
return c
}, c.writeFmtp = function(a) {
var b = "",
c = a.payloadType;
if (void 0 !== a.preferredPayloadType && (c = a.preferredPayloadType), a.parameters && Object.keys(a.parameters).length) {
var d = [];
Object.keys(a.parameters).forEach(function(b) {
d.push(b + "=" + a.parameters[b])
}), b += "a=fmtp:" + c + " " + d.join(";") + "\r\n"
}
return b
}, c.parseRtcpFb = function(a) {
var b = a.substr(a.indexOf(" ") + 1).split(" ");
return {
type: b.shift(),
parameter: b.join(" ")
}
}, c.writeRtcpFb = function(a) {
var b = "",
c = a.payloadType;
return void 0 !== a.preferredPayloadType && (c = a.preferredPayloadType), a.rtcpFeedback && a.rtcpFeedback.length &&
a.rtcpFeedback.forEach(function(a) {
b += "a=rtcp-fb:" + c + " " + a.type + " " + a.parameter + "\r\n"
}), b
}, c.parseSsrcMedia = function(a) {
var b = a.indexOf(" "),
c = {
ssrc: parseInt(a.substr(7, b - 7), 10)
},
d = a.indexOf(":", b);
return d > -1 ? (c.attribute = a.substr(b + 1, d - b - 1), c.value = a.substr(d + 1)) : c.attribute = a.substr(b +
1), c
}, c.getDtlsParameters = function(a, b) {
var d = c.splitLines(a);
d = d.concat(c.splitLines(b));
var e = d.filter(function(a) {
return 0 === a.indexOf("a=fingerprint:")
})[0].substr(14);
return {
role: "auto",
fingerprints: [{
algorithm: e.split(" ")[0],
value: e.split(" ")[1]
}]
}
}, c.writeDtlsParameters = function(a, b) {
var c = "a=setup:" + b + "\r\n";
return a.fingerprints.forEach(function(a) {
c += "a=fingerprint:" + a.algorithm + " " + a.value + "\r\n"
}), c
}, c.getIceParameters = function(a, b) {
var d = c.splitLines(a);
return d = d.concat(c.splitLines(b)), {
usernameFragment: d.filter(function(a) {
return 0 === a.indexOf("a=ice-ufrag:")
})[0].substr(12),
password: d.filter(function(a) {
return 0 === a.indexOf("a=ice-pwd:")
})[0].substr(10)
}
}, c.writeIceParameters = function(a) {
return "a=ice-ufrag:" + a.usernameFragment + "\r\na=ice-pwd:" + a.password + "\r\n"
}, c.parseRtpParameters = function(a) {
for (var b = {
codecs: [],
headerExtensions: [],
fecMechanisms: [],
rtcp: []
}, d = c.splitLines(a), e = d[0].split(" "), f = 3; f < e.length; f++) {
var g = e[f],
h = c.matchPrefix(a, "a=rtpmap:" + g + " ")[0];
if (h) {
var i = c.parseRtpMap(h),
j = c.matchPrefix(a, "a=fmtp:" + g + " ");
switch (i.parameters = j.length ? c.parseFmtp(j[0]) : {}, i.rtcpFeedback = c.matchPrefix(a, "a=rtcp-fb:" + g +
" ").map(c.parseRtcpFb), b.codecs.push(i), i.name.toUpperCase()) {
case "RED":
case "ULPFEC":
b.fecMechanisms.push(i.name.toUpperCase())
}
}
}
return c.matchPrefix(a, "a=extmap:").forEach(function(a) {
b.headerExtensions.push(c.parseExtmap(a))
}), b
}, c.writeRtpDescription = function(a, b) {
var d = "";
return d += "m=" + a + " ", d += b.codecs.length > 0 ? "9" : "0", d += " UDP/TLS/RTP/SAVPF ", d += b.codecs.map(
function(a) {
return void 0 !== a.preferredPayloadType ? a.preferredPayloadType : a.payloadType
}).join(" ") + "\r\n", d += "c=IN IP4 0.0.0.0\r\n", d += "a=rtcp:9 IN IP4 0.0.0.0\r\n", b.codecs.forEach(
function(a) {
d += c.writeRtpMap(a), d += c.writeFmtp(a), d += c.writeRtcpFb(a)
}), d += "a=rtcp-mux\r\n"
}, c.parseRtpEncodingParameters = function(a) {
var b, d = [],
e = c.parseRtpParameters(a),
f = -1 !== e.fecMechanisms.indexOf("RED"),
g = -1 !== e.fecMechanisms.indexOf("ULPFEC"),
h = c.matchPrefix(a, "a=ssrc:").map(function(a) {
return c.parseSsrcMedia(a)
}).filter(function(a) {
return "cname" === a.attribute
}),
i = h.length > 0 && h[0].ssrc,
j = c.matchPrefix(a, "a=ssrc-group:FID").map(function(a) {
var b = a.split(" ");
return b.shift(), b.map(function(a) {
return parseInt(a, 10)
})
});
j.length > 0 && j[0].length > 1 && j[0][0] === i && (b = j[0][1]), e.codecs.forEach(function(a) {
if ("RTX" === a.name.toUpperCase() && a.parameters.apt) {
var c = {
ssrc: i,
codecPayloadType: parseInt(a.parameters.apt, 10),
rtx: {
payloadType: a.payloadType,
ssrc: b
}
};
d.push(c), f && (c = JSON.parse(JSON.stringify(c)), c.fec = {
ssrc: b,
mechanism: g ? "red+ulpfec" : "red"
}, d.push(c))
}
}), 0 === d.length && i && d.push({
ssrc: i
});
var k = c.matchPrefix(a, "b=");
return k.length && (0 === k[0].indexOf("b=TIAS:") ? k = parseInt(k[0].substr(7), 10) : 0 === k[0].indexOf("b=AS:") &&
(k = parseInt(k[0].substr(5), 10)), d.forEach(function(a) {
a.maxBitrate = k
})), d
}, c.writeSessionBoilerplate = function() {
return "v=0\r\no=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"
}, c.writeMediaSection = function(a, b, d, e) {
var f = c.writeRtpDescription(a.kind, b);
if (f += c.writeIceParameters(a.iceGatherer.getLocalParameters()), f += c.writeDtlsParameters(a.dtlsTransport.getLocalParameters(),
"offer" === d ? "actpass" : "active"), f += "a=mid:" + a.mid + "\r\n", a.rtpSender && a.rtpReceiver ? f +=
"a=sendrecv\r\n" : a.rtpSender ? f += "a=sendonly\r\n" : a.rtpReceiver ? f += "a=recvonly\r\n" : f +=
"a=inactive\r\n", a.rtpSender) {
var g = "msid:" + e.id + " " + a.rtpSender.track.id + "\r\n";
f += "a=" + g, f += "a=ssrc:" + a.sendEncodingParameters[0].ssrc + " " + g
}
return f += "a=ssrc:" + a.sendEncodingParameters[0].ssrc + " cname:" + c.localCName + "\r\n"
}, c.getDirection = function(a, b) {
for (var d = c.splitLines(a), e = 0; e < d.length; e++) switch (d[e]) {
case "a=sendrecv":
case "a=sendonly":
case "a=recvonly":
case "a=inactive":
return d[e].substr(2)
}
return b ? c.getDirection(b) : "sendrecv"
}, a.RTCIceGatherer && (a.RTCIceCandidate || (a.RTCIceCandidate = function(a) {
return a
}), a.RTCSessionDescription || (a.RTCSessionDescription = function(a) {
return a
}));
var d = {};
d.pc_config = {
iceServers: []
}, b.iceServers instanceof Array && (d.pc_config.iceServers = b.iceServers), void 0 === b.audio && (b.audio = !0),
void 0 === b.video && (b.video = !0), a.RTCPeerConnection = function(a) {
var b = this,
c = document.createDocumentFragment();
if (["addEventListener", "removeEventListener", "dispatchEvent"].forEach(function(a) {
b[a] = c[a].bind(c)
}), this.onicecandidate = null, this.onaddstream = null, this.ontrack = null, this.onremovestream = null, this.onsignalingstatechange =
null, this.oniceconnectionstatechange = null, this.onnegotiationneeded = null, this.ondatachannel = null, this.localStreams = [],
this.remoteStreams = [], this.getLocalStreams = function() {
return b.localStreams
}, this.getRemoteStreams = function() {
return b.remoteStreams
}, this.localDescription = new RTCSessionDescription({
type: "",
sdp: ""
}), this.remoteDescription = new RTCSessionDescription({
type: "",
sdp: ""
}), this.signalingState = "stable", this.iceConnectionState = "new", this.iceGatheringState = "new", this.iceOptions = {
gatherPolicy: "all",
iceServers: []
}, a && a.iceTransportPolicy) switch (a.iceTransportPolicy) {
case "all":
case "relay":
this.iceOptions.gatherPolicy = a.iceTransportPolicy;
break;
case "none":
throw new TypeError('iceTransportPolicy "none" not supported')
}
if (this.usingBundle = a && "max-bundle" === a.bundlePolicy, a && a.iceServers) {
var d = JSON.parse(JSON.stringify(a.iceServers));
this.iceOptions.iceServers = d.filter(function(a) {
if (a && a.urls) {
var b = a.urls;
return "string" == typeof b && (b = [b]), !!(b = b.filter(function(a) {
return 0 === a.indexOf("turn:") && -1 !== a.indexOf("transport=udp")
})[0])
}
return !1
})
}
this.transceivers = [], this._localIceCandidatesBuffer = []
}, a.RTCPeerConnection.prototype.addStream = function(a) {
this.localStreams.push(a.clone()), this._maybeFireNegotiationNeeded()
}, a.RTCPeerConnection.prototype.removeStream = function(a) {
var b = this.localStreams.indexOf(a);
b > -1 && (this.localStreams.splice(b, 1), this._maybeFireNegotiationNeeded())
}, a.RTCPeerConnection.prototype.getSenders = function() {
return this.transceivers.filter(function(a) {
return !!a.rtpSender
}).map(function(a) {
return a.rtpSender
})
}, a.RTCPeerConnection.prototype.getReceivers = function() {
return this.transceivers.filter(function(a) {
return !!a.rtpReceiver
}).map(function(a) {
return a.rtpReceiver
})
}, a.RTCPeerConnection.prototype._emitBufferedCandidates = function() {
var a = this,
b = c.splitSections(a.localDescription.sdp);
this._localIceCandidatesBuffer.forEach(function(c) {
if (c.candidate && 0 !== Object.keys(c.candidate).length) - 1 === c.candidate.candidate.indexOf(
"typ endOfCandidates") && (b[c.candidate.sdpMLineIndex + 1] += "a=" + c.candidate.candidate + "\r\n");
else
for (var d = 1; d < b.length; d++) - 1 === b[d].indexOf("\r\na=end-of-candidates\r\n") && (b[d] +=
"a=end-of-candidates\r\n");
if (a.localDescription.sdp = b.join(""), null !== a.dispatchEvent && a.dispatchEvent(c), null !== a.onicecandidate &&
a.onicecandidate(c), !c.candidate && "complete" !== a.iceGatheringState) {
a.transceivers.every(function(a) {
return a.iceGatherer && "completed" === a.iceGatherer.state
}) && (a.iceGatheringState = "complete")
}
}), this._localIceCandidatesBuffer = []
}, a.RTCPeerConnection.prototype._getCommonCapabilities = function(a, b) {
var c = {
codecs: [],
headerExtensions: [],
fecMechanisms: []
};
return a.codecs.forEach(function(a) {
for (var d = 0; d < b.codecs.length; d++) {
var e = b.codecs[d];
if (a.name.toLowerCase() === e.name.toLowerCase() && a.clockRate === e.clockRate && a.numChannels === e.numChannels) {
c.codecs.push(e);
break
}
}
}), a.headerExtensions.forEach(function(a) {
for (var d = 0; d < b.headerExtensions.length; d++) {
var e = b.headerExtensions[d];
if (a.uri === e.uri) {
c.headerExtensions.push(e);
break
}
}
}), c
}, a.RTCPeerConnection.prototype._createIceAndDtlsTransports = function(b, d) {
var e = this,
f = new a.RTCIceGatherer(e.iceOptions),
g = new a.RTCIceTransport(f);
f.onlocalcandidate = function(a) {
var h = new Event("icecandidate");
h.candidate = {
sdpMid: b,
sdpMLineIndex: d
};
var i = a.candidate,
j = !i || 0 === Object.keys(i).length;
j ? (void 0 === f.state && (f.state = "completed"), h.candidate.candidate =
"candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates") : (i.component = "RTCP" === g.component ? 2 : 1, h.candidate
.candidate = c.writeCandidate(i));
var k = c.splitSections(e.localDescription.sdp); - 1 === h.candidate.candidate.indexOf("typ endOfCandidates") ?
k[h.candidate.sdpMLineIndex + 1] += "a=" + h.candidate.candidate + "\r\n" : k[h.candidate.sdpMLineIndex + 1] +=
"a=end-of-candidates\r\n", e.localDescription.sdp = k.join("");
var l = e.transceivers.every(function(a) {
return a.iceGatherer && "completed" === a.iceGatherer.state
});
switch (e.iceGatheringState) {
case "new":
e._localIceCandidatesBuffer.push(h), j && l && e._localIceCandidatesBuffer.push(new Event("icecandidate"));
break;
case "gathering":
e._emitBufferedCandidates(), null !== e.dispatchEvent && e.dispatchEvent(h), null !== e.onicecandidate && e.onicecandidate(
h), l && (null !== e.dispatchEvent && e.dispatchEvent(new Event("icecandidate")), null !== e.onicecandidate &&
e.onicecandidate(new Event("icecandidate")), e.iceGatheringState = "complete")
}
}, g.onicestatechange = function() {
e._updateConnectionState()
};
var h = new a.RTCDtlsTransport(g);
return h.ondtlsstatechange = function() {
e._updateConnectionState()
}, h.onerror = function() {
e._updateConnectionState()
}, {
iceGatherer: f,
iceTransport: g,
dtlsTransport: h
}
}, a.RTCPeerConnection.prototype._transceive = function(a, b, d) {
var e = this._getCommonCapabilities(a.localCapabilities, a.remoteCapabilities);
b && a.rtpSender && (e.encodings = a.sendEncodingParameters, e.rtcp = {
cname: c.localCName
}, a.recvEncodingParameters.length && (e.rtcp.ssrc = a.recvEncodingParameters[0].ssrc), a.rtpSender.send(e)), d &&
a.rtpReceiver && (e.encodings = a.recvEncodingParameters, e.rtcp = {
cname: a.cname
}, a.sendEncodingParameters.length && (e.rtcp.ssrc = a.sendEncodingParameters[0].ssrc), a.rtpReceiver.receive(e))
}, a.RTCPeerConnection.prototype._updateSignalingState = function(a) {
this.signalingState = a;
var b = new Event("signalingstatechange");
null !== this.dispatchEvent && this.dispatchEvent(b), null !== this.onsignalingstatechange && this.onsignalingstatechange(
b)
}, a.RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {
var a = new Event("negotiationneeded");
null !== this.dispatchEvent && this.dispatchEvent(a), null !== this.onnegotiationneeded && this.onnegotiationneeded(
a)
}, a.RTCPeerConnection.prototype._updateConnectionState = function() {
var a, b = this,
c = {
new: 0,
closed: 0,
connecting: 0,
checking: 0,
connected: 0,
completed: 0,
failed: 0
};
if (this.transceivers.forEach(function(a) {
c[a.iceTransport.state]++, c[a.dtlsTransport.state]++
}), c.connected += c.completed, a = "new", c.failed > 0 ? a = "failed" : c.connecting > 0 || c.checking > 0 ? a =
"connecting" : c.disconnected > 0 ? a = "disconnected" : c.new > 0 ? a = "new" : (c.connected > 0 || c.completed >
0) && (a = "connected"), a !== b.iceConnectionState) {
b.iceConnectionState = a;
var d = new Event("iceconnectionstatechange");
null !== this.dispatchEvent && this.dispatchEvent(d), null !== this.oniceconnectionstatechange && this.oniceconnectionstatechange(
a)
}
}, a.RTCPeerConnection.prototype.setLocalDescription = function(b) {
var d, e, f = this;
if ("offer" === b.type) this._pendingOffer && (d = c.splitSections(b.sdp), e = d.shift(), d.forEach(function(a, b) {
var d = c.parseRtpParameters(a);
f._pendingOffer[b].localCapabilities = d
}), this.transceivers = this._pendingOffer, delete this._pendingOffer);
else if ("answer" === b.type) {
d = c.splitSections(f.remoteDescription.sdp), e = d.shift();
var g = c.matchPrefix(e, "a=ice-lite").length > 0;
d.forEach(function(a, b) {
var d = f.transceivers[b],
h = d.iceGatherer,
i = d.iceTransport,
j = d.dtlsTransport,
k = d.localCapabilities,
l = d.remoteCapabilities;
if ("0" !== a.split("\n", 1)[0].split(" ", 2)[1]) {
var m = c.getIceParameters(a, e);
if (g) {
var n = c.matchPrefix(a, "a=candidate:").map(function(a) {
return c.parseCandidate(a)
}).filter(function(a) {
return "1" === a.component
});
n.length && i.setRemoteCandidates(n)
}
var o = c.getDtlsParameters(a, e);
g && (o.role = "server"), o.role = "client", f.usingBundle && 0 !== b || (i.start(h, m, g ? "controlling" :
"controlled"), j.start(o));
var p = f._getCommonCapabilities(k, l);
f._transceive(d, p.codecs.length > 0, !1)
}
})
}
switch (this.localDescription = {
type: b.type,
sdp: b.sdp
}, b.type) {
case "offer":
this._updateSignalingState("have-local-offer");
break;
case "answer":
this._updateSignalingState("stable");
break;
default:
throw new TypeError('unsupported type "' + b.type + '"')
}
var h = arguments.length > 1 && "function" == typeof arguments[1];
if (h) {
var i = arguments[1];
a.setTimeout(function() {
i(), "new" === f.iceGatheringState && (f.iceGatheringState = "gathering"), f._emitBufferedCandidates()
}, 0)
}
var j = a.Promise.resolve();
return j.then(function() {
h || ("new" === f.iceGatheringState && (f.iceGatheringState = "gathering"), a.setTimeout(f._emitBufferedCandidates
.bind(f), 500))
}), j
}, a.RTCPeerConnection.prototype.setRemoteDescription = function(b) {
var d = this,
e = new a.MediaStream,
f = [],
g = c.splitSections(b.sdp),
h = g.shift(),
i = c.matchPrefix(h, "a=ice-lite").length > 0;
switch (this.usingBundle = c.matchPrefix(h, "a=group:BUNDLE ").length > 0, g.forEach(function(g, j) {
var k, l, m, n, o, p, q, r, s, t, u, v, w = c.splitLines(g),
x = w[0].substr(2).split(" "),
y = x[0],
z = "0" === x[1],
A = c.getDirection(g, h),
B = c.parseRtpParameters(g);
z || (u = c.getIceParameters(g, h), v = c.getDtlsParameters(g, h), v.role = "client"), v.role = "client", r = c
.parseRtpEncodingParameters(g);
var C = c.matchPrefix(g, "a=mid:");
C = C.length ? C[0].substr(6) : c.generateIdentifier();
var D, E = c.matchPrefix(g, "a=ssrc:").map(function(a) {
return c.parseSsrcMedia(a)
}).filter(function(a) {
return "cname" === a.attribute
})[0];
E && (D = E.value);
var F = !0,
G = c.matchPrefix(g, "a=candidate:").map(function(a) {
return c.parseCandidate(a)
}).filter(function(a) {
return "1" === a.component
});
if (G.push({}), "offer" !== b.type || z) "answer" !== b.type || z || (k = d.transceivers[j], l = k.iceGatherer,
m = k.iceTransport, n = k.dtlsTransport, o = k.rtpSender, p = k.rtpReceiver, q = k.sendEncodingParameters, s =
k.localCapabilities, d.transceivers[j].recvEncodingParameters = r, d.transceivers[j].remoteCapabilities = B,
d.transceivers[j].cname = D, (i || F) && G.length && m.setRemoteCandidates(G), d.usingBundle && 0 !== j || (m
.start(l, u, "controlling"), n.start(v)), d._transceive(k, "sendrecv" === A || "recvonly" === A, "sendrecv" ===
A || "sendonly" === A), !p || "sendrecv" !== A && "sendonly" !== A ? delete k.rtpReceiver : (t = p.track, f.push(
[t, p]), e.addTrack(t)));
else {
var H = d.usingBundle && j > 0 ? {
iceGatherer: d.transceivers[0].iceGatherer,
iceTransport: d.transceivers[0].iceTransport,
dtlsTransport: d.transceivers[0].dtlsTransport
} : d._createIceAndDtlsTransports(C, j);
if (F && H.iceTransport.setRemoteCandidates(G), s = a.RTCRtpReceiver.getCapabilities(y), q = [{
ssrc: 1001 * (2 * j + 2)
}], p = new a.RTCRtpReceiver(H.dtlsTransport, y), t = p.track, f.push([t, p]), e.addTrack(t), d.localStreams.length >
0 && d.localStreams[0].getTracks().length >= j) {
var I;
"audio" === y ? I = d.localStreams[0].getAudioTracks()[0] : "video" === y && (I = d.localStreams[0].getVideoTracks()[
0]), I && (o = new a.RTCRtpSender(I, H.dtlsTransport))
}
d.transceivers[j] = {
iceGatherer: H.iceGatherer,
iceTransport: H.iceTransport,
dtlsTransport: H.dtlsTransport,
localCapabilities: s,
remoteCapabilities: B,
rtpSender: o,
rtpReceiver: p,
kind: y,
mid: C,
cname: D,
sendEncodingParameters: q,
recvEncodingParameters: r
}, d._transceive(d.transceivers[j], !1, "sendrecv" === A || "sendonly" === A)
}
}), this.remoteDescription = {
type: b.type,
sdp: b.sdp
}, b.type) {
case "offer":
this._updateSignalingState("have-remote-offer");
break;
case "answer":
this._updateSignalingState("stable");
break;
default:
throw new TypeError('unsupported type "' + b.type + '"')
}
return e.getTracks().length && (d.remoteStreams.push(e), a.setTimeout(function() {
var b = new Event("addstream");
b.stream = e, null !== d.dispatchEvent && d.dispatchEvent(b), null !== d.onaddstream && a.setTimeout(function() {
d.onaddstream(b)
}, 0), f.forEach(function(c) {
var f = c[0],
g = c[1],
h = new Event("track");
h.track = f, h.receiver = g, h.streams = [e], null !== d.dispatchEvent && d.dispatchEvent(b), null !== d.ontrack &&
a.setTimeout(function() {
d.ontrack(h)
}, 0)
})
}, 0)), arguments.length > 1 && "function" == typeof arguments[1] && a.setTimeout(arguments[1], 0), a.Promise.resolve()
}, a.RTCPeerConnection.prototype.close = function() {
this.transceivers.forEach(function(a) {
a.iceTransport && a.iceTransport.stop(), a.dtlsTransport && a.dtlsTransport.stop(), a.rtpSender && a.rtpSender.stop(),
a.rtpReceiver && a.rtpReceiver.stop()
}), this._updateSignalingState("closed")
}, a.RTCPeerConnection.prototype.createOffer = function() {
var b = this;
if (this._pendingOffer) throw new Error("createOffer called while there is a pending offer.");
var d;
1 === arguments.length && "function" != typeof arguments[0] ? d = arguments[0] : 3 === arguments.length && (d =
arguments[2]);
var e = [],
f = 0,
g = 0;
if (this.localStreams.length && (f = this.localStreams[0].getAudioTracks().length, g = this.localStreams[0].getVideoTracks()
.length), d) {
if (d.mandatory || d.optional) throw new TypeError("Legacy mandatory/optional constraints not supported.");
void 0 !== d.offerToReceiveAudio && (f = d.offerToReceiveAudio), void 0 !== d.offerToReceiveVideo && (g = d.offerToReceiveVideo)
}
for (this.localStreams.length && this.localStreams[0].getTracks().forEach(function(a) {
e.push({
kind: a.kind,
track: a,
wantReceive: "audio" === a.kind ? f > 0 : g > 0
}), "audio" === a.kind ? f-- : "video" === a.kind && g--
}); f > 0 || g > 0;) f > 0 && (e.push({
kind: "audio",
wantReceive: !0
}), f--), g > 0 && (e.push({
kind: "video",
wantReceive: !0
}), g--);
var h = c.writeSessionBoilerplate(),
i = [];
e.forEach(function(d, e) {
var f, g, h = d.track,
j = d.kind,
k = c.generateIdentifier(),
l = b.usingBundle && e > 0 ? {
iceGatherer: i[0].iceGatherer,
iceTransport: i[0].iceTransport,
dtlsTransport: i[0].dtlsTransport
} : b._createIceAndDtlsTransports(k, e),
m = a.RTCRtpSender.getCapabilities(j),
n = [{
ssrc: 1001 * (2 * e + 1)
}];
h && (f = new a.RTCRtpSender(h, l.dtlsTransport)), d.wantReceive && (g = new a.RTCRtpReceiver(l.dtlsTransport,
j)), i[e] = {
iceGatherer: l.iceGatherer,
iceTransport: l.iceTransport,
dtlsTransport: l.dtlsTransport,
localCapabilities: m,
remoteCapabilities: null,
rtpSender: f,
rtpReceiver: g,
kind: j,
mid: k,
sendEncodingParameters: n,
recvEncodingParameters: null
}
}), this.usingBundle && (h += "a=group:BUNDLE " + i.map(function(a) {
return a.mid
}).join(" ") + "\r\n"), e.forEach(function(a, d) {
var e = i[d];
h += c.writeMediaSection(e, e.localCapabilities, "offer", b.localStreams[0])
}), this._pendingOffer = i;
var j = new RTCSessionDescription({
type: "offer",
sdp: h
});
return arguments.length && "function" == typeof arguments[0] && a.setTimeout(arguments[0], 0, j), a.Promise.resolve(
j)
}, a.RTCPeerConnection.prototype.createAnswer = function() {
var b = this,
d = c.writeSessionBoilerplate();
this.usingBundle && (d += "a=group:BUNDLE " + this.transceivers.map(function(a) {
return a.mid
}).join(" ") + "\r\n"), this.transceivers.forEach(function(a) {
var e = b._getCommonCapabilities(a.localCapabilities, a.remoteCapabilities);
d += c.writeMediaSection(a, e, "answer", b.localStreams[0])
});
var e = new RTCSessionDescription({
type: "answer",
sdp: d
});
return arguments.length && "function" == typeof arguments[0] && a.setTimeout(arguments[0], 0, e), a.Promise.resolve(
e)
}, a.RTCPeerConnection.prototype.addIceCandidate = function(b) {
if (null === b) this.transceivers.forEach(function(a) {
a.iceTransport.addRemoteCandidate({})
});
else {
var d = b.sdpMLineIndex;
if (b.sdpMid)
for (var e = 0; e < this.transceivers.length; e++)
if (this.transceivers[e].mid === b.sdpMid) {
d = e;
break
} var f = this.transceivers[d];
if (f) {
var g = Object.keys(b.candidate).length > 0 ? c.parseCandidate(b.candidate) : {};
if ("tcp" === g.protocol && 0 === g.port) return;
if ("1" !== g.component) return;
"endOfCandidates" === g.type && (g = {}), f.iceTransport.addRemoteCandidate(g);
var h = c.splitSections(this.remoteDescription.sdp);
h[d + 1] += (g.type ? b.candidate.trim() : "a=end-of-candidates") + "\r\n", this.remoteDescription.sdp = h.join(
"")
}
}
return arguments.length > 1 && "function" == typeof arguments[1] && a.setTimeout(arguments[1], 0), a.Promise.resolve()
}, d.mediaConstraints = {
offerToReceiveVideo: b.video,
offerToReceiveAudio: b.audio
};
var f, g = function(a) {
e.Logger.error("Error in Edge Stack ", a)
},
h = function(a) {
if ("H264" !== b.videoCodec && "h264" !== b.videoCodec) return a;
try {
var c = a.match(/m=video.*\r\n/g)[0],
d = c.replace(/\s120/, "").replace("\r\n", "") + " 120\r\n";
return a.replace(c, d)
} catch (e) {
return a
}
},
i = function(a) {
return h(a)
},
j = function(a) {
var c, d;
return b.video && b.maxVideoBW && (c = a.match(/m=video.*\r\n/), null == c && (c = a.match(/m=video.*\n/)), c && c
.length > 0 && (d = c[0] + "b=AS:" + b.maxVideoBW + "\r\n", a = a.replace(c[0], d))), b.audio && b.maxAudioBW &&
(c = a.match(/m=audio.*\r\n/), null == c && (c = a.match(/m=audio.*\n/)), c && c.length > 0 && (d = c[0] +
"b=AS:" + b.maxAudioBW + "\r\n", a = a.replace(c[0], d))), a
},
k = function(a) {
a.sdp = j(a.sdp), a.sdp = i(a.sdp.replace(/a=ice-options:google-ice\r\n/g, "")), b.callback(a), f = a
},
l = function(a) {
a.sdp = j(a.sdp), a.sdp = a.sdp.replace(/a=ice-options:google-ice\r\n/g, ""), b.callback(a), f = a, d.peerConnection
.setLocalDescription(f)
};
return d.peerConnection = new a.RTCPeerConnection(d.pc_config), b.localCandidates = [], b.remoteCandidates = [], b.remoteDescriptionSet = !
1, d.peerConnection.onicecandidate = function(a) {
if (a.candidate) {
a.candidate.candidate.match(/a=/) || (a.candidate.candidate = "a=" + a.candidate.candidate);
var c = {
sdpMLineIndex: a.candidate.sdpMLineIndex,
sdpMid: a.candidate.sdpMid,
candidate: a.candidate.candidate
};
b.remoteDescriptionSet ? b.callback({
type: "candidate",
candidate: c
}) : b.localCandidates.push(c)
} else b.localCandidates.push({
candidate: {}
}), console.log("End of candidates.")
}, d.peerConnection.onaddstream = function(a) {
d.onaddstream && d.onaddstream(a)
}, d.peerConnection.onremovestream = function(a) {
d.onremovestream && d.onremovestream(a)
}, d.peerConnection.oniceconnectionstatechange = function(a) {
d.oniceconnectionstatechange && d.oniceconnectionstatechange(a)
}, d.createOffer = function(a) {
!0 === a ? d.peerConnection.createOffer(k, g, d.mediaConstraints) : d.peerConnection.createOffer(k, g)
}, d.addStream = function(a) {
d.peerConnection.addStream(a)
}, d.processSignalingMessage = function(a) {
if (e.Logger.debug("Process Signaling Message", a.type), "offer" === a.type) a.sdp = j(a.sdp), console.log(
"Set offer description \n", a.sdp), d.peerConnection.setRemoteDescription(new RTCSessionDescription(a), function() {
d.peerConnection.createAnswer(l, function(a) {
e.Logger.error("Error", a)
}, d.mediaConstraints), b.remoteDescriptionSet = !0
}, function(a) {
e.Logger.error("Error setting Remote Description", a)
});
else if ("answer" === a.type) console.log("Set remote description", a.sdp), a.sdp = j(a.sdp), d.peerConnection.setLocalDescription(
f,
function() {
d.peerConnection.setRemoteDescription(new RTCSessionDescription(a), function() {
for (b.remoteDescriptionSet = !0, e.Logger.info("Remote Description successfully set"); b.remoteCandidates.length >
0;) d.peerConnection.addIceCandidate(b.remoteCandidates.shift());
for (; b.localCandidates.length > 0;) b.callback({
type: "candidate",
candidate: b.localCandidates.shift()
})
}, function(a) {
e.Logger.error("Failure Setting Remote Description", a)
})
},
function(a) {
e.Logger.error("Failure Setting Local Description", a)
});
else if ("candidate" === a.type) try {
var c;
c = "object" == typeof a.candidate ? a.candidate : JSON.parse(a.candidate), c.candidate = c.candidate.replace(
/a=/g, ""), c.sdpMLineIndex = parseInt(c.sdpMLineIndex, 10);
var g = new RTCIceCandidate(c);
if (b.remoteDescriptionSet)
for (d.peerConnection.addIceCandidate(g); b.remoteCandidates.length > 0;) e.Logger.info(
"Setting stored remote candidates"), d.peerConnection.addIceCandidate(b.remoteCandidates.shift());
else b.remoteCandidates.push(g)
} catch (h) {
e.Logger.error("Error parsing candidate", a.candidate, h)
}
}, d.close = function() {
d.state = "closed", "closed" !== d.peerConnection.signalingState && d.peerConnection.close()
}, d.getConnectionStats = function(a, b) {
b("getConnectionStats is not supported on Edge.")
}, d.iceRestart = function() {
e.Logger.error("ICE restart on Edge is not supported yet.")
}, d
};
var d = d || {};
d.Error = {
STREAM_LOCAL_ACCESS_DENIED: {
code: 1101,
message: "Cannot access to camera or micphone."
},
P2P_CONN_SERVER_UNKNOWN: {
code: 2100,
message: "Server unknown error."
},
P2P_CONN_SERVER_UNAVAILABLE: {
code: 2101,
message: "Server is unavaliable."
},
P2P_CONN_SERVER_BUSY: {
code: 2102,
message: "Server is too busy."
},
P2P_CONN_SERVER_NOT_SUPPORTED: {
code: 2103,
message: "Method has not been supported by server"
},
P2P_CONN_CLIENT_UNKNOWN: {
code: 2110,
message: "Client unknown error."
},
P2P_CONN_CLIENT_NOT_INITIALIZED: {
code: 2111,
message: "Connection is not initialized."
},
P2P_CONN_AUTH_UNKNOWN: {
code: 2120,
message: "Authentication unknown error."
},
P2P_CONN_AUTH_FAILED: {
code: 2121,
message: "Wrong username or token."
},
P2P_MESSAGING_TARGET_UNREACHABLE: {
code: 2201,
message: "Remote user cannot be reached."
},
P2P_CHATROOM_ATTENDEE_EXCEED: {
code: 2301,
message: "Exceed room's limitation"
},
P2P_CHATROOM_PEER_NOT_FOUND: {
code: 2302,
message: "Peer not found. Only one client in the room."
},
P2P_CLIENT_UNKNOWN: {
code: 2400,
message: "Unknown errors."
},
P2P_CLIENT_UNSUPPORTED_METHOD: {
code: 2401,
message: "This method is unsupported in current browser."
},
P2P_CLIENT_ILLEGAL_ARGUMENT: {
code: 2402,
message: "Illegal argument."
},
P2P_CLIENT_INVALID_STATE: {
code: 2403,
message: "Invalid peer state."
},
getErrorByCode: function(a) {
return {
1101: d.Error.STREAM_LOCAL_ACCESS_DENIED,
2100: d.Error.P2P_CONN_SERVER_UNKNOWN,
2101: d.Error.P2P_CONN_SERVER_UNAVAILABLE,
2102: d.Error.P2P_CONN_SERVER_BUSY,
2103: d.Error.P2P_CONN_SERVER_NOT_SUPPORTED,
2110: d.Error.P2P_CONN_CLIENT_UNKNOWN,
2111: d.Error.P2P_CONN_CLIENT_NOT_INITIALIZED,
2120: d.Error.P2P_CONN_AUTH_UNKNOWN,
2121: d.Error.P2P_CONN_AUTH_FAILED,
2201: d.Error.P2P_MESSAGING_TARGET_UNREACHABLE,
2301: d.Error.P2P_CHATROOM_ATTENDEE_EXCEED,
2302: d.Error.P2P_CHATROOM_PEER_NOT_FOUND,
2400: d.Error.P2P_CLIENT_UNKNOWN,
2401: d.Error.P2P_CLIENT_UNSUPPORTED_METHOD,
2402: d.Error.P2P_CLIENT_ILLEGAL_ARGUMENT,
2403: d.Error.P2P_CLIENT_INVALID_STATE
} [a]
}
};
var d = d || {};
d.PeerClient = function(b) {
"use strict";
var f, g, h = d.EventDispatcher({}),
i = {
READY: 1,
MATCHED: 2,
OFFERED: 3,
PENDING: 4,
CONNECTING: 5,
CONNECTED: 6,
ERROR: 9
},
j = {
READY: 1,
REQUESTED: 2,
ACCEPTED: 3,
NEGOTIATING: 4
},
k = {
MESSAGE: "message",
FILE: "file"
},
l = {
READY: 1,
CONNECTING: 2,
CONNECTED: 3
},
m = l.READY,
n = b,
o = function(a) {
return "[object Array]" === Object.prototype.toString.call(a)
},
p = 15e3,
q = null,
r = {},
s = {},
t = null,
u = !1,
v = {},
w = {
optional: [{
DtlsSrtpKeyAgreement: "true"
}]
},
x = null,
y = {
offerToReceiveAudio: !0,
offerToReceiveVideo: !0
},
z = null,
A = d.Common.sysInfo(),
B = !navigator.mozGetUserMedia,
C = !!navigator.mozGetUserMedia;
b && (z = {
iceServers: b.iceServers
});
var D = function(a, b) {
return a.localeCompare(b)
},
E = function(a) {
return a
},
F = function(a, b) {
a.negotiationState = b
},
G = function(a, b) {
a.state !== i.CONNECTED && a.state !== i.CONNECTING || (a.sendDataChannel && a.sendDataChannel.close(), a.receiveDataChannel &&
a.receiveDataChannel.close(), a.connection && "closed" !== a.connection.iceConnectionState && a.connection.close(),
a.state !== i.READY && (a.state = i.READY, h.dispatchEvent(new d.ChatEvent({
target: h,
type: "chat-stopped",
peerId: a.id,
senderId: b
}))), fa(a.connection))
},
H = function(a, b) {
b.sdk && b.sdk && "JavaScript" === b.sdk.type && b.runtime && "FireFox" === b.runtime.name ? (a.remoteSideSupportsRemoveStream = !
1, a.remoteSideSupportsPlanB = !1, a.remoteSideSupportsUnifiedPlan = !0, a.preferredVideoCodec = "vp8") : (a.remoteSideSupportsRemoveStream = !
0, a.remoteSideSupportsPlanB = !0, a.remoteSideSupportsUnifiedPlan = !1)
},
I = function() {
u = !0, m = l.CONNECTED
},
J = function() {
g && g(), f = void 0, g = void 0
},
K = function() {
u = !1, m = l.READY, h.dispatchEvent(new d.ClientEvent({
target: h,
type: "server-disconnected"
}))
},
L = function(a, b) {
var c = r[a];
c || (ka(a), c = r[a]), H(c, b), c.state === i.READY || c.state === i.PENDING ? (r[a].state = i.PENDING, h.dispatchEvent(
new d.ChatEvent({
target: h,
type: "chat-invited",
senderId: a
}))) : c.state === i.OFFERED && D(t, a) < 0 && (c.state = i.PENDING, qa(a, function() {
h.dispatchEvent(new d.ChatEvent({
target: h,
type: "chat-accepted",
senderId: a
}))
}))
},
M = function(a) {
var b = r[a];
b && b.connection && (b.sendDataChannel && b.sendDataChannel.close(), b.receiveDataChannel && b.receiveDataChannel
.close(), b.connection.close()), delete r[a], h.dispatchEvent(new d.ChatEvent({
target: h,
type: "chat-denied",
senderId: a
}))
},
N = function(a, b) {
e.Logger.debug("Received chat accepted.");
var c = r[a];
c && (c.state = i.MATCHED, H(c, b), ea(c), c.state = i.CONNECTING, ha(c.id), h.dispatchEvent(new d.ChatEvent({
target: h,
type: "chat-accepted",
senderId: a
})))
},
O = function(a) {
var b = r[a];
b && b.connection && G(b, a), delete r[a]
},
P = function(a, b) {
var c = r[b];
c && c.state === i.CONNECTING && (c.connection || ea(c)), _(c, a)
},
Q = function(a, b) {
s[a.streamId] = a.type, e.Logger.debug("remote stream ID:" + a.streamId + ",type:" + s[a.streamId])
},
R = function(a) {
t = a, f && f(a), f = void 0, g = void 0
},
S = function() {
Ea()
},
T = function(a) {
e.Logger.debug("On negotiation needed."), a.isCaller && "stable" === a.connection.signalingState && a.negotiationState ===
j.READY ? ja(a) : !a.isCaller && q ? q.sendNegotiationNeeded(a.id) : a.isNegotiationNeeded = !0
},
U = function(a, b) {
b.candidate && q && q.sendSignalMessage(a.id, {
type: "candidates",
candidate: b.candidate.candidate,
sdpMid: b.candidate.sdpMid,
sdpMLineIndex: b.candidate.sdpMLineIndex
})
},
V = function(a, b) {
if (a && e.Logger.debug("On remote ice candidate from peer " + a.id), a && (a.state === i.OFFERED || a.state ===
i.CONNECTING || a.state === i.CONNECTED)) {
var c = new RTCIceCandidate({
candidate: b.message.candidate,
sdpMid: b.message.sdpMid,
sdpMLineIndex: b.message.sdpMLineIndex
});
a.connection ? (e.Logger.debug("Add remote ice candidates."), a.connection.addIceCandidate(c, ba, ca)) : (e.Logger
.debug("Cache remote ice candidates."), a.remoteIceCandidates || (a.remoteIceCandidates = []), a.remoteIceCandidates
.push(c))
}
},
W = function(a, b) {
if (!a) return void e.Logger.debug('"peer" cannot be null or undefined');
switch (a.state) {
case i.OFFERED:
case i.MATCHED:
a.state = i.CONNECTING, ea(a);
case i.CONNECTING:
case i.CONNECTED:
e.Logger.debug("About to set remote description. Signaling state: " + a.connection.signalingState);
var c = new RTCSessionDescription(b.message);
c.sdp = Pa(c.sdp), a.connection.setRemoteDescription(c, function() {
xa(a), ta(a)
}, function(a) {
e.Logger.debug("Set remote description failed. Message: " + JSON.stringify(a))
});
break;
default:
e.Logger.debug("Unexpected peer state: " + a.state)
}
},
X = function(a, b) {
if (a && (a.state === i.CONNECTING || a.state === i.CONNECTED)) {
e.Logger.debug("About to set remote description. Signaling state: " + a.connection.signalingState);
var c = new RTCSessionDescription(b.message);
c.sdp = Pa(c.sdp), a.connection.setRemoteDescription(new RTCSessionDescription(c), function() {
e.Logger.debug("Set remote descripiton successfully."), ta(a), va(a)
}, function(a) {
e.Logger.debug("Set remote description failed. Message: " + a)
})
}
},
Y = function(a, b) {
var c = s[a.id];
if (c) {
var e = {};
"screen" === c ? e.video = {
device: "screen"
} : (e.video = a.getVideoTracks().length > 0 && {
device: "camera"
}, e.audio = a.getAudioTracks().length > 0);
var f = new d.RemoteStream(e);
return f.mediaStream = a, f.from = b.id, f.id = function() {
return a.id
}, f
}
return null
},
Z = function(a, b) {
e.Logger.debug("Remote stream added.");
var c = Y(b.stream, a);
if (c) {
var f = new d.StreamEvent({
target: h,
type: "stream-added",
senderId: a.id,
stream: c
});
h.dispatchEvent(f)
}
},
$ = function(a, b) {
e.Logger.debug("Remote stream removed.");
var c = Y(b.stream, a);
if (c) {
var f = new d.StreamEvent({
target: h,
type: "stream-removed",
stream: c
});
h.dispatchEvent(f)
}
},
_ = function(a, b) {
e.Logger.debug("S->C: " + JSON.stringify(b)), "offer" === b.type ? W(a, {
message: b
}) : "answer" === b.type ? X(a, {
message: b
}) : "candidates" === b.type && V(a, {
message: b
})
},
aa = function(a, b) {
a && (e.Logger.debug("Ice connection state changed. State: " + a.connection.iceConnectionState), "closed" !== a.connection
.iceConnectionState && "failed" !== a.connection.iceConnectionState || a.state !== i.CONNECTED || (G(a, a.id), q &&
q.sendChatStopped(a.id), delete r[a.id]), "connected" !== a.connection.iceConnectionState && "completed" !== a.connection
.iceConnectionState || (a.lastDisconnect = new Date("2099/12/31").getTime(), a.state !== i.CONNECTED && (a.state =
i.CONNECTED, h.dispatchEvent(new d.ChatEvent({
target: h,
type: "chat-started",
peerId: a.id
})))), "checking" === a.connection.iceConnectionState && (a.lastDisconnect = new Date("2099/12/31").getTime()),
"disconnected" === a.connection.iceConnectionState && (a.lastDisconnect = (new Date).getTime(), setTimeout(
function() {
(new Date).getTime() - a.lastDisconnect >= p && (e.Logger.debug("Disconnect timeout."), G(a, a.id), a === r[a
.id] && delete r[a.id])
}, p)))
},
ba = function() {
e.Logger.debug("Add ice candidate success.")
},
ca = function(a) {
e.Logger.debug("Add ice candidate failed. Error: " + a)
},
da = function(a) {
e.Logger.debug("Signaling state changed: " + a.connection.signalingState), "closed" === a.connection.signalingState ?
(G(a, a.id), delete r[a.id]) : "stable" === a.connection.signalingState && (F(a, j.READY), a.isCaller && a.isNegotiationNeeded &&
q ? ja(a) : ua(a))
},
ea = function(a) {
if (!a || a.connection) return !0;
try {
a.connection = new RTCPeerConnection(z, w), a.connection.onicecandidate = function(b) {
U(a, b)
}, a.connection.onaddstream = function(b) {
Z(a, b)
}, a.connection.onremovestream = function(b) {
$(a, b)
}, a.connection.oniceconnectionstatechange = function(b) {
aa(a, b)
}, a.connection.onnegotiationneeded = function() {
T(r[a.id])
}, a.connection.onsignalingstatechange = function() {
da(a)
}, a.connection.ondatachannel = function(b) {
e.Logger.debug(t + ": On data channel"), a.dataChannels[b.channel.label] || (a.dataChannels[b.channel.label] =
b.channel, e.Logger.debug("Save remote created data channel.")), ga(b.channel, a)
}
} catch (b) {
return e.Logger.debug("Failed to create PeerConnection, exception: " + b.message), !1
}
return !0
},
fa = function(a) {
a.onicecandidate = void 0, a.onaddstream = void 0, a.onremovestream = void 0, a.oniceconnectionstatechange = void 0,
a.onnegotiationneeded = void 0, a.onsignalingstatechange = void 0
},
ga = function(a, b) {
a.onmessage = function(a) {
Ja(b, a)
}, a.onopen = function(a) {
Ka(b, a)
}, a.onclose = function(a) {
La(b, a)
}, a.onerror = function(a) {
e.Logger.debug("Data Channel Error:", a)
}
},
ha = function(a, b) {
b || (b = k.MESSAGE), ia(E(a), b)
},
ia = function(a, b) {
var c = r[a];
if (c && !c.dataChannels[b]) {
e.Logger.debug("Do create data channel.");
try {
var d = c.connection.createDataChannel(b, x);
ga(d, c), c.dataChannels[k.MESSAGE] = d
} catch (f) {
e.Logger.error("Failed to create SendDataChannel, exception: " + f.message)
}
}
},
ja = function(a) {
e.Logger.debug("Do renegotiation."), F(a, j.NEGOTIATING), sa(a)
},
ka = function(a) {
return r[a] || (r[a] = {
state: i.READY,
id: a,
pendingStreams: [],
pendingUnpublishStreams: [],
remoteIceCandidates: [],
dataChannels: {},
pendingMessages: [],
negotiationState: j.READY,
lastDisconnect: new Date("2099/12/31").getTime(),
publishedStreams: [],
isCaller: !0,
remoteSideSupportsRemoveStream: !1,
remoteSideSupportsPlanB: !1,
remoteSideSupportsUnifiedPlan: !1
}), r[a]
},
la = function(a) {
var b = r[a];
e.Logger.debug(t + ": Remote side needs negotiation."), b && (b.isCaller && "stable" === b.connection.signalingState &&
b.negotiationState === j.READY ? ja(b) : (b.isNegotiationNeeded = !0, e.Logger.error(
"Should not receive negotiation needed request because user is callee.")))
},
ma = function(a, b, f) {
if (m !== l.READY) return e.Logger.warning("Another peer has already connected"), void(f && f(d.Error.P2P_CLIENT_INVALID_STATE));
m = l.CONNECTING, q = new c(a), q.onConnected = I, q.onDisconnected = K, q.onConnectFailed = J, q.onChatStopped =
O, q.onChatAccepted = N, q.onChatDenied = M, q.onChatInvitation = L, q.onChatSignal = P, q.onStreamType = Q, q.onNegotiationNeeded =
la, q.onAuthenticated = R, q.onForceDisconnect = S, q.connect(a, b, f)
},
na = function(a, b) {
if (!u) return void(b && b(d.Error.P2P_CLIENT_INVALID_STATE));
Ea(), q && q.finalize(), q = null, a && a()
},
oa = function(a, b, c) {
if (!q) return void(c && c(d.Error.P2P_CONN_CLIENT_NOT_INITIALIZED));
if (a === t) return void(c && c(d.Error.P2P_CLIENT_ILLEGAL_ARGUMENT));
r[a] || ka(a);
var f = r[a];
f.state === i.READY || f.state === i.OFFERED ? (e.Logger.debug("Send invitation to " + a), f.state = i.OFFERED, q.sendChatInvitation(
a, A,
function() {
b && b()
},
function(a) {
f.state = i.READY, c && c(d.Error.getErrorByCode(a))
})) : (e.Logger.debug("Invalid state. Will not send invitation."), c && c(d.Error.P2P_CLIENT_INVALID_STATE))
},
pa = function(a, b, c, d) {
oa(a, function() {
za(b, a)
}, d)
},
qa = function(a, b, c) {
!q && c && c(d.Error.P2P_CONN_CLIENT_NOT_INITIALIZED), r[a] || ka(a);
var f = r[a];
f.isCaller = !1, f.state === i.PENDING ? (f.state = i.MATCHED, q.sendChatAccepted(a, A, b, function(a) {
f.state = i.PENDING, c && c(d.Error.getErrorByCode(a))
})) : (e.Logger.debug("Invalid state. Will not send acceptance."), c && c(d.Error.P2P_CLIENT_INVALID_STATE))
},
ra = function(a, b, c, d) {
qa(a, function() {
za(b, a)
}, d)
},
sa = function(a) {
return a.connection ? "stable" !== a.connection.signalingState ? void F(a, j.NEGOTIATING) : (ua(a), a.isNegotiationNeeded = !
1, void a.connection.createOffer(function(b) {
b.sdp = Qa(b.sdp, a), a.connection.setLocalDescription(b, function() {
e.Logger.debug("Set local descripiton successfully."), F(a, j.READY), q && q.sendSignalMessage(a.id, b)
}, function(a) {
e.Logger.debug("Set local description failed. Message: " + JSON.stringify(a))
})
}, function(a) {
e.Logger.debug("Create offer failed. Error info: " + JSON.stringify(a))
}, y)) : void e.Logger.error("Peer connection have not been created.")
},
ta = function(a) {
if (a && a.connection && a.remoteIceCandidates && 0 !== a.remoteIceCandidates.length) {
for (var b = 0; b < a.remoteIceCandidates.length; b++) e.Logger.debug("remoteIce, length:" + remoteIceCandidates.length +
", current:" + b), a.state !== i.CONNECTED && a.state !== i.CONNECTING || a.connection.addIceCandidate(
remoteIceCandidates[b], ba, ca);
a.remoteIceCandidates = []
}
},
ua = function(a) {
if (e.Logger.debug("Draining pending streams."), a.connection) {
e.Logger.debug("Peer connection is ready for draining pending streams.");
for (var b = 0; b < a.pendingStreams.length; b++) {
var c = a.pendingStreams[b];
a.pendingStreams.shift(), c.mediaStream && (wa(c, a), c.onClose || (c.onClose = function() {
Ma(c)
}), a.connection.addStream(c.mediaStream), e.Logger.debug("Added stream to peer connection."), Ca(c, a), e.Logger
.debug("Sent stream type."))
}
a.pendingStreams = [];
for (var d = 0; d < a.pendingUnpublishStreams.length; d++) a.pendingUnpublishStreams[d].mediaStream && (a.connection
.removeStream(a.pendingUnpublishStreams[d].mediaStream), e.Logger.debug("Remove stream."));
a.pendingUnpublishStreams = []
}
},
va = function(a) {
e.Logger.debug("Draining pendding messages.");
var b = a.dataChannels[k.MESSAGE];
if (b && "closed" !== b.readyState) {
for (var c = 0; c < a.pendingMessages.length; c++) b.send(a.pendingMessages[c]);
a.pendingMessages = []
}
},
wa = function(a, b) {
var c = a.id();
v[c] || (v[c] = []), v[c].push(b.id)
},
xa = function(a) {
if (!a.connection) return void e.Logger.error("Peer connection have not been created.");
ua(a), a.isNegotiationNeeded = !1, a.connection.createAnswer(function(b) {
b.sdp = Qa(b.sdp, a), a.connection.setLocalDescription(b, function() {
e.Logger.debug("Set local description successfully."), q && q.sendSignalMessage(a.id, b), e.Logger.debug(
"Sent answer.")
}, function(a) {
e.Logger.error("Error occurred while setting local description. Error message:" + a)
})
}, function(a) {
e.Logger.error("Create answer failed. Message: " + a)
})
},
ya = function(a, b) {
for (var c = 0; c < a.length; c++)
if (a[c] === b) return c;
return -1
},
za = function(a, b, c, e) {
if (!(a instanceof d.LocalStream && a.mediaStream && b)) return void(e && e(d.Error.P2P_CLIENT_ILLEGAL_ARGUMENT));
Aa(a, b, c, e)
},
Aa = function(a, b, c, f) {
e.Logger.debug("Publish to: " + b);
var g = E(b);
if (!g) return void(f && f(d.Error.P2P_CLIENT_ILLEGAL_ARGUMENT));
r[g] || ka(g);
var h = r[g],
j = h.publishedStreams.length + h.pendingStreams.length > 0,
k = h.remoteSideSupportsUnifiedPlan && C || h.remoteSideSupportsPlanB && B;
if (j && !k) return e.Logger.warning(
"Cannot publish more than one streams if local and remote use different multiple media sources plan."), void(f &&
f(d.Error.P2P_CLIENT_UNSUPPORTED_METHOD));
switch (h.state) {
case i.OFFERED:
case i.MATCHED:
case i.CONNECTING:
case i.CONNECTED:
break;
default:
return e.Logger.warning("Cannot publish stream in this state: " + h.state), void(f && f(d.Error.P2P_CLIENT_INVALID_STATE))
}
if (ya(h.publishedStreams, a) > -1) return void(f && f("The stream has been published."));
switch (h.publishedStreams.push(a), o(a) ? h.pendingStreams = h.pendingStreams.concat(a) : a && h.pendingStreams.push(
a), h.state) {
case i.CONNECTING:
case i.CONNECTED:
h.pendingStreams.length > 0 && ua(h);
break;
default:
return e.Logger.debug("Unexpected peer state: " + h.state), void(f && f(d.Error.P2P_CLIENT_INVALID_STATE))
}
c && c()
},
Ba = function(a, b, c, f) {
if (e.Logger.debug("Unpublish stream."), !(a instanceof d.LocalStream)) return e.Logger.warning(
"Invalid argument stream"), void(f && f(d.Error.P2P_CLIENT_ILLEGAL_ARGUMENT));
var g = E(b);
if (!g) return void(f && (e.Logger.warning("Invalid argument targetId"), f(d.Error.P2P_CLIENT_ILLEGAL_ARGUMENT)));
if (!r[g] || ya(r[g].publishedStreams, a) < 0) return void(f && f(d.Error.P2P_CLIENT_ILLEGAL_ARGUMENT));
var h = r[g];
if (navigator.mozGetUserMedia || !h.remoteSideSupportsRemoveStream) return e.Logger.error(
"Unpublish is not supported on Firefox."), void(f && f(d.Error.P2P_CLIENT_UNSUPPORTED_METHOD));
var j = ya(h.publishedStreams, a);
h.publishedStreams.splice(j, 1), h.pendingUnpublishStreams.push(a), h.state === i.CONNECTED && ua(h), c && c()
},
Ca = function(a, b) {
if (null !== a) {
var c = "audio";
a.isScreen() ? (c = "screen", a.hide = function() {
e.Logger.debug("Unpublish screen sharing."), Ba(a, b.id)
}) : a.hasVideo() && (c = "video"), q && q.sendStreamType(b.id, {
streamId: a.mediaStream.id,
type: c
})
}
},
Da = function(a, b, c) {
if (r[a] && r[a].state === i.PENDING) {
if (!q && c) return void c(d.Error.P2P_CONN_CLIENT_NOT_INITIALIZED);
q.sendChatDenied(a, b, function(a) {
c && c(d.Error.getErrorByCode(a))
}), delete r[a]
} else c && c(d.Error.P2P_CLIENT_INVALID_STATE)
},
Ea = function(a, b, c) {
if (!q) return void(c && c(d.Error.P2P_CONN_CLIENT_NOT_INITIALIZED));
if (a) {
var f = r[a];
if (!f) return void(c && (e.Logger.warning("Invalid target ID for stopping chat."), c(d.Error.P2P_CLIENT_ILLEGAL_ARGUMENT)));
q.sendChatStopped(f.id), G(f, t), delete r[a]
} else {
var g = !0;
for (var h in r) {
g = !1;
var i = r[h];
q.sendChatStopped(i.id), G(i, t), delete r[i.id]
}
if (g) return void(c && (e.Logger.warning("No active connections can be stopped."), c(d.Error.P2P_CLIENT_INVALID_STATE)))
}
b && b()
},
Fa = function(b, c, e) {
var f = E(b),
g = r[f];
if (!g || !g.connection || g.state !== i.CONNECTED) return void(e && e("failed to get peerconnection statistics"));
c && g.connection.getStats(null, function(b) {
c(a.navigator.appVersion.indexOf("Trident") > -1 ? b : d.Common.parseStats(b))
}, function(a) {
e && e(a)
})
},
Ga = function(a, b, c) {
var f = E(a),
g = r[f];
if (g && g.connection && g.state === i.CONNECTED || c("Invalid peer connection status."), navigator.mozGetUserMedia)
return e.Logger.error("GetAudioLevels is not supported on Firefox."), void(c && c(d.Error.P2P_CLIENT_UNSUPPORTED_METHOD));
b && g.connection.getStats(function(a) {
b(d.Common.parseAudioLevel(a))
}, function(a) {
c && c(a)
})
},
Ha = function(a, b, c, f) {
return a ? a.length > 65535 ? (e.Logger.warning("Message too long. Max size: 65535."), void(f && f(d.Error.P2P_CLIENT_ILLEGAL_ARGUMENT))) :
void Ia(a, E(b), c, f) : (e.Logger.warning("Message cannot be undefined, null or empty."), void(f && f(d.Error.P2P_CLIENT_ILLEGAL_ARGUMENT)))
},
Ia = function(a, b, c, f) {
var g = r[b];
if (!g || g.state !== i.CONNECTED) return void(f && (e.Logger.error("Invalid peer state."), f(d.Error.P2P_CLIENT_INVALID_STATE)));
var h = g.dataChannels[k.MESSAGE];
h && "open" === h.readyState ? h.send(a) : (g.pendingMessages.push(a), ha(b)), c && c()
},
Ja = function(a, b) {
var c = new d.DataEvent({
target: h,
type: "data-received",
senderId: a.id,
data: b.data
});
h.dispatchEvent(c)
},
Ka = function(a, b) {
e.Logger.debug("Data Channel is opened"), b.target.label === k.MESSAGE && (e.Logger.debug(
"Data channel for messages is opened."), va(a))
},
La = function(a) {
e.Logger.debug("Data Channel for " + a + " is closed.")
},
Ma = function(a) {
var b = v[a.getID()];
if (b)
for (var c = 0; c < b.length; c++) Ba(a, b[c])
},
Na = function(a) {
return n.bandWidth && n.bandWidth.maxAudioBW ? d.Common.setPreferredBitrate(a, "audio", n.bandWidth.maxAudioBW) :
a
},
Oa = function(a) {
return n.bandWidth && n.bandWidth.maxVideoBW ? d.Common.setPreferredBitrate(a, "video", n.bandWidth.maxVideoBW) :
a
},
Pa = function(a) {
return a = Na(a), a = Oa(a)
},
Qa = function(a, b) {
return a = Ra(a), a = Sa(a, b)
},
Ra = function(a) {
return n.audioCodec ? a : d.Common.setPreferredCodec(a, "audio", n.audioCodec)
},
Sa = function(a, b) {
var c;
return c = navigator.mozGetUserMedia ? "vp8" : b && b.preferredVideoCodec ? b.preferredVideoCodec : n.videoCodec ?
n.videoCodec : "h264", d.Common.setPreferredCodec(a, "video", c)
};
return h.connect = ma, h.disconnect = na, h.invite = oa, h.inviteWithStream = pa, h.publish = za, h.unpublish = Ba,
h.deny = Da, h.accept = qa, h.acceptWithStream = ra, h.stop = Ea, h.send = Ha, h.getConnectionStats = Fa, h.getAudioLevels =
Ga, h
}, a.Erizo = f, a.PureRTC = d, a.L = e
}(window);
/*
* Intel WebRTC SDK version 3.4.1
* Copyright (c) 2018 Intel <http://webrtc.purertc.com>
* Homepage: http://webrtc.purertc.com
*/
(function(window) {
/* global window, webkitURL, Erizo */
/*
* AudioPlayer represents a Licode Audio component that shows either a local or a remote Audio.
* Ex.: var player = AudioPlayer({id: id, stream: stream, elementID: elementID, doc: document});
* A AudioPlayer is also a View component.
*/
Erizo.AudioPlayer = function(spec) {
'use strict';
var that = Erizo.View({}),
onmouseover,
onmouseout;
// Variables
// current document object where AudioPlayer will be located
that.document = spec.doc || document;
// AudioPlayer ID
that.id = spec.id;
// Stream that the AudioPlayer will play
that.stream = spec.stream.mediaStream;
// DOM element in which the AudioPlayer will be appended
that.elementID = spec.elementID;
that.stream_url = (window.URL || webkitURL).createObjectURL(that.stream);
// Audio tag
that.audio = that.document.createElement('audio');
that.audio.setAttribute('id', 'stream' + that.id);
that.audio.setAttribute('style',
'width: 100%; height: 100%; position: absolute');
that.audio.setAttribute('autoplay', 'autoplay');
if (spec.stream instanceof PureRTC.LocalStream) {
that.audio.volume = 0;
}
if (that.elementID !== undefined) {
// It will stop the AudioPlayer and remove it from the HTML
that.destroy = function() {
that.audio.pause();
that.parentNode.removeChild(that.div);
};
onmouseover = function() {
that.bar.display();
};
onmouseout = function() {
that.bar.hide();
};
// Container
that.div = that.document.createElement('div');
that.div.setAttribute('id', 'player_' + that.id);
that.div.setAttribute('style',
'width: 100%; height: 100%; position: relative; overflow: hidden;');
that.document.getElementById(that.elementID).appendChild(that.div);
that.container = that.document.getElementById(that.elementID);
that.parentNode = that.div.parentNode;
that.div.appendChild(that.audio);
// Bottom Bar
that.bar = new Erizo.Bar({
elementID: 'player_' + that.id,
id: that.id,
stream: spec.stream,
media: that.audio,
options: spec.options,
doc: that.document
});
that.div.onmouseover = onmouseover;
that.div.onmouseout = onmouseout;
} else {
// It will stop the AudioPlayer and remove it from the HTML
that.destroy = function() {
that.audio.pause();
that.parentNode.removeChild(that.audio);
};
that.document.body.appendChild(that.audio);
that.parentNode = that.document.body;
}
that.audio.src = that.stream_url;
return that;
};
/* global clearTimeout, setTimeout, Erizo */
/*
* Bar represents the bottom menu bar of every mediaPlayer.
* It contains a Speaker and an icon.
* Every Bar is a View.
* Ex.: var bar = Bar({elementID: element, id: id, doc: document});
*/
Erizo.Bar = function(spec) {
'use strict';
var that = Erizo.View({}),
waiting,
show;
// Variables
// current document object where Bar will be located
that.document = spec.doc || document;
// DOM element in which the Bar will be appended
that.elementID = spec.elementID;
// Bar ID
that.id = spec.id;
// Container
that.div = that.document.createElement('div');
that.div.setAttribute('id', 'bar_' + that.id);
// Bottom bar
that.bar = that.document.createElement('div');
that.bar.setAttribute('style',
'width: 100%; height: 15%; max-height: 1.5rem; position: absolute; bottom: 3.8rem; right: 0;'
);
that.bar.setAttribute('id', 'subbar_' + that.id);
// Private functions
show = function(displaying) {
if (displaying !== 'block') {
displaying = 'none';
} else {
clearTimeout(waiting);
}
that.div.setAttribute('style',
'width: 100%; height: 100%; position: relative; bottom: 0; right: 0; display:' +
displaying);
};
// Public functions
that.display = function() {
show('block');
};
that.hide = function() {
waiting = setTimeout(show, 1000);
};
that.document.getElementById(that.elementID).appendChild(that.div);
that.div.appendChild(that.bar);
// Speaker component
if (!spec.stream.screen && (spec.options === undefined || spec.options.speaker ===
undefined || spec.options.speaker === true)) {
that.speaker = new Erizo.Speaker({
elementID: 'subbar_' + that.id,
id: that.id,
stream: spec.stream,
media: spec.media,
doc: that.document
});
}
that.display();
that.hide();
return that;
};
/**
* Copyright 2013 Marc J. Schmidt. See the LICENSE file at the top-level
* directory of this distribution and at
* https://github.com/marcj/css-element-queries/blob/master/LICENSE.
*/
;
(function() {
window.L = window.L || {};
window.L.ElementQueries = function() {
/**
*
* @param element
* @returns {Number}
*/
function getEmSize(element) {
if (!element) {
element = document.documentElement;
}
var fontSize = getComputedStyle(element, 'fontSize');
return parseFloat(fontSize) || 16;
}
/**
*
* @copyright https://github.com/Mr0grog/element-query/blob/master/LICENSE
*
* @param element
* @param value
* @param units
* @returns {*}
*/
function convertToPx(element, value) {
var units = value.replace(/[0-9]*/, '');
value = parseFloat(value);
switch (units) {
case "px":
return value;
case "em":
return value * getEmSize(element);
case "rem":
return value * getEmSize();
// Viewport units!
// According to http://quirksmode.org/mobile/tableViewport.html
// documentElement.clientWidth/Height gets us the most reliable info
case "vw":
return value * document.documentElement.clientWidth / 100;
case "vh":
return value * document.documentElement.clientHeight / 100;
case "vmin":
case "vmax":
var vw = document.documentElement.clientWidth / 100;
var vh = document.documentElement.clientHeight / 100;
var chooser = Math[units === "vmin" ? "min" : "max"];
return value * chooser(vw, vh);
default:
return value;
// for now, not supporting physical units (since they are just a set number of px)
// or ex/ch (getting accurate measurements is hard)
}
}
function SetupInformation(element) {
this.element = element;
this.options = [];
var i, j, option, width = 0,
height = 0,
value, actualValue, attrValues, attrValue, attrName;
//
// @param option {mode: 'min|max', property: 'width|height', value: '123px'}
//
this.addOption = function(option) {
this.options.push(option);
}
var attributes = ['min-width', 'min-height', 'max-width',
'max-height'
];
/**
* Extracts the computed width/height and sets to min/max- attribute.
*/
this.call = function() {
// extract current dimensions
width = this.element.offsetWidth;
height = this.element.offsetHeight;
attrValues = {};
for (i = 0, j = this.options.length; i < j; i++) {
option = this.options[i];
value = convertToPx(this.element, option.value);
actualValue = option.property == 'width' ? width : height;
attrName = option.mode + '-' + option.property;
attrValue = '';
if (option.mode == 'min' && actualValue >= value) {
attrValue += option.value;
}
if (option.mode == 'max' && actualValue <= value) {
attrValue += option.value;
}
if (!attrValues[attrName]) attrValues[attrName] = '';
if (attrValue && -1 === (' ' + attrValues[attrName] + ' ').indexOf(
' ' + attrValue + ' ')) {
attrValues[attrName] += ' ' + attrValue;
}
}
for (var k in attributes) {
if (attrValues[attributes[k]]) {
this.element.setAttribute(attributes[k], attrValues[
attributes[k]].substr(1));
} else {
this.element.removeAttribute(attributes[k]);
}
}
};
}
/**
* @param {HTMLElement} element
* @param {Object} options
*/
function setupElement(element, options) {
if (element.elementQueriesSetupInformation) {
element.elementQueriesSetupInformation.addOption(options);
} else {
element.elementQueriesSetupInformation = new SetupInformation(
element);
element.elementQueriesSetupInformation.addOption(options);
new ResizeSensor(element, function() {
element.elementQueriesSetupInformation.call();
});
}
element.elementQueriesSetupInformation.call();
}
/**
* @param {String} selector
* @param {String} mode min|max
* @param {String} property width|height
* @param {String} value
*/
function queueQuery(selector, mode, property, value) {
var query;
if (document.querySelectorAll) query = document.querySelectorAll.bind(
document);
if (!query && 'undefined' !== typeof $$) query = $$;
if (!query && 'undefined' !== typeof jQuery) query = jQuery;
if (!query) {
throw 'No document.querySelectorAll, jQuery or Mootools\'s $$ found.';
}
var elements = query(selector);
for (var i = 0, j = elements.length; i < j; i++) {
setupElement(elements[i], {
mode: mode,
property: property,
value: value
});
}
}
var regex =
/,?([^,\n]*)\[[\s\t]*(min|max)-(width|height)[\s\t]*[~$\^]?=[\s\t]*"([^"]*)"[\s\t]*]([^\n\s\{]*)/mgi;
/**
* @param {String} css
*/
function extractQuery(css) {
var match;
css = css.replace(/'/g, '"');
while (null !== (match = regex.exec(css))) {
if (5 < match.length) {
queueQuery(match[1] || match[5], match[2], match[3], match[4]);
}
}
}
/**
* @param {CssRule[]|String} rules
*/
function readRules(rules) {
var selector = '';
if (!rules) {
return;
}
if ('string' === typeof rules) {
rules = rules.toLowerCase();
if (-1 !== rules.indexOf('min-width') || -1 !== rules.indexOf(
'max-width')) {
extractQuery(rules);
}
} else {
for (var i = 0, j = rules.length; i < j; i++) {
if (1 === rules[i].type) {
selector = rules[i].selectorText || rules[i].cssText;
if (-1 !== selector.indexOf('min-height') || -1 !== selector.indexOf(
'max-height')) {
extractQuery(selector);
} else if (-1 !== selector.indexOf('min-width') || -1 !==
selector.indexOf('max-width')) {
extractQuery(selector);
}
} else if (4 === rules[i].type) {
readRules(rules[i].cssRules || rules[i].rules);
}
}
}
}
/**
* Searches all css rules and setups the event listener to all elements with element query rules..
*/
this.init = function() {
for (var i = 0, j = document.styleSheets.length; i < j; i++) {
var rules;
try {
rules = document.styleSheets[i].cssText || document.styleSheets[
i].cssRules || document.styleSheets[i].rules;
readRules( rules );
} catch (e) {
console.warn("Can't read the css rules of: " + document.styleSheets[i].href, e);
continue;
}
}
}
}
function init() {
new L.ElementQueries().init();
}
if (window.addEventListener) {
window.addEventListener('load', init, false);
} else {
window.attachEvent('onload', init);
}
window.L.ResizeSensor = function(element, callback) {
/**
* Adds a listener to the over/under-flow event.
*
* @param {HTMLElement} element
* @param {Function} callback
*/
function addResizeListener(element, callback) {
if (window.OverflowEvent) {
// webkit [deprecated!]
// element.addEventListener('overflowchanged', function(e) {
// callback.call(this, e);
// });
} else {
element.addEventListener('overflow', function(e) {
callback.call(this, e);
});
element.addEventListener('underflow', function(e) {
callback.call(this, e);
});
}
}
function EventQueue() {
this.q = [];
this.add = function(ev) {
this.q.push(ev);
};
var i, j;
this.call = function() {
for (i = 0, j = this.q.length; i < j; i++) {
this.q[i].call();
}
};
}
/**
* @param {HTMLElement} element
* @param {String} prop
* @returns {String|Number}
*/
function getComputedStyle(element, prop) {
if (element.currentStyle) {
return element.currentStyle[prop];
} else if (window.getComputedStyle) {
return window.getComputedStyle(element, null).getPropertyValue(prop);
} else {
return element.style[prop];
}
}
/**
*
* @param {HTMLElement} element
* @param {Function} resized
*/
function attachResizeEvent(element, resized) {
if (!element.resizedAttached) {
element.resizedAttached = new EventQueue();
element.resizedAttached.add(resized);
} else if (element.resizedAttached) {
element.resizedAttached.add(resized);
return;
}
/*if ('onresize' in element) {
//internet explorer
if (element.attachEvent) {
element.attachEvent('onresize', function() {
element.resizedAttached.call();
});
} else if (element.addEventListener) {
element.addEventListener('resize', function(){
element.resizedAttached.call();
});
}
} else {*/
var myResized = function() {
if (setupSensor()) {
element.resizedAttached.call();
}
};
element.resizeSensor = document.createElement('div');
element.resizeSensor.className = 'resize-sensor';
var style =
'position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1;';
element.resizeSensor.style.cssText = style;
element.resizeSensor.innerHTML =
'<div class="resize-sensor-overflow" style="' + style + '">' +
'<div></div>' +
'</div>' +
'<div class="resize-sensor-underflow" style="' + style + '">' +
'<div></div>' +
'</div>';
element.appendChild(element.resizeSensor);
if ('absolute' !== getComputedStyle(element, 'position')) {
element.style.position = 'relative';
}
var x = -1,
y = -1,
firstStyle = element.resizeSensor.firstElementChild.firstChild.style,
lastStyle = element.resizeSensor.lastElementChild.firstChild.style;
function setupSensor() {
var change = false,
width = element.resizeSensor.offsetWidth,
height = element.resizeSensor.offsetHeight;
if (x != width) {
firstStyle.width = (width - 1) + 'px';
lastStyle.width = (width + 1) + 'px';
change = true;
x = width;
}
if (y != height) {
firstStyle.height = (height - 1) + 'px';
lastStyle.height = (height + 1) + 'px';
change = true;
y = height;
}
return change;
}
setupSensor();
addResizeListener(element.resizeSensor, myResized);
addResizeListener(element.resizeSensor.firstElementChild, myResized);
addResizeListener(element.resizeSensor.lastElementChild, myResized);
/*}*/
}
if ('array' === typeof element ||
('undefined' !== typeof jQuery && element instanceof jQuery) //jquery
||
('undefined' !== typeof Elements && element instanceof Elements) //mootools
) {
var i = 0,
j = element.length;
for (; i < j; i++) {
attachResizeEvent(element[i], callback);
}
} else {
attachResizeEvent(element, callback);
}
}
})();
/* global Erizo*/
/*
* Speaker represents the volume icon that will be shown in the mediaPlayer, for example.
* It manages the volume level of the media tag given in the constructor.
* Every Speaker is a View.
* Ex.: var speaker = Speaker({elementID: element, media: mediaTag, id: id, doc: document});
*/
Erizo.Speaker = function(spec) {
'use strict';
var that = Erizo.View({}),
show,
mute,
unmute,
lastVolume = 50;
// Variables
// current document object where Speaker will be located
that.document = spec.doc || document;
// DOM element in which the Speaker will be appended
that.elementID = spec.elementID;
// media tag
that.media = spec.media;
// Speaker id
that.id = spec.id;
// MediaStream
that.stream = spec.stream;
// Container
that.div = that.document.createElement('div');
that.div.setAttribute('style',
'width: 40%; height: 100%; max-width: 32px; position: absolute; right: 0;z-index:0;'
);
// Volume icon
that.icon = that.document.createElement('img');
that.icon.setAttribute('id', 'volume_' + that.id);
that.icon.setAttribute('src', PureRTC.Images.sound48);
that.icon.setAttribute('style',
'width: 80%; height: 100%; position: absolute;');
that.div.appendChild(that.icon);
if (that.stream instanceof PureRTC.RemoteStream) {
// Volume bar
that.picker = that.document.createElement('input');
that.picker.setAttribute('id', 'picker_' + that.id);
that.picker.type = 'range';
that.picker.min = 0;
that.picker.max = 100;
that.picker.step = 10;
that.picker.value = lastVolume;
that.picker.orient = 'vertical'; // FireFox supports range sliders as of version 23
that.div.appendChild(that.picker);
that.media.volume = that.picker.value / 100;
that.media.muted = false;
that.picker.oninput = function() {
if (that.picker.value > 0) {
that.media.muted = false;
that.icon.setAttribute('src', PureRTC.Images.sound48);
} else {
that.media.muted = true;
that.icon.setAttribute('src', PureRTC.Images.mute48);
}
that.media.volume = that.picker.value / 100;
};
// Private functions
show = function(displaying) {
that.picker.setAttribute('style',
'width: 32px; height: 100px; position: absolute; bottom: 90%; z-index: 1;' +
that.div.offsetHeight +
'px; right: 0px; -webkit-appearance: slider-vertical; display: ' +
displaying);
};
mute = function() {
that.icon.setAttribute('src', PureRTC.Images.mute48);
lastVolume = that.picker.value;
that.picker.value = 0;
that.media.volume = 0;
that.media.muted = true;
};
unmute = function() {
that.icon.setAttribute('src', PureRTC.Images.sound48);
that.picker.value = lastVolume;
that.media.volume = that.picker.value / 100;
that.media.muted = false;
};
that.icon.onclick = function() {
if (that.media.muted) {
unmute();
} else {
mute();
}
};
// Public functions
that.div.onmouseover = function() {
show('block');
};
that.div.onmouseout = function() {
show('none');
};
show('none');
} else if (that.stream instanceof PureRTC.LocalStream) {
mute = function() {
if (that.stream.mediaStream.getAudioTracks().length == 0)
return;
that.media.muted = true;
that.icon.setAttribute('src', PureRTC.Images.mute48);
that.stream.mediaStream.getAudioTracks()[0].enabled = false;
};
unmute = function() {
if (that.stream.mediaStream.getAudioTracks().length == 0)
return;
that.media.muted = false;
that.icon.setAttribute('src', PureRTC.Images.sound48);
that.stream.mediaStream.getAudioTracks()[0].enabled = true;
};
that.icon.onclick = function() {
if (that.media.muted) {
unmute();
} else {
mute();
}
};
}
that.document.getElementById(that.elementID).appendChild(that.div);
return that;
};
/* global Erizo */
/*
* View class represents a HTML component
* Every view is an EventDispatcher.
*/
Erizo.View = function(spec) {
'use strict';
var that = PureRTC.EventDispatcher(spec);
// Variables
// URL where it will look for icons and assets
that.url = spec.url || '.';
return that;
};
/* global window, webkitURL, Erizo, L */
/*
* VideoPlayer represents a Licode video component that shows either a local or a remote video.
* Ex.: var player = VideoPlayer({id: id, stream: stream, elementID: elementID, doc: document});
* A VideoPlayer is also a View component.
*/
Erizo.VideoPlayer = function(spec) {
'use strict';
var that = Erizo.View({}),
onmouseover,
onmouseout;
// Variables
// current document object where VideoPlayer will be located
that.document = spec.doc || document;
// VideoPlayer ID
that.id = spec.id;
// Stream that the VideoPlayer will play
that.stream = spec.stream.mediaStream;
// DOM element in which the VideoPlayer will be appended
that.elementID = spec.elementID;
// Private functions
onmouseover = function() {
that.bar.display();
};
onmouseout = function() {
that.bar.hide();
};
// Public functions
// It will stop the VideoPlayer and remove it from the HTML
that.destroy = function() {
that.video.pause();
delete that.resizer;
that.parentNode.removeChild(that.div);
};
that.resize = function(hasAbsoluteLeft) {
var width = that.container.offsetWidth,
height = that.container.offsetHeight;
if (hasAbsoluteLeft) {
that.video.style.width = "calc(100% + " + ((4 / 3) * height - width) +
"px)";
that.video.style.height = "100%";
that.video.style.top = '0px';
that.video.style.left = -((4 / 3) * height / 2 - width / 2) + 'px';
} else {
that.video.style.height = '100%';
that.video.style.width = '100%';
that.video.style.left = '0px';
that.video.style.top = '0px';
}
that.containerWidth = width;
that.containerHeight = height;
};
/*window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
document.getElementById(key).value = unescape(value);
});*/
// that.stream_url = (window.URL || webkitURL).createObjectURL(that.stream);
// Container
that.div = that.document.createElement('div');
that.div.setAttribute('id', 'player_' + that.id);
that.div.setAttribute('style',
'width: 100%; height: 100%; position: relative; background-color: black; overflow: hidden;'
);
// Video tag
that.video = that.document.createElement('video');
that.video.setAttribute('id', 'stream' + that.id);
that.video.setAttribute('style',
'width: 100%; height: 100%; position: absolute');
that.video.setAttribute('autoplay', 'autoplay');
if (spec.stream instanceof PureRTC.LocalStream) {
that.video.volume = 0;
}
if (that.elementID !== undefined) {
that.document.getElementById(that.elementID).appendChild(that.div);
that.container = that.document.getElementById(that.elementID);
} else {
that.document.body.appendChild(that.div);
that.container = that.document.body;
}
var fullscreenHandler = (function() {
var requestFS = ['requestFullScreen', 'webkitRequestFullScreen',
'mozRequestFullScreen', 'msRequestFullScreen',
'oRequestFullScreen'
];
function _enterFullScreen(tag) {
var method;
for (var p in requestFS) {
if (typeof tag[requestFS[p]] === 'function') {
method = requestFS[p];
break;
}
}
if (typeof tag[method] === 'function') {
tag[method]();
}
}
function _exitFullScreen() {
if (typeof that.document.exitFullscreen === 'function') {
that.document.exitFullscreen();
} else if (typeof that.document.webkitExitFullscreen === 'function') {
that.document.webkitExitFullscreen();
} else if (typeof that.document.mozCancelFullScreen === 'function') {
that.document.mozCancelFullScreen();
}
}
return function(tag) {
if (tag.offsetWidth == screen.width) _exitFullScreen();
else _enterFullScreen(tag);
};
}());
that.parentNode = that.div.parentNode;
that.div.appendChild(that.video);
that.video.addEventListener('playing', function display() {
if (that.video.videoWidth * that.video.videoHeight > 4) { // why remote video size initially is 2*2 in chrome?
L.Logger.debug('video dimensions:', that.video.videoWidth, that.video
.videoHeight);
return;
}
setTimeout(display, 50);
});
that.containerWidth = 0;
that.containerHeight = 0;
that.resizer = new L.ResizeSensor(that.container, that.resize);
that.resize();
that.div.ondblclick = function() {
fullscreenHandler(that.video);
};
// Bottom Bar
that.bar = new Erizo.Bar({
elementID: 'player_' + that.id,
id: that.id,
stream: spec.stream,
media: that.video,
options: spec.options,
doc: that.document
});
that.div.onmouseover = onmouseover;
that.div.onmouseout = onmouseout;
//that.video.src = that.stream_url;
that.video.srcObject = that.stream;
return that;
};
/* global Erizo */
PureRTC.Images = {
sound48: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAQAAAD9CzEMAAAABGdBTUEAALGOfPtRkwAACidpQ0NQaWNjAAB42p2Wd1RU1xaHz713eqHNMAJSht67wADSe5NeRWGYGWAoAw4zNLEhogIRRUSaIkhQxIDRUCRWRLEQFFSwByQIKDEYRVQsb0bWi66svPfy8vvjrG/ts/e5++y9z1oXAJKnL5eXBksBkMoT8IM8nOkRkVF07ACAAR5ggCkATFZGul+wewgQycvNhZ4hcgJfBAHweli8AnDT0DOATgf/n6RZ6XyB6JgAEZuzORksEReIOCVLkC62z4qYGpcsZhglZr4oQRHLiTlhkQ0++yyyo5jZqTy2iMU5p7NT2WLuFfG2TCFHxIiviAszuZwsEd8SsUaKMJUr4jfi2FQOMwMAFElsF3BYiSI2ETGJHxLkIuLlAOBICV9x3Fcs4GQLxJdySUvP4XMTEgV0HZYu3dTamkH35GSlcAQCwwAmK5nJZ9Nd0lLTmbwcABbv/Fky4trSRUW2NLW2tDQ0MzL9qlD/dfNvStzbRXoZ+LlnEK3/i+2v/NIaAGDMiWqz84strgqAzi0AyN37YtM4AICkqG8d17+6D008L4kCQbqNsXFWVpYRl8MyEhf0D/1Ph7+hr75nJD7uj/LQXTnxTGGKgC6uGystJU3Ip2ekM1kcuuGfh/gfB/51HgZBnHgOn8MTRYSJpozLSxC1m8fmCrhpPDqX95+a+A/D/qTFuRaJ0vgRUGOMgNR1KkB+7QcoChEg0fvFXf+jb774MCB+eeEqk4tz/+83/WfBpeIlg5vwOc4lKITOEvIzF/fEzxKgAQFIAiqQB8pAHegAQ2AGrIAtcARuwBv4gxAQCVYDFkgEqYAPskAe2AQKQTHYCfaAalAHGkEzaAXHQSc4Bc6DS+AauAFug/tgFEyAZ2AWvAYLEARhITJEgeQhFUgT0ofMIAZkD7lBvlAQFAnFQgkQDxJCedBmqBgqg6qheqgZ+h46CZ2HrkCD0F1oDJqGfofewQhMgqmwEqwFG8MM2An2gUPgVXACvAbOhQvgHXAl3AAfhTvg8/A1+DY8Cj+D5xCAEBEaoooYIgzEBfFHopB4hI+sR4qQCqQBaUW6kT7kJjKKzCBvURgUBUVHGaJsUZ6oUBQLtQa1HlWCqkYdRnWgelE3UWOoWdRHNBmtiNZH26C90BHoBHQWuhBdgW5Ct6Mvom+jJ9CvMRgMDaONscJ4YiIxSZi1mBLMPkwb5hxmEDOOmcNisfJYfawd1h/LxAqwhdgq7FHsWewQdgL7BkfEqeDMcO64KBwPl4+rwB3BncEN4SZxC3gpvCbeBu+PZ+Nz8KX4Rnw3/jp+Ar9AkCZoE+wIIYQkwiZCJaGVcJHwgPCSSCSqEa2JgUQucSOxkniMeJk4RnxLkiHpkVxI0SQhaQfpEOkc6S7pJZlM1iI7kqPIAvIOcjP5AvkR+Y0ERcJIwkuCLbFBokaiQ2JI4rkkXlJT0klytWSuZIXkCcnrkjNSeCktKRcpptR6qRqpk1IjUnPSFGlTaX/pVOkS6SPSV6SnZLAyWjJuMmyZApmDMhdkxikIRZ3iQmFRNlMaKRcpE1QMVZvqRU2iFlO/ow5QZ2VlZJfJhslmy9bInpYdpSE0LZoXLYVWSjtOG6a9W6K0xGkJZ8n2Ja1LhpbMyy2Vc5TjyBXJtcndlnsnT5d3k0+W3yXfKf9QAaWgpxCokKWwX+GiwsxS6lLbpaylRUuPL72nCCvqKQYprlU8qNivOKekrOShlK5UpXRBaUaZpuyonKRcrnxGeVqFomKvwlUpVzmr8pQuS3eip9Ar6b30WVVFVU9VoWq96oDqgpq2Wqhavlqb2kN1gjpDPV69XL1HfVZDRcNPI0+jReOeJl6ToZmouVezT3NeS1srXGurVqfWlLactpd2rnaL9gMdso6DzhqdBp1buhhdhm6y7j7dG3qwnoVeol6N3nV9WN9Sn6u/T3/QAG1gbcAzaDAYMSQZOhlmGrYYjhnRjHyN8o06jZ4baxhHGe8y7jP+aGJhkmLSaHLfVMbU2zTftNv0dzM9M5ZZjdktc7K5u/kG8y7zF8v0l3GW7V92x4Ji4Wex1aLH4oOllSXfstVy2krDKtaq1mqEQWUEMEoYl63R1s7WG6xPWb+1sbQR2By3+c3W0DbZ9ojt1HLt5ZzljcvH7dTsmHb1dqP2dPtY+wP2ow6qDkyHBofHjuqObMcmx0knXackp6NOz51NnPnO7c7zLjYu61zOuSKuHq5FrgNuMm6hbtVuj9zV3BPcW9xnPSw81nqc80R7+nju8hzxUvJieTV7zXpbea/z7vUh+QT7VPs89tXz5ft2+8F+3n67/R6s0FzBW9HpD/y9/Hf7PwzQDlgT8GMgJjAgsCbwSZBpUF5QXzAlOCb4SPDrEOeQ0pD7oTqhwtCeMMmw6LDmsPlw1/Cy8NEI44h1EdciFSK5kV1R2KiwqKaouZVuK/esnIi2iC6MHl6lvSp71ZXVCqtTVp+OkYxhxpyIRceGxx6Jfc/0ZzYw5+K84mrjZlkurL2sZ2xHdjl7mmPHKeNMxtvFl8VPJdgl7E6YTnRIrEic4bpwq7kvkjyT6pLmk/2TDyV/SglPaUvFpcamnuTJ8JJ5vWnKadlpg+n66YXpo2ts1uxZM8v34TdlQBmrMroEVNHPVL9QR7hFOJZpn1mT+SYrLOtEtnQ2L7s/Ry9ne85krnvut2tRa1lre/JU8zblja1zWle/Hloft75ng/qGgg0TGz02Ht5E2JS86ad8k/yy/Febwzd3FygVbCwY3+KxpaVQopBfOLLVdmvdNtQ27raB7ebbq7Z/LGIXXS02Ka4ofl/CKrn6jek3ld982hG/Y6DUsnT/TsxO3s7hXQ67DpdJl+WWje/2291RTi8vKn+1J2bPlYplFXV7CXuFe0crfSu7qjSqdla9r06svl3jXNNWq1i7vXZ+H3vf0H7H/a11SnXFde8OcA/cqfeo72jQaqg4iDmYefBJY1hj37eMb5ubFJqKmz4c4h0aPRx0uLfZqrn5iOKR0ha4RdgyfTT66I3vXL/rajVsrW+jtRUfA8eEx55+H/v98HGf4z0nGCdaf9D8obad0l7UAXXkdMx2JnaOdkV2DZ70PtnTbdvd/qPRj4dOqZ6qOS17uvQM4UzBmU9nc8/OnUs/N3M+4fx4T0zP/QsRF271BvYOXPS5ePmS+6ULfU59Zy/bXT51xebKyauMq53XLK919Fv0t/9k8VP7gOVAx3Wr6103rG90Dy4fPDPkMHT+puvNS7e8bl27veL24HDo8J2R6JHRO+w7U3dT7r64l3lv4f7GB+gHRQ+lHlY8UnzU8LPuz22jlqOnx1zH+h8HP74/zhp/9kvGL+8nCp6Qn1RMqkw2T5lNnZp2n77xdOXTiWfpzxZmCn+V/rX2uc7zH35z/K1/NmJ24gX/xaffS17Kvzz0atmrnrmAuUevU18vzBe9kX9z+C3jbd+78HeTC1nvse8rP+h+6P7o8/HBp9RPn/4FA5jz/EHct2gAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAAJiS0dEAP+Hj8y/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAACXZwQWcAAAAwAAAAMADO7oxXAAAHsUlEQVRYw+1XfVSUVRp/ZgYGcPgc+TRNPgtRhDZELEhdPX62kZuSCGoqIIsalKUejazctW3LXE9ohoAppeDXKOdQIpsgioKCqwYKAwoCKSDDt8LAzNx+dwYExMMiq//1/s6ZM/e+931+z+e9zyX64/njGfARkJBEWgjx/xmIF5GBYJiliVBChqT3/1MI+ogQQOQwkoaPvfXRHj+yJGPSHzoF11UPAvTx2+0MERmRtb9XxUGNqi4z1I/sQDFEK4QQLCGzkFGBXIgYY06pT+bkkveNRqVhGk3J0fFeZANK0VDEG5AZ2cXPbkq7tg5CTCFaCF0l0Pml9etq76qZmnW2n/onudJwrBU8qWsMSWrgkBTWKmcsLxZCbCDaiEwcbPVdyJcC18c1tHYyFWuuWLeQRoP+CdykC6PlSNf06LYaNetg6T/QRHIgK7Ja7l62PT6M/kyL9D5I+KVN3QGKguPW3mQLcuHgxUvI2tszP6ajpZO1MyVLOUzTaCw5+k+8naRRN5SsiKAACnfaeqVMibf3m75aTc6IzKCyiQfWhGwX+5UcVimVrA1oZ8dkNJteJo/0rZ0d7ayTFZ13WkZLKTLqQIOyDRS/ppr50IiBbBB0QaQN7MiNr989o1K3sftaPGBHjtPrNIkmvRFSdOMBxu0diXtoEYVYRV8obsO4oXbVUthgBtsfU/J6SEADhNQIfjdB4TjsXNLwaycEtwCtwH12mBO8gtD+dfHXVY2tEFldPnUNBdPqTcmNKqzQpOwiT6TBI7mkyxUTiaXzKGd7R4fRTs5uQVPSt7RWdkB080O0sEMymkveNIHm0Krv0ps0zeyB+sfvYcOK8X8vruZqFOXYTEMuSfo6idekdL1X5S7FyXtptWk1p6ozmks64dUm1tgLTSyZx8CT3GBFoNfWopomkMoLng+hxcKoE7nNWFFdvWAJvYhA6/XWn9ek/YXPVRoV04HnTAtreASNLElGMyH+eRpDMyg8MZPP1jWHboYNYVuSFZoGplD+I5rGI5H1exMYoiY9/7OvkdU/hOIxqGcHZRD8IlnTc3BTQMjuO0oFa9B8t5cC6Z35/668j1WaxN1w4gjIFPQkpARe8z2SXMfuDYg6doATuJAFSUEz66WN12vrQJyWTotpiefHRff46CcZvQZ5w3oTGJMTTTtwtJbVDIha9qOOwAxfjCJfm/DsYk6c81/D5RRs90H+bT7KzDScCXkmPWEWYuBCMxJl1ezugKhmiToCUzjAhrzEwen5nDivSLqSgoZHZstrsCYrx/wv9AKUED1CsF92h/02IO6w/d0EBqgTD2HAyYucOEduFQGCNWeL7mLNmUsWbyIJzPsR7JNVscoBUcX2dROIsTG7mwacvsyJz9ww/hsFWa+5UMJH6TnG85BpFv0I9soq2O0BUcH29iFwC84rqQBxyiURXOT43uVKPjqWIfB/rAUJsnJWNiDKWUKPi6zIY8FqeX05ZnefpBUUNG1TgUI7kqHOXR+NgTNN332oR9Qt4GY/3GJx3QRGqJwJu7aXq24yeWdkAr1Di1Z9Vdp+k5Woo+NRjHyNsHcd2NNk/49PFp9XnFOcqz9bn9tc0FHKSllJH5SyPToCc3xubz394pkyzF2q89lCSygoLvkW1hQ8CNiKY8gRiSzsqWQjVJ43BdhtcN7mGOO40+Fbt7h5x/YUXFXKWXEvyFksJ3gB/rcit81RlY1yiDyYZ/AuBVusOJdfihVnq8ZEYrcdBZmCHgKxtjKn0AIKoVW0hqLoQ/rI8IuVqecab7DrD3GDfcsJXOGe0a5TCzPKMHelfVk8hVLg2xuL6/mK/dnCYOIHZ58NW4TCtoFmE2gyTYcHZ9ObFEQRgk2zE1N+u8auduEai5HRLBpHjgKPn7+uVl7DTFLh8A08AocOl2BFfmdEHL51h436/Q93KdcM3nMG1Tgc7rOww7w/Libu+iV1PuO4zHbwDPkTucdHKeoKMZPV4h9L4RQ0ObKo4grep1a5rOtKA1H/9kQMv0kQHBO8lmK/HEN+cNpq6y8+O5+lzGU5LJdt4weOD/ns+PRqYy47r4pOF63l+h9JkmtysOKTVIT7FRr5+FNZdxoLtdDjRxAaFG96g1Yabg5N/ak5m2WzL3VHpp9wfkRCWlPMZavNFEaBoZvKa3Lx9kS1x2fkDwdZ/u++ghOJYclInF+zaJlgw8wfDt7JYv/iLpoIzNEPm7PD6RNCBbuHFeYVskx2Wr02BQU3BWoZD66B5I2LMfJhLPI6iN7z2P7NxaidSAIP7DQ+sCQQswulwRknKtQZ7DSLLbbbSPOgkPXg20dd8K0QdF96i5ZLwozeRtPihPg4gOZVEM89ElurzGKn2KH6yTvRIb2GNyZP0gB39ab47GWImwGiMdr21xy/Djh7p3657Zemn9nxtqVHUUFzYa3lk7a/1BWNEUhfV2hvCw3FcJ4RNmR7miRcuPT7A4qoNPFamo86eg55KBra/WAY9kcLaC7R3g94EhjC2+40Vxzm/bnZ+7QQ1jlgzRBvObrOj99wuq97PD6mKExv5NVbSGZflJcU7hE+vTsav5hYgGIc4jMeO8Bw2CR8urdMTmGKLLOFs8wgXvT0r7LccbxhNtBeq57BTbmn4X8mwgf1/A4Bm4o+oeC48QAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAwOS0wOS0xM1QxNjoxMjo1OCswMjowMBbbrGkAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDktMDktMTNUMTY6MTI6NTgrMDI6MDBnhhTVAAAAAElFTkSuQmCC',
mute48: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAQAAAD9CzEMAAAABGdBTUEAALGOfPtRkwAACidpQ0NQaWNjAAB42p2Wd1RU1xaHz713eqHNMAJSht67wADSe5NeRWGYGWAoAw4zNLEhogIRRUSaIkhQxIDRUCRWRLEQFFSwByQIKDEYRVQsb0bWi66svPfy8vvjrG/ts/e5++y9z1oXAJKnL5eXBksBkMoT8IM8nOkRkVF07ACAAR5ggCkATFZGul+wewgQycvNhZ4hcgJfBAHweli8AnDT0DOATgf/n6RZ6XyB6JgAEZuzORksEReIOCVLkC62z4qYGpcsZhglZr4oQRHLiTlhkQ0++yyyo5jZqTy2iMU5p7NT2WLuFfG2TCFHxIiviAszuZwsEd8SsUaKMJUr4jfi2FQOMwMAFElsF3BYiSI2ETGJHxLkIuLlAOBICV9x3Fcs4GQLxJdySUvP4XMTEgV0HZYu3dTamkH35GSlcAQCwwAmK5nJZ9Nd0lLTmbwcABbv/Fky4trSRUW2NLW2tDQ0MzL9qlD/dfNvStzbRXoZ+LlnEK3/i+2v/NIaAGDMiWqz84strgqAzi0AyN37YtM4AICkqG8d17+6D008L4kCQbqNsXFWVpYRl8MyEhf0D/1Ph7+hr75nJD7uj/LQXTnxTGGKgC6uGystJU3Ip2ekM1kcuuGfh/gfB/51HgZBnHgOn8MTRYSJpozLSxC1m8fmCrhpPDqX95+a+A/D/qTFuRaJ0vgRUGOMgNR1KkB+7QcoChEg0fvFXf+jb774MCB+eeEqk4tz/+83/WfBpeIlg5vwOc4lKITOEvIzF/fEzxKgAQFIAiqQB8pAHegAQ2AGrIAtcARuwBv4gxAQCVYDFkgEqYAPskAe2AQKQTHYCfaAalAHGkEzaAXHQSc4Bc6DS+AauAFug/tgFEyAZ2AWvAYLEARhITJEgeQhFUgT0ofMIAZkD7lBvlAQFAnFQgkQDxJCedBmqBgqg6qheqgZ+h46CZ2HrkCD0F1oDJqGfofewQhMgqmwEqwFG8MM2An2gUPgVXACvAbOhQvgHXAl3AAfhTvg8/A1+DY8Cj+D5xCAEBEaoooYIgzEBfFHopB4hI+sR4qQCqQBaUW6kT7kJjKKzCBvURgUBUVHGaJsUZ6oUBQLtQa1HlWCqkYdRnWgelE3UWOoWdRHNBmtiNZH26C90BHoBHQWuhBdgW5Ct6Mvom+jJ9CvMRgMDaONscJ4YiIxSZi1mBLMPkwb5hxmEDOOmcNisfJYfawd1h/LxAqwhdgq7FHsWewQdgL7BkfEqeDMcO64KBwPl4+rwB3BncEN4SZxC3gpvCbeBu+PZ+Nz8KX4Rnw3/jp+Ar9AkCZoE+wIIYQkwiZCJaGVcJHwgPCSSCSqEa2JgUQucSOxkniMeJk4RnxLkiHpkVxI0SQhaQfpEOkc6S7pJZlM1iI7kqPIAvIOcjP5AvkR+Y0ERcJIwkuCLbFBokaiQ2JI4rkkXlJT0klytWSuZIXkCcnrkjNSeCktKRcpptR6qRqpk1IjUnPSFGlTaX/pVOkS6SPSV6SnZLAyWjJuMmyZApmDMhdkxikIRZ3iQmFRNlMaKRcpE1QMVZvqRU2iFlO/ow5QZ2VlZJfJhslmy9bInpYdpSE0LZoXLYVWSjtOG6a9W6K0xGkJZ8n2Ja1LhpbMyy2Vc5TjyBXJtcndlnsnT5d3k0+W3yXfKf9QAaWgpxCokKWwX+GiwsxS6lLbpaylRUuPL72nCCvqKQYprlU8qNivOKekrOShlK5UpXRBaUaZpuyonKRcrnxGeVqFomKvwlUpVzmr8pQuS3eip9Ar6b30WVVFVU9VoWq96oDqgpq2Wqhavlqb2kN1gjpDPV69XL1HfVZDRcNPI0+jReOeJl6ToZmouVezT3NeS1srXGurVqfWlLactpd2rnaL9gMdso6DzhqdBp1buhhdhm6y7j7dG3qwnoVeol6N3nV9WN9Sn6u/T3/QAG1gbcAzaDAYMSQZOhlmGrYYjhnRjHyN8o06jZ4baxhHGe8y7jP+aGJhkmLSaHLfVMbU2zTftNv0dzM9M5ZZjdktc7K5u/kG8y7zF8v0l3GW7V92x4Ji4Wex1aLH4oOllSXfstVy2krDKtaq1mqEQWUEMEoYl63R1s7WG6xPWb+1sbQR2By3+c3W0DbZ9ojt1HLt5ZzljcvH7dTsmHb1dqP2dPtY+wP2ow6qDkyHBofHjuqObMcmx0knXackp6NOz51NnPnO7c7zLjYu61zOuSKuHq5FrgNuMm6hbtVuj9zV3BPcW9xnPSw81nqc80R7+nju8hzxUvJieTV7zXpbea/z7vUh+QT7VPs89tXz5ft2+8F+3n67/R6s0FzBW9HpD/y9/Hf7PwzQDlgT8GMgJjAgsCbwSZBpUF5QXzAlOCb4SPDrEOeQ0pD7oTqhwtCeMMmw6LDmsPlw1/Cy8NEI44h1EdciFSK5kV1R2KiwqKaouZVuK/esnIi2iC6MHl6lvSp71ZXVCqtTVp+OkYxhxpyIRceGxx6Jfc/0ZzYw5+K84mrjZlkurL2sZ2xHdjl7mmPHKeNMxtvFl8VPJdgl7E6YTnRIrEic4bpwq7kvkjyT6pLmk/2TDyV/SglPaUvFpcamnuTJ8JJ5vWnKadlpg+n66YXpo2ts1uxZM8v34TdlQBmrMroEVNHPVL9QR7hFOJZpn1mT+SYrLOtEtnQ2L7s/Ry9ne85krnvut2tRa1lre/JU8zblja1zWle/Hloft75ng/qGgg0TGz02Ht5E2JS86ad8k/yy/Febwzd3FygVbCwY3+KxpaVQopBfOLLVdmvdNtQ27raB7ebbq7Z/LGIXXS02Ka4ofl/CKrn6jek3ld982hG/Y6DUsnT/TsxO3s7hXQ67DpdJl+WWje/2291RTi8vKn+1J2bPlYplFXV7CXuFe0crfSu7qjSqdla9r06svl3jXNNWq1i7vXZ+H3vf0H7H/a11SnXFde8OcA/cqfeo72jQaqg4iDmYefBJY1hj37eMb5ubFJqKmz4c4h0aPRx0uLfZqrn5iOKR0ha4RdgyfTT66I3vXL/rajVsrW+jtRUfA8eEx55+H/v98HGf4z0nGCdaf9D8obad0l7UAXXkdMx2JnaOdkV2DZ70PtnTbdvd/qPRj4dOqZ6qOS17uvQM4UzBmU9nc8/OnUs/N3M+4fx4T0zP/QsRF271BvYOXPS5ePmS+6ULfU59Zy/bXT51xebKyauMq53XLK919Fv0t/9k8VP7gOVAx3Wr6103rG90Dy4fPDPkMHT+puvNS7e8bl27veL24HDo8J2R6JHRO+w7U3dT7r64l3lv4f7GB+gHRQ+lHlY8UnzU8LPuz22jlqOnx1zH+h8HP74/zhp/9kvGL+8nCp6Qn1RMqkw2T5lNnZp2n77xdOXTiWfpzxZmCn+V/rX2uc7zH35z/K1/NmJ24gX/xaffS17Kvzz0atmrnrmAuUevU18vzBe9kX9z+C3jbd+78HeTC1nvse8rP+h+6P7o8/HBp9RPn/4FA5jz/EHct2gAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAAJiS0dEAP+Hj8y/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAACXZwQWcAAAAwAAAAMADO7oxXAAAD9UlEQVRYw+2Ya0wUVxTH/7PAwgo+l1WU9bG+RS0lqY1oNYKvpjWaGGOMxmht4tsPShpstBJbEm3QCssuIIiPTvWD/dIalagoqNFWa9oGoqAr8YVrKaKisuCy7Om5u1IeSrJXhm/OL5OdmZ17/nfOuXfuOQO837p0UxgdgnjX8VEXmA9GGLr5CGUZjSV0CEEEjN/G3U7Km4heLBWkZd913OeeiE6b8+SCt/FyCgayRIhWz6BwX8PQW2c5+GVtOVETFWRgPKL4mqKVeQOMkSNPfu1yetl8Ix3NQzw/QzctBERgw2GKi72yx10rjDeSm9RDmAYLX1c6b14ENmpefPlhT4OHTQte0UEViRjK/yidHTd69EB00qcPzjR5GtmwnwYhML2zAv5x0wuDrItr/m7ymW2mng50WuB1YA3Djm54cdfDJl28N+Oi/ZICorfB7G29j1DGwK7pZxlXlFr/2E117XhJ+VICYoYa0J3dYYTJRz+YzWO2zCs74q5rYHPteSElIALZ3dj/2PyKXRU2h81hd9hv2m/lPzztcnq8Lnr+Fmppn4RAkBiGuQvrK73UGg8H8zk9Y2Nv8ozyAhZQuP9GjCpIq/OKhn78R0875AnlSggYMAAfHdlbQ4HzmPZKCIRjMKbsO1DNzQKlmnKkBCxIyPmxiv4NmCrKlhCI4BunZ6v/kAzSAlmqkx4FjJOyZAXsaiU9pMqAEPfZZQVs6n16EDD3ySYrkKneIxkyZQWs6h2SwSorkKFWkAwZkgKJ6aqDbgeMg9KlJtoQTEu2lrkd1JZbHXKT9ki9i8yYFL3SXlj46Fz12dcU1Vypu+4tp7K3coN2SwiEoi9iMTd0vSm17w+mdFMGYzVnxatJxSerSrylVEKl7SihNIn1IJgTwiGYiLlYhtVYx6zHBmxEMraPzskuveb5i/5sgzj/XmpFC0MflojFJCRwsxmYiVn4HAuwApt679x6/kL9NfqjDVdph4SAP+/swY4ys4yFmw3FcIzGh5iK+Vil37bslxNPf6ff2pAqtej7cwqRSYQzEYxIAEwsF4fZWI7khEOHKy9Sa757h7xIaYWoYTgV4Pw5hnPQxdg4KjP9+rmmYiryUUzbNcnsgjl7jsQITOZ4rIvcufnSiYazVOgjRQOB/6sCdtUEHmMr9SlLj/9ce5pOMd9oItCS/po55LPxhbI58ad8ZwEV0FaNsuuWykxEIwFLsCnGtvvGr+6vcrWqD1qiYcJIfMLRWBu1bVFOzBqeNRpVOK2jYeFofIZFWMgO+4Az2DDtClnFF42eHI2x+JhfLLEYxGch2lbKIhrhPHCj2Tn9ta6TW1yl9814A/8Gdc3HhOZvFUpH5v8DfKpM3OEGjaEAAAAldEVYdGRhdGU6Y3JlYXRlADIwMDktMDktMTNUMTY6MTI6NTgrMDI6MDAW26xpAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDA5LTA5LTEzVDE2OjEyOjU4KzAyOjAwZ4YU1QAAAABJRU5ErkJggg=='
};
/**
* @function show
* @desc This function displays the stream on HTML in the container with id elementId.
<br><b>Remarks:</b><br>
Tag with id value elementId should be pre-created.
* @memberOf PureRTC.Stream
* @instance
* @param {string} elementId tag id of the element on HTML.
* @param {doc} document object of target window.
* @return {boolean} true If stream has been displayed already or is now displaying; otherwise false.
* @example
<script type="text/JavaScript">
stream.show('stream-div', document);
</script>
*/
PureRTC.Stream.prototype.show = function(elementId, doc) {
'use strict';
if (!(this.mediaStream) || this.showing === true) return (this.showing ===
true);
if (typeof elementId !== 'string') elementId = 'stream_' + this.id();
var options = arguments[1] || {};
this.elementId = elementId;
if (this.hasVideo()) {
this.player = new Erizo.VideoPlayer({
id: this.id(),
stream: this,
elementID: elementId,
options: options,
doc: doc
});
this.showing = true;
} else if (this.hasAudio()) {
this.player = new Erizo.AudioPlayer({
id: this.id(),
stream: this,
elementID: elementId,
options: options,
doc: doc
});
this.showing = true;
}
return (this.showing === true);
};
/**
* @function hide
* @desc This function removes the stream on HTML from its container.
* @memberOf PureRTC.Stream
* @instance
* @example
<script type="text/JavaScript">
stream.hide();
</script>
*/
PureRTC.Stream.prototype.hide = function() {
'use strict';
if (this.showing === true) {
if (this.player && typeof this.player.destroy === 'function') {
this.player.destroy();
this.showing = false;
}
}
};
/*
* PureRTC.UI provides UI functions for PureRTC SDK.
*/
PureRTC.UI = PureRTC.UI || Object.create({});
PureRTC.UI.attachMediaStream = function() {
// When attachMediaStream is removed from adapter.js, we should implement by ourself.
adapter.browserShim.attachMediaStream.apply(this, arguments);
};
window.Erizo = Erizo;
window.PureRTC = PureRTC;
window.L = L;
}(window));