/*
IIFE 立即执行函数表达式
函数体写完之后立刻执行，传入的参数是js的内置变量window
这个函数相当于给window添加了一个类TLWSA
*/
//作为命名空间的匿名函数
(function(w)
{
	//唯一公开的类  每一个页面内只能有该类的一个实例
	w.TLWSA=TLWSA;
	//TongLiWebSocketAgent class
	function TLWSA()
	{
	}
	//wbsocketagnt类的方法
	TLWSA.prototype.initTLWSA=_initTLWSA;
	
	TLWSA.prototype.tlaOpen=_tlaOpen;
	TLWSA.prototype.tlaOpenSlaveServer=_tlaOpenSlaveServer;
	TLWSA.prototype.tlaClose=_tlaClose; //本方法没有响应事件 认为一定成功
	TLWSA.prototype.tlaLogin=_tlaLogin;
	TLWSA.prototype.tlaLogoff=_tlaLogoff;
	TLWSA.prototype.tlaAcw=_tlaAcw;
	TLWSA.prototype.tlaWca=_tlaWca;
    TLWSA.prototype.tlaMute=_tlaMute;
	TLWSA.prototype.tlaUnmute=_tlaUnmute;
	TLWSA.prototype.tlaSetphonenumber=_tlaSetphonenumber;
	TLWSA.prototype.tlaCallagc=_tlaCallagc;
	TLWSA.prototype.tlaCallout=_tlaCallout;
	TLWSA.prototype.tlaCancelcall=_tlaCancelcall;
	TLWSA.prototype.tlaTransfertoagc=_tlaTransfertoagc;
	TLWSA.prototype.tlaTransferout=_tlaTransferout;
	TLWSA.prototype.tlaCanceltransfer=_tlaCanceltransfer;
	TLWSA.prototype.tlaBlindtransfertoagc=_tlaBlindtransfertoagc;
	TLWSA.prototype.tlaBlindtransferout=_tlaBlindtransferout;
	TLWSA.prototype.tlaTransfertoivr=_tlaTransfertoivr;
	TLWSA.prototype.tlaTransfertoivrandwait=_tlaTransfertoivrandwait;
	TLWSA.prototype.tlaStartlisten=_tlaStartlisten;
	TLWSA.prototype.tlaStoplisten=_tlaStoplisten;
	TLWSA.prototype.tlaKillworkid=_tlaKillworkid;
	TLWSA.prototype.tlaForceonhook=_tlaForceonhook;
	TLWSA.prototype.tlaIntrude=_tlaIntrude;
	TLWSA.prototype.tlaGetcall=_tlaGetcall;
	TLWSA.prototype.tlaForceacw=_tlaForceacw;
	TLWSA.prototype.tlaForcewca=_tlaForcewca;
	TLWSA.prototype.tlaAnswercall=_tlaAnswercall;
	TLWSA.prototype.tlaOnhook=_tlaOnhook;
	TLWSA.prototype.tlaHoldcall=_tlaHoldcall;
	TLWSA.prototype.tlaSwapcall=_tlaSwapcall;
	TLWSA.prototype.tlaActivatecall=_tlaActivatecall;
	TLWSA.prototype.tlaUnhold=_tlaUnhold;
	TLWSA.prototype.tlaLoginotheragent=_tlaLoginotheragent;
	TLWSA.prototype.tlaSendmessagetoagent=_tlaSendmessagetoagent;
	TLWSA.prototype.tlaKillbyline=_tlaKillbyline;
	TLWSA.prototype.tlaAnswerqueue=_tlaAnswerqueue;
	TLWSA.prototype.tlaOnhookline=_tlaOnhookline;
	
	TLWSA.prototype.tlaGetagentcount=_tlaGetagentcount;
	TLWSA.prototype.tlaGetagentwid=_tlaGetagentwid;
	TLWSA.prototype.tlaGetagentstate=_tlaGetagentstate;
	TLWSA.prototype.tlaGetacdlength=_tlaGetacdlength;
	TLWSA.prototype.tlaGettrknumber=_tlaGettrknumber;
	TLWSA.prototype.tlaGetlicenseinfo=_tlaGetlicenseinfo
	
	TLWSA.prototype.tlaSubscribestateinfo=_tlaSubscribestateinfo
	
	TLWSA.prototype.tlaConsulttransferout=_tlaConsulttransferout
	TLWSA.prototype.tlaConsulttransfertoagc=_tlaConsulttransfertoagc
	TLWSA.prototype.tlaCompleteconsulttransfer=_tlaCompleteconsulttransfer
	
	TLWSA.prototype.tlaGetnodestate=_tlaGetnodestate
	TLWSA.prototype.tlaTransfernode=_tlaTransfernode

	TLWSA.prototype.tlaConferencestart=_tlaConferencestart //启动会议
	TLWSA.prototype.tlaConferenceappendmember=_tlaConferenceappendmember  //添加会议成员
	TLWSA.prototype.tlaConferencequery=_tlaConferencequery  //会议状态查询
	TLWSA.prototype.tlaConferencedismiss=_tlaConferencedismiss  //解散会议
	TLWSA.prototype.tlaConferencekickout=_tlaConferencekickout  //解散会议
	TLWSA.prototype.tlaConferencechangememberstate=_tlaConferencechangememberstate  //解散会议
	//公开的事件 绑在类上 静态 所以每一个页面只能有一个类实例
	//1 调用方法的应答
	TLWSA.onOpenreturn=null; //连接到服务器的结果事件
	TLWSA.onOpenslaveserverreturn=null; //连接到双机热备系统的备用服务器的结果事件
	
	TLWSA.onLoginreturn=null;
	TLWSA.onLogoffreturn=null;
	TLWSA.onAcwreturn=null;
	TLWSA.onWcareturn=null;
	TLWSA.onMutereturn=null;
	TLWSA.onUnmutereturn=null;
	TLWSA.onSetphonenumberreturn=null;

	TLWSA.onCallreturn=null; //调用外呼方法之后，返回该事件 如果错误码为0，仅仅表示开始外呼了，不代表呼叫接通或者呼叫失败
	TLWSA.onCancelcallreturn=null;
	TLWSA.onTransferreturn=null;//调用转移方法之后，返回该事件 如果错误码为0，仅仅表示开始转移，不代表转移目标已经接听或已经拒绝
	TLWSA.onCanceltransferreturn=null;
	TLWSA.onBlindtransferreturn=null;
	TLWSA.onConsulttransferreturn=null;
	TLWSA.onCompleteconsulttransferreturn=null;
	TLWSA.onTransfertoivrreturn=null;
	TLWSA.onTransfertoivrandwaitreturn=null;
	TLWSA.onStartlistenreturn=null;
	TLWSA.onStoplistenreturn=null;
	TLWSA.onKillreturn=null;
	TLWSA.onForceonhookreturn=null;
	TLWSA.onIntrudereturn=null;
	TLWSA.onGetcallreturn=null;
	TLWSA.onAnswercallreturn=null;
	TLWSA.onOnhookreturn=null;
	TLWSA.onHoldcallreturn=null;
	TLWSA.onSwapcallreturn=null;
	TLWSA.onActivatecallreturn=null;
	TLWSA.onUnholdcallreturn=null;
	TLWSA.onLoginotheragentreturn=null;
	TLWSA.onSendmessagetoagentreturn=null;
	TLWSA.onAnswerqueuereturn=null;
	TLWSA.onSubscribestateinforeturn=null;
	TLWSA.onGetnodestatereturn=null;
	TLWSA.onTransfernodereturn=null;
	TLWSA.onConferencestartreturn=null;
	TLWSA.onConferenceappendmemberreturn=null;
	TLWSA.onConferencequeryreturn=null;
	TLWSA.onConferencedismissreturn=null;
	TLWSA.onConferencekickoutreturn=null;
	TLWSA.onConferencechangememberstatereturn=null;
	//2 主动事件
	TLWSA.onCallin=null;
	TLWSA.onCallend=null;
	TLWSA.onAcw=null;
	TLWSA.onWca=null;
	TLWSA.onLogoff=null;
	TLWSA.onCallresult=null;
	TLWSA.onTransferresult=null;
	TLWSA.onBlindtransferresult=null;
	TLWSA.onAnswer=null;
	TLWSA.onClosed=null;
	TLWSA.onSlaveserverclosed=null; //与备用服务器的连接断开
	TLWSA.onHoldcallreleased=null;
	TLWSA.onKeepcallreleased=null;
	TLWSA.onCalleealerting=null;
	TLWSA.onQueue=null;
	TLWSA.onQuitqueue=null;
	TLWSA.onRecvmsg=null;
	TLWSA.onHookstate=null;
	TLWSA.onHardwarefail=null;
	TLWSA.onMisscall=null;
	TLWSA.onConsulttransfercalleeanswer=null;
	TLWSA.onStatechange=null;
	TLWSA.onMyStatechange=null;
	TLWSA.onConferencememberstate=null; //会议成员状态变化 1呼出接通进入会议 2呼出失败 3挂机退出会议
	//双机热备相关
	TLWSA.onSlaveserverfail=null; //备用服务器失联
	TLWSA.onSwitchtoslaveserver=null; //主服务器失联，已切换到备用服务器
	//临时调试用 公开这个日志记录函数
	w.log=_log;
	//下面的变量和函数用户不使用也不必关心

	//枚举状态
	var WSAState={
		Waiting:1,
		Idle:2
	};
	var Funs = {
	    none: 0,
	    login: 1,
	    acw: 2,
	    wca: 3,
	    logoff: 4,
	    call: 5,
	    callin_notify: 6,
	    transfer: 8,
	    listen: 9,
	    stoplisten: 10,
	    answer: 11,
	    onhook: 13,
	    stoptransfer: 14,
	    cancelcall: 15,
	    callend: 16,
	    transfertoivr: 17,
	    queue: 18,
	    holdcallend: 22,
	    hold: 25,
	    unhold: 26,
	    cut: 29,
	    setphonenumber: 31,
	    getcall: 32,
	    intrude: 33,
	    callresult: 34,
	    transferresult: 35,
	    kill: 36,
	    swap: 40,
	    activate: 41,
        mute: 42,
        unmute: 43,
	    stateinfo: 44,
	    transfertoivrandwait: 45,
	    keepcallend: 46,
	    quitqueue: 47,
	    answerqueue: 48,
	    forcedacw: 49,
	    forcedwca: 50,
	    licenseinfo: 53,
		subscribe:54,
		mystateinfo:55,
	    sendmessage: 56,
	    receivemessage: 57,
	    hardwarefail: 58,
	    hookstate: 59,
	    blindtransfer: 61,
	    loginotheragent: 62,
	    misscall: 71,
	    stateinfo2: 72,
		slaveserverfail:73,
		switchtoslaveserver:74,
	    calleealerting: 75,
		getnodestate:82,
		transfernode:83,
		consulttransfer:91,
		completeconsulttransfer:92,
		consulttransfercalleeanswer:93, //咨询转移目标坐席或外线应答
		//坐席发起多方会议相关请求 开始会议 添加会议成员 会议状态查询 解散会议
		conferencestart:94, //参数1个 字符串 会议名称。同一个坐席不可发起多个同名会议；不同的坐席可以发起相同名称的会议
		conferenceappendmember:95, //参数4个 被叫电话号码，是否有发言权
		conferencequery:96, //无参数 返回本坐席发起的所有会议及会议内部成员的状态
		conferencedismiss:97, //解散会议 参数1个 会议名称
		conferencememberstate:98, // 会议成员状态变化通知 1呼叫成功，进入会议 2呼叫失败 3退出会议（挂机）
		conferencekickout:99, //踢出会议成员
		conferencechangememberstate:100001, //改变会议成员发言权
	};
	//私有变量声明开始
	var wsClient=null;
	var wsClientSlave=null; //用于连接热备系统的备用服务器的websocket
	var connected=false;
	var connectedSlave=false; //与热备系统的备用服务器的连接状态标识
	var tickHeartpac = null;//心跳
	//服务端推送来的状态信息
	var allAgcStatejson={};
	//服务端程序的授权信息
	var LicenseJson={'validdate':'0000-00-00','lines':'0','cardno':''}; 
	var heartPacket={};
	
	var logined=false;
	var fsmState=WSAState.Idle; //状态机 idle / wait
	var fsmWaitingwho=Funs.none; //如果正在等待服务端的返回 等待的功能码是多少

	//私有变量声明结束

	//函数
	//初始化全局变量
	function _initTLWSA()
	{
		if(wsClient !=null && connected)
			wsClient.close();
		if(wsClientSlave !=null && connectedSlave)
			wsClientSlave.close();
		
		wsClient=null;
		wsClientSlave=null;
		connected=false;
		connectedSlave=false;
		
		tickHeartpac = null;//心跳
		allAgcStatejson={};
		heartPacket={};
		heartPacket.fun=0;
		
		logined=false;
		fsmState=WSAState.Idle; //状态机 free / wait
		fsmWaitingwho=Funs.none; //状态机子状态 空闲 waitlogin waitlogoff waitacw waitwca waitcall waittransfer wait...
	}
	//连接到服务器
	function _tlaOpen ( serveIP)
	{
		if(connected)
		{
			_log("已经连接到服务器");
			return false;
		}
		var wsUrl='ws://' + serveIP;
		_log(wsUrl);
		if ("WebSocket" in w) //IE  Chrome
		{
			wsClient= new WebSocket(wsUrl);
		} 
		else if ("MozWebSocket" in w)  //FireFox
		{
			wsClient = new MozWebSocket(wsUrl);
		}
		else
		{
			alert("browser not support WebSocket");
			return false;
		}
		wsClient.onopen = function()
		{
			connected=true;
			_log("连接服务器成功");
			_sendauthentication(1);//参数=1 发送到主服务器 2发送到备用服务器 3发送到两个服务器
			fsmState=WSAState.Idle;
			clearInterval(tickHeartpac);
			tickHeartpac = setInterval(function () {
			_send2server(heartPacket);//定时心跳包
				}, 2000);
			if(TLWSA.onOpenreturn)
				TLWSA.onOpenreturn(0);
		}
		
		wsClient.onclose = function()
		{
			connected=false;
			_log("与服务器的连接中断");
			if(!connectedSlave)
			{
				logined=false;
				clearInterval(tickHeartpac);
			}
			if (TLWSA.onClosed)
			    TLWSA.onClosed();
		}
		
		wsClient.onerror = function()
		{
			connected=false;
			if(!connectedSlave)
				logined=false;
			_log("websocket出错了");
			if(TLWSA.onOpenreturn)
				TLWSA.onOpenreturn(1);
		}
		
		wsClient.onmessage = function(receiveMsg){return _onreceivefromserver(receiveMsg.data);}
		return true;
	}
	//连接到双机热备系统的备用服务器
	function _tlaOpenSlaveServer ( slaveServeIP)
	{
		if(connectedSlave)
		{
			_log("已经连接到备用服务器");
			return false;
		}
		if(logined) //本方法必须主调用 tlaopen之后，tlalogin成功之前调用，如果已经登录了，就不允许再调用open函数了
		{
			_log("必须在登录之前调用本函数");
			return false;
		}
		var wsUrl='ws://' + slaveServeIP;
		_log(wsUrl);
		if ("WebSocket" in w) //IE  Chrome
		{
			wsClientSlave= new WebSocket(wsUrl);
		} 
		else if ("MozWebSocket" in w)  //FireFox
		{
			wsClientSlave = new MozWebSocket(wsUrl);
		}
		else
		{
			alert("browser not support WebSocket");
			return false;
		}
		wsClientSlave.onopen = function()
		{
			connectedSlave=true;
			_log("连接备用服务器成功");
			_sendauthentication(2);//参数=1 发送到主服务器 2发送到备用服务器 3发送到两个服务器
			/* 要求在调用tlaopen成功之后立刻调用这个函数 所以下面的设置状态和定时发送心跳都不需要再次设置了
			fsmState=WSAState.Idle;
			tickHeartpac = setInterval(function () {
			_send2server(heartPacket);//定时心跳包
				}, 11000);
				*/
			if(TLWSA.onOpenslaveserverreturn)
				TLWSA.onOpenslaveserverreturn(0);
		}
		
		wsClientSlave.onclose = function()
		{
			connectedSlave=false;
			_log("与备用服务器的连接中断");
			if(!connected) //如果与主服务器的连接也中断了 停止定时任务
			{
				logined=false;
				clearInterval(tickHeartpac);
			}
			if (TLWSA.onSlaveserverclosed)
			    TLWSA.onSlaveserverclosed();
		}
		
		wsClientSlave.onerror = function()
		{
			connectedSlave=false;
			if(!connected) 
			{
				logined=false;
			}
			_log("连接备用服务器的websocket出错了");
			if(TLWSA.onOpenslaveserverreturn)
				TLWSA.onOpenslaveserverreturn(1);
		}
		
		wsClientSlave.onmessage = function(receiveMsg){return _onreceivefromslaveserver(receiveMsg.data);}
		return true;
	}
	//断开与服务器的连接
	function _tlaClose()
	{
		if(wsClient!=null)
		{
			wsClient.onerror=null;
			wsClient.onclose=null;
			wsClient.close();
		}
		wsClient=null;
		connected=false;
		
		if(wsClientSlave!=null)
		{
			wsClientSlave.onerror=null;
			wsClientSlave.onclose=null;
			wsClientSlave.close();
		}
		wsClientSlave=null;
		connectedSlave=false;
		
		logined=false;
		_log('socket to server closed');
	}
	//登录
	function _tlaLogin(wid,gid,lineid,phonenumber)
	{	
		if(!connected && !connectedSlave)
		{
			_log("in _tlaLogin not connected to server yet");
			return false;
		}
		if(logined)
		{
			_log("already login-ed");
			return false;
		}
		
		var jso={};
		jso.fun=Funs.login;
		jso.wid=wid;
		jso.gid=gid;
		jso.line=lineid;
		jso.phonenumber=phonenumber;
		_send2server(jso);
		fsmState=WSAState.Waiting;
		fsmWaitingwho=Funs.login;
		//_log('login');
		return true;
	}
	//注销
	function _tlaLogoff()
	{
		var jso={};
		jso.fun=Funs.logoff;
		
		return commonRequest(jso);
	}
	//离席
	function _tlaAcw()
	{
		var jso={};
		jso.fun=Funs.acw;
		//jso.reason=3;//自定义
		
		return commonRequest(jso);
	}
	//复席
	function _tlaWca()
	{
		var jso={};
		jso.fun=Funs.wca;
		
		return commonRequest(jso);
	}
//静音
	function _tlaMute()
	{
		var jso={};
		jso.fun=Funs.mute;
		
		return commonRequest(jso);
	}
	//取消静音
	function _tlaUnmute()
	{
		var jso={};
		jso.fun=Funs.unmute;
		
		return commonRequest(jso);
	}
	//IP坐席在登录后必须向服务器报告自己所使用的ip电话机的号码
	function _tlaSetphonenumber(phone)
	{
		var jso={};
		jso.fun = Funs.setphonenumber;
		jso.phone=phone;
		
		return commonRequest(jso);
	}
	//呼叫其他坐席
	function _tlaCallagc(calledagcworkid)
	{
		var jso={};
		jso.fun = Funs.call;
		jso.mode=1;
		jso.callee=calledagcworkid;
		
		return commonRequest(jso);
	}
	//呼叫外线
	function _tlaCallout(calledno,callerno,pbxno)
	{
		var jso={};
		jso.fun = Funs.call;
		jso.mode=2;
		jso.callee=calledno;
		jso.caller=callerno;
		jso.co=pbxno;
		
		return commonRequest(jso);
	}
	//取消正在进行的呼叫（坐席或外线）
	function _tlaCancelcall()
	{
		var jso={};
		jso.fun = Funs.cancelcall;
		
		return commonRequest(jso);
	}
	//把正在进行的通话转移给其他坐席
	function _tlaTransfertoagc(destworkid)
	{
		var jso={};
		jso.fun = Funs.transfer;
		jso.mode=1;
		jso.callee=destworkid;
		
		return commonRequest(jso);
	}
	//把正在进行的通话转移到外线
	function _tlaTransferout(calledno,callerno,pbxno)
	{
		var jso={};
		jso.fun = Funs.transfer;
		jso.mode=2;
		jso.callee=calledno;
		jso.caller=callerno;
		jso.co=pbxno;
		
		return commonRequest(jso);
	}
	function _tlaConsulttransfertoagc(destworkid)
	{
		var jso={};
		jso.fun = Funs.consulttransfer;
		jso.mode=1;
		jso.callee=destworkid;
		
		return commonRequest(jso);
	}
	function _tlaConsulttransferout(calledno,callerno,pbxno)
	{
		var jso={};
		jso.fun = Funs.consulttransfer;
		jso.mode=2;
		jso.callee=calledno;
		jso.caller=callerno;
		jso.co=pbxno;
		
		return commonRequest(jso);
	}
	function _tlaCompleteconsulttransfer()
	{
		var jso={};
		jso.fun = Funs.completeconsulttransfer;
		
		return commonRequest(jso);
	}
	
	//取消正在进行的转移
	function _tlaCanceltransfer()
	{
		var jso={};
		jso.fun = Funs.stoptransfer;
		
		return commonRequest(jso);
		
	}
	//盲转到其他坐席 不等待目标坐席应答即认为转移完成
	function _tlaBlindtransfertoagc(destworkid)
	{
		var jso={};
		jso.fun = Funs.blindtransfer;
		jso.mode=1;
		jso.callee=destworkid;
		
		return commonRequest(jso);
		
	}
	//盲转到外线
	function _tlaBlindtransferout(calledno,callerno,pbxno)
	{
		var jso={};
		jso.fun = Funs.blindtransfer;
		jso.mode=2;
		jso.callee=calledno;
		jso.caller=callerno;
		jso.co=pbxno;
		
		return commonRequest(jso);
	}
	//转移到ivr
	function _tlaTransfertoivr(scriptfile,parameter)
	{
		var jso={};
		jso.fun = Funs.transfertoivr;
		jso.file=scriptfile;
		jso.data=parameter;
		
		return commonRequest(jso);
	}
	//转移到ivr并等待
	function _tlaTransfertoivrandwait(scriptfile,parameter)
	{
		var jso={};
		jso.fun = Funs.transfertoivrandwait;
		jso.file=scriptfile;
		jso.data=parameter;
		
		return commonRequest(jso);
	}
	//监听
	function _tlaStartlisten(destwid)
	{
		var jso={};
		jso.fun=Funs.listen;
		jso.wid=destwid;
		
		return commonRequest(jso);
	}
	//停止监听
	function _tlaStoplisten()
	{
		var jso={};
		jso.fun=Funs.stoplisten;
		
		return commonRequest(jso);
	}
	//强制注销工号 本函数不要求必须先登录，所以不能用commonrequest函数，只能把发送请求的代码和改变等待状态的代码写作函数体里
	function _tlaKillworkid(destwid)
	{
		if(!connected)
		{
			_log("not connected to server yet");
			return false;
		}
		var jso={};
		jso.fun = Funs.kill;
		jso.mode=2;
		jso.dest=destwid;
		_send2server(jso);
		fsmState=WSAState.Waiting;
		fsmWaitingwho = Funs.kill;
		//_log('强制注销工号  ' + destwid);
		return true;
	}
	//强拆
	function _tlaForceonhook(destwid)
	{
		var jso={};
		jso.fun = Funs.cut;
		jso.wid=destwid;
		
		return commonRequest(jso);
	}
	//强插
	function _tlaIntrude(destwid)
	{
		var jso={};
		jso.fun = Funs.intrude;
		jso.wid=destwid;
		
		return commonRequest(jso);
	}
	//代接 工号为-1表示代接正在振铃的任意坐席
	function _tlaGetcall(destwid)
	{
		var jso={};
		jso.fun = Funs.getcall;
		jso.wid=destwid;
		
		return commonRequest(jso);
	}
	//强制离席
	function _tlaForceacw(destwid)
	{
		var jso={};
		jso.fun = Funs.kill;
		jso.mode=3;
		jso.dest=destwid;
		
		return commonRequest(jso);
	}
	//强制就绪
	function _tlaForcewca(destwid)
	{
		var jso={};
		jso.fun = Funs.kill;
		jso.mode=4;
		jso.dest=destwid;
		
		return commonRequest(jso);
	}
	//应答
	function _tlaAnswercall()
	{
		var jso={};
		jso.fun = Funs.answer;
		
		return commonRequest(jso);
	}
	//挂机
	function _tlaOnhook()
	{
		var jso={};
		jso.fun = Funs.onhook;
		
		return commonRequest(jso);
	}
	//保持
	function _tlaHoldcall()
	{
		var jso={};
		jso.fun = Funs.hold;
		
		return commonRequest(jso);
	}
	//切换呼叫 在保持了一个呼叫后，开始了新的呼叫，在新呼叫与被保持的呼叫之间切换
	function _tlaSwapcall()
	{
		var jso={};
		jso.fun = Funs.swap;
		
		return commonRequest(jso);
	}
	//激活保持的通话 在保持一个呼叫后，发起新的呼叫，新呼叫建立后，可以激活被保持的通话，与新建立的通话形成3方通话
	function _tlaActivatecall()
	{
		var jso={};
		jso.fun = Funs.activate;
		
		return commonRequest(jso);
	}

	//取回保持的通话 要求当前必须处于空闲状态
	function _tlaUnhold()
	{
		var jso={};
		jso.fun = Funs.unhold;
		
		return commonRequest(jso);
	}
	//登录一个坐席（不是自己登录 是替别人登录） 参数：工号，组号，电话线线号（IP坐席填-1），电话号码（非IP坐席填空字符串）
	function _tlaLoginotheragent(owid,ogid,olid,ophone)
	{
		var jso={};
		jso.fun = Funs.loginotheragent;
		jso.line=olid;
		jso.wid=owid;
		jso.gid=ogid;
		jso.phone=ophone;
		
		return commonRequest(jso);
	}
	//坐席之间发送消息（类似qq聊天）
	function _tlaSendmessagetoagent(mode,destworkidorgroupid,smessage)
	{
		var jso={};
		jso.fun = Funs.sendmessage;
		jso.mode=mode;
		jso.dest=destworkidorgroupid;
		jso.msg=smessage;
		
		return commonRequest(jso);
	}
	//根据电话线号注销坐席 如果自己登录的时候失败，可能是自己使用的电话线被占用了，可以把占用自己电话线的坐席强制踢出去
	//本函数不要求坐席处于登录状态 所以不能调用commonrequest
	function _tlaKillbyline(destlineno)
	{
		if(!connected)
		{
			_log("not connected to server yet");
			return false;
		}
		var jso={};
		jso.fun = Funs.kill;
		jso.mode=1;
		jso.dest=destlineno;
		_send2server(jso);
		fsmState=WSAState.Waiting;
		fsmWaitingwho = Funs.kill;
		//_log('注销线号为 '+destlineno+' 的坐席');
		return true;
	}
	//在有外线进入队列排队的时候，坐席可以收到排队通知，通知里面有外线的呼叫流水号（唯一标识） 可以指定接听这个排队的外线，参数是呼叫流水号
	function _tlaAnswerqueue(callcrs)
	{
		var jso={};
		jso.fun = Funs.answerqueue;
		jso.crs=callcrs;
		
		return commonRequest(jso);
	}
	//挂断外线
	function _tlaOnhookline(destline)
	{
		var jso={};
		jso.fun = Funs.kill;
		jso.mode=5;
		jso.dest=destline;
		
		return commonRequest(jso);
	}

	function _tlaGetagentcount()
	{
		try
		{
			return allAgcStatejson.p.length/2;
		}
		catch(e)
		{
			return 0;
		}
	}
	function _tlaGetagentwid(index)
	{
		var agcount=_tlaGetagentcount();
		if(index>=0 && index<agcount)
			return allAgcStatejson.p[index*2];	
		return '';
	}
	function _tlaGetagentstate(index)
	{
		var agcount=_tlaGetagentcount();
		if(index>=0 && index<agcount)
			return allAgcStatejson.p[index*2+1];
		return 0;
	}
	function _tlaGetacdlength()
	{
		try
		{
			return allAgcStatejson.acdlen;
		}
		catch(e)
		{
			return 0;
		}
	}
	function _tlaGettrknumber()
	{
		try
		{
			return allAgcStatejson.trkno;
		}
		catch(e)
		{
			return 0;
		}
	}

	//查询服务端的授权信息
	function _tlaGetlicenseinfo()
	{
		return LicenseJson;
	}
	//为了降低网络流量压力 cit服务端默认是不向座席端推送状态变化消息的，
	//如果希望收到状态变化消息 必须调用下面的方法订阅
	function _tlaSubscribestateinfo(level)
	{
		var jso={};
		jso.fun = Funs.subscribe;
		jso.level=level;
		
		return commonRequest(jso);
	}
	function _tlaGetnodestate(nodeid,type)
	{
		var jso={};
		jso.fun = Funs.getnodestate;
		jso.nodeid=nodeid;
		jso.type=type;
		
		return commonRequest(jso);
	}
	function _tlaTransfernode(nodeid,wid,co,data)
	{
		var jso={};
		jso.fun = Funs.transfernode;
		jso.nodeid=nodeid;
		jso.wid=wid;
		jso.co=co;
		jso.data=data;
		
		return commonRequest(jso);
	}
	function _tlaConferencestart()
	{
		
		var jso={};
		jso.fun = Funs.conferencestart;
		
		return commonRequest(jso);
	}
	//membertype=1 添加坐席成员 called是坐席工号  caller被忽略  
	//membertype=2 添加外线成员
	function _tlaConferenceappendmember(conferenceid,membertype,called,caller,canspeak)
	{
		var jso={};
		jso.fun = Funs.conferenceappendmember;
		jso.conferenceid=conferenceid;
		jso.membertype=membertype;
		jso.called=called;
		jso.caller=caller;
		jso.canspeak=canspeak;
		
		return commonRequest(jso);
	}
	function _tlaConferencequery(confid)
	{	
		var jso={};
		jso.fun = Funs.conferencequery;
		jso.conferenceid=confid;
		return commonRequest(jso);
	}
	function _tlaConferencedismiss(conferenceid)
	{
		
		var jso={};
		jso.fun = Funs.conferencedismiss;
		jso.conferenceid=conferenceid;
		return commonRequest(jso);
		
	}
	//membertype=1 踢出坐席成员 called是坐席工号    
	//membertype=2 踢出外线成员
	function _tlaConferencekickout(conferenceid,membertype,called)
	{
		
		var jso={};
		jso.fun = Funs.conferencekickout;
		jso.conferenceid=conferenceid;
		jso.membertype=membertype;
		jso.called=called;
		return commonRequest(jso);
		
	}
	//membertype=1 改变坐席成员 called是坐席工号    
	//membertype=2 改变外线成员
	function _tlaConferencechangememberstate(conferenceid,membertype,called,newstate)
	{
		
		var jso={};
		jso.fun = Funs.conferencechangememberstate;
		jso.conferenceid=conferenceid;
		jso.membertype=membertype;
		jso.called=called;
		jso.newstate=newstate;
		return commonRequest(jso);
		
	}
	//在连接到服务器后 必须发送校验密码 通过后才能继续通信 否则服务端会忽略客户端发送的任何信息
	//参数=1 发送到主服务器 =2 发送到备用服务器 =3发送到两个服务器
	function _sendauthentication(towhichserver) 
	{
		var jobj={};
		jobj.fun=999;
		jobj.authcode=authCode;
		_send2server(jobj,towhichserver);
	}

	//向服务器发送请求的通用代码抽出来做一个函数 调用者给一个json对象赋值，然后调用本函数
	//本函数负责判断是否已经登录，向服务器发送请求包，并修改当前状态为等待，以及等待哪个功能码的返回
	function commonRequest(jso)
	{
		if(!logined)
		{
			_log("not logined yet");
			return false;
		}
		
		_send2server(jso);
		fsmState=WSAState.Waiting;
		fsmWaitingwho = jso.fun;
		return true;
	}
	//通过websocket向服务器发送包 默认向主服务器和备用服务器都发送 
	//参数towhichserver=1 发送到主服务器 =2 发送到备用服务器 =3发送到两个服务器
	//如果指定了只发送给备用服务器（例如在连接备用服务器的时候向备用服务器发送校验包,就不需要向主服务器发送）
	function _send2server(jsobject,towhichserver=3)
	{
		if(towhichserver==1 || towhichserver==3)
		{
			if(wsClient!=null && connected)
			{
				var msg=JSON.stringify(jsobject);
				if(jsobject!=heartPacket)
					_log('发送到服务端 '+msg);
				wsClient.send(msg);
			}
		}	
		if(towhichserver==2 || towhichserver==3)
		{
			if(wsClientSlave!=null && connectedSlave)
			{
				var msg=JSON.stringify(jsobject);
				if(jsobject!=heartPacket)
					_log('发送到备用服务器 '+msg);
				wsClientSlave.send(msg);
			}
		}
		
	}
	//添加日志到日志窗口
	function _log(logmsg)
	 {
	 	//console.log("socket>>>log", logmsg);
	 	//return;
		var logwindow=document.getElementById("logwindow");
		if(logwindow!=null)
		{
			if(logwindow.value.length>5000)
				logwindow.value="";
			logwindow.value=logwindow.value+(new Date()).toLocaleString()+" "+logmsg+"\n";
		}
	}
	//当收到来自备用服务器的包
	function _onreceivefromslaveserver(data)
	{
		_onreceivefromserver(data);
	}
	//当websocket收到服务器发来的socket包
	function _onreceivefromserver(data)
	{
		
		var jso=JSON.parse(data);
		if(jso==null)
		{
			_log("recv invalid packet="+data);
			return false;
		}
		
		if(jso.r!=null)
			jso=jso.r;
		if(typeof jso.fun == 'string')
			jso.fun=parseInt(jso.fun);
		//_log('recv from server: '+JSON.stringify(jso));
		switch(jso.fun)
		{
			case Funs.login:
				if(jso.err==0)
					logined=true;
				if(TLWSA.onLoginreturn!=null)
					TLWSA.onLoginreturn(jso.err);
				fsmState = WSAState.Idle;
				break;
			case Funs.acw:
				if(fsmState == WSAState.Waiting && fsmWaitingwho==jso.fun)
				{	if(TLWSA.onAcwreturn!=null)
						TLWSA.onAcwreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				else
				{
					if(TLWSA.onAcw!=null)
						TLWSA.onAcw(0);
				}
				break;
			case Funs.wca:
				if(fsmState == WSAState.Waiting && fsmWaitingwho==jso.fun)
				{
					if(TLWSA.onWcareturn!=null)
						TLWSA.onWcareturn(jso.err);
					fsmState = WSAState.Idle;
				}
				else
				{
					if(TLWSA.onWca)
						TLWSA.onWca(0);
				}
				break;
			case Funs.mute:
				if(fsmState == WSAState.Waiting && fsmWaitingwho==jso.fun)
				{	if(TLWSA.onMutereturn!=null)
						TLWSA.onMutereturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.unmute:
				if(fsmState == WSAState.Waiting && fsmWaitingwho==jso.fun)
				{
					if(TLWSA.onUnmutereturn!=null)
						TLWSA.onUnmutereturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.logoff:
				if(jso.err==0)
					logined=false;
				if(fsmState == WSAState.Waiting && fsmWaitingwho==jso.fun)
				{
					if(TLWSA.onLogoffreturn!=null)
						TLWSA.onLogoffreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				else
				{
					if(TLWSA.onLogoff!=null)
						TLWSA.onLogoff(0);
				}
				break;
			case Funs.call:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onCallreturn != null)
						TLWSA.onCallreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.callin_notify:
				if(TLWSA.onCallin)
					TLWSA.onCallin(jso);
				break;
			case Funs.transfer:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onTransferreturn != null)
						TLWSA.onTransferreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.listen:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onStartlistenreturn != null)
						TLWSA.onStartlistenreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.stoplisten:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onStoplistenreturn != null)
						TLWSA.onStoplistenreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.answer:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onAnswercallreturn != null)
						TLWSA.onAnswercallreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				else {
					if (TLWSA.onAnswer != null)
						TLWSA.onAnswer(0);
				}
				break;
			case Funs.onhook:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onOnhookreturn != null)
						TLWSA.onOnhookreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				else {
					if (TLWSA.onCallend != null)
						TLWSA.onCallend(jso);
				}
				break;
			case Funs.stoptransfer:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onCanceltransferreturn != null)
						TLWSA.onCanceltransferreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.consulttransfer:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onConsulttransferreturn != null)
						TLWSA.onConsulttransferreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.completeconsulttransfer:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onCompleteconsulttransferreturn != null)
						TLWSA.onCompleteconsulttransferreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.cancelcall:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onCancelcallreturn != null)
						TLWSA.onCancelcallreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.callend:
				if(TLWSA.onCallend)
					TLWSA.onCallend(jso);
				break;
			case Funs.transfertoivr:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onTransfertoivrreturn != null)
						TLWSA.onTransfertoivrreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.queue:
				if (TLWSA.onQueue != null)
					TLWSA.onQueue(jso.crs, jso.caller, jso.callee, jso.data);
				break;
			case Funs.holdcallend:
				if (TLWSA.onHoldcallreleased != null)
					TLWSA.onHoldcallreleased(0);
				break;
			case Funs.hold:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onHoldcallreturn != null)
						TLWSA.onHoldcallreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.unhold:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onUnholdcallreturn != null)
						TLWSA.onUnholdcallreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.cut:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onForceonhookreturn != null)
						TLWSA.onForceonhookreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.setphonenumber:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onSetphonenumberreturn != null)
						TLWSA.onSetphonenumberreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.getcall:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onGetcallreturn != null)
						TLWSA.onGetcallreturn(jso.err,jso.caller,jso.callee,jso.data,jso.crs);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.intrude:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onIntrudereturn != null)
						TLWSA.onIntrudereturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.callresult:
				if (TLWSA.onCallresult != null)
					TLWSA.onCallresult(jso);
				break;
			case Funs.transferresult:
				if (TLWSA.onTransferresult != null)
					TLWSA.onTransferresult(jso.err);
				break;
			case Funs.consulttransfercalleeanswer:
				if (TLWSA.onConsulttransfercalleeanswer != null)
					TLWSA.onConsulttransfercalleeanswer(jso.crs);
				break;
			case Funs.kill:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onKillreturn != null)
						TLWSA.onKillreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.swap:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onSwapcallreturn != null)
						TLWSA.onSwapcallreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.activate:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onActivatecallreturn != null)
						TLWSA.onActivatecallreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.stateinfo:
				allAgcStatejson = jso;
				if(TLWSA.onStatechange!=null)
				{
					TLWSA.onStatechange();
				}
				break;
			case Funs.mystateinfo:
				if(TLWSA.onMyStatechange!=null)
				{
					TLWSA.onMyStatechange(jso);
				}
				break;
			case Funs.transfertoivrandwait:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onTransfertoivrreturn != null)
						TLWSA.onTransfertoivrreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.keepcallend:
				if (TLWSA.onKeepcallreleased != null)
					TLWSA.onKeepcallreleased(0);
				break;
			case Funs.quitqueue:
				if (TLWSA.onQuitqueue != null)
					TLWSA.onQuitqueue(jso.crs);
				break;
			case Funs.answerqueue:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onAnswerqueuereturn != null)
						TLWSA.onAnswerqueuereturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.forcedacw:
				if (TLWSA.onAcw != null)
					TLWSA.onAcw(0);
				break;
			case Funs.forcedwca:
				if (TLWSA.onWca != null)
					TLWSA.onWca(0);
				break;
			case Funs.licenseinfo:
				LicenseJson = jso;
				break;
			case Funs.sendmessage:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onSendmessagetoagentreturn != null)
						TLWSA.onSendmessagetoagentreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.receivemessage:
				if (TLWSA.onRecvmsg != null)
					TLWSA.onRecvmsg(jso.msg,jso.from);
				break;
			case Funs.hardwarefail:
				if (TLWSA.onHardwarefail != null)
					TLWSA.onHardwarefail(0);
				break;
			case Funs.hookstate:
				if (TLWSA.onHookstate != null)
					TLWSA.onHookstate(jso);
				break;
			case Funs.blindtransfer:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onBlindtransferreturn != null)
						TLWSA.onBlindtransferreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.getnodestate:
				if(fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if(TLWSA.onGetnodestatereturn !=null)
						TLWSA.onGetnodestatereturn(jso);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.transfernode:
				if(fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if(TLWSA.onTransfernodereturn !=null)
						TLWSA.onTransfernodereturn(jso);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.loginotheragent:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onLoginotheragentreturn != null)
						TLWSA.onLoginotheragentreturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.misscall:
				if (TLWSA.onMisscall != null)
					TLWSA.onMisscall(0);
				break;
			case Funs.stateinfo2:
				
				break;
			case Funs.slaveserverfail:
				if(TLWSA.onSlaveserverfail!=null)
					TLWSA.onSlaveserverfail(jso.err);
				break;
			case Funs.switchtoslaveserver:
				if(TLWSA.onSwitchtoslaveserver!=null)
					TLWSA.onSwitchtoslaveserver(jso.err);
				break;
			case Funs.calleealerting:
				if (TLWSA.onCalleealerting != null)
					TLWSA.onCalleealerting(0);
				break;
			case Funs.subscribe:
				if (fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if (TLWSA.onSubscribestateinforeturn != null)
						TLWSA.onSubscribestateinforeturn(jso.err);
					fsmState = WSAState.Idle;
				}
				break;
			case Funs.conferencestart:
				if(fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if(TLWSA.onConferencestartreturn !=null)
						TLWSA.onConferencestartreturn(jso);
					fsmState = WSAState.Idle;	
				}
				break;
			case Funs.conferenceappendmember:
				if(fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if(TLWSA.onConferenceappendmemberreturn !=null)
						TLWSA.onConferenceappendmemberreturn(jso);
					fsmState = WSAState.Idle;	
				}
				break;
			case Funs.conferencequery:
				if(fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if(TLWSA.onConferencequeryreturn !=null)
						TLWSA.onConferencequeryreturn(jso);
					fsmState = WSAState.Idle;	
				}
				break;
			case Funs.conferencedismiss:
				if(fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if(TLWSA.onConferencedismissreturn !=null)
						TLWSA.onConferencedismissreturn(jso);
					fsmState = WSAState.Idle;	
				}
				break;
			case Funs.conferencekickout:
				if(fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if(TLWSA.onConferencekickoutreturn !=null)
						TLWSA.onConferencekickoutreturn(jso);
					fsmState = WSAState.Idle;	
				}
				break;
			case Funs.conferencechangememberstate:
				if(fsmState == WSAState.Waiting && fsmWaitingwho == jso.fun) {
					if(TLWSA.onConferencechangememberstatereturn !=null)
						TLWSA.onConferencechangememberstatereturn(jso);
					fsmState = WSAState.Idle;	
				}
				break;
			case Funs.conferencememberstate:
				if(TLWSA.onConferencememberstate !=null)
					TLWSA.onConferencememberstate(jso);
				break;
			
		}
		
	}
}(window));