/*
 * 呼叫中心电话工具条
 * author: easycallcenter365@126.com
 * 2025-04-08
 * 修改：增强断开清理，优化重连状态，修复isCalling死锁
*/

// import $ from './jquery-1.11.0.js';
import $ from 'jquery';

function _phoneBarObserver() {
  if (!(this instanceof _phoneBarObserver)) {
    return new _phoneBarObserver();
  }
  this.listeners = {};
}
_phoneBarObserver.prototype = {
  on: function(key, callback) { this.addListener(key, callback); },
  off: function(key, callback) { this.removeListener(key, callback); },
  addListener: function(key, callback) {
    if (!this.listeners[key]) this.listeners[key] = [];
    this.listeners[key].push(callback);
  },
  removeListener: function(key, callback) {
    if (this.listeners[key]) {
      if (callback) {
        const index = this.listeners[key].indexOf(callback);
        if (index > -1) this.listeners[key].splice(index, 1);
      } else {
        this.listeners[key] = [];
      }
    }
  },
  notifyAll: function(key, info) {
    var keys = [key];
    if (key != null && String(key) !== key) keys.push(String(key));
    keys.forEach(function(keyName) {
      if (keyName == null || !this.listeners[keyName]) return;
      for (var i = 0; i < this.listeners[keyName].length; i++) {
        if (typeof this.listeners[keyName][i] === 'function') {
          this.listeners[keyName][i](info);
        }
      }
    }, this);
  },
  make: function(o) {
    for (var i in this) {
      o[i] = this[i];
      o.listeners = {};
    }
  }
};

"use strict";
function ccPhoneBarSocket() {
  var observer = new _phoneBarObserver();
  observer.make(this);
  var _cc = this;
  var ws = null;
  var wsuri = null;
  var _connectPending = false;
  var _sendQueue = [];
  var _kickedByOtherDevice = false;
  var _everConnected = false;
  var _reconnectSeq = 0;
  var _reconnectAttemptAt = 0;
  var _reconnect201RetryCount = 0;
  var _staleCloseTimer = null;
  var MAX_201_RETRY = 5;
  var CONNECT_RACE_WINDOW_MS = 12000;
  var STALE_CLOSE_DELAY_MS = 400;
  var RECOVERING_CALL_CLEAR_MS = 5000;
  var RETRY_201_BASE_MS = 400;
  var RETRY_201_STEP_MS = 300;
  var isConnected = false;
  var callConnected = false;
  var canSendVideoReInvite = false;
  this.subscribeAgentListStarted = false;
  this.iframe = null;
  this.isCalling = false;   // 防止重复外呼
  this._intentionalDisconnect = false;

  this.resetOutboundLock = function() {
    this.isCalling = false;
  };

  this._activeCallUuid = null;
  this._recoveringCall = false;
  this._suppressSessionNotFound = false;

  this.setActiveCallUuid = function(uuid) {
    if (uuid) this._activeCallUuid = String(uuid);
  };
  this.getActiveCallUuid = function() {
    return this._activeCallUuid;
  };
  this.clearActiveCallUuid = function() {
    this._activeCallUuid = null;
    this._recoveringCall = false;
  };
  this._captureCallUuid = function(msg) {
    if (msg && msg.object && msg.object.uuid) {
      this.setActiveCallUuid(msg.object.uuid);
    }
  };
  this.restoreCallSession = function() {
    if (!this._activeCallUuid) return;
    if (!ws || ws.readyState !== WebSocket.OPEN) return;
    this._recoveringCall = true;
    callConnected = true;
  };

  this.callConfig = {
    'useDefaultUi': false,
    'ipccServer': 'sip.ylrzcloud.com',
    'enableWss': true,
    'callCodec': 'pcma',
    'enableHeartBeat': true,
    'heartBeatIntervalSecs': 16,
    'useSameAudioCodeForOutbound': true,
    'loginToken': '',
    'gatewayList': [{
      uuid: '01',
      updateTime: 1675953810492,
      gatewayAddr: '113.207.68.30:5543;external',
      callerNumber: '02386990208',
      calleePrefix: '',
      priority: 1,
      concurrency: 1000,
      register: false,
      audioCodec: 'g711'
    }],
    'gatewayEncrypted': false,
    'fsServer': '129.28.164.235:5060',
    'groupId': '1',
    'groups': null,
    'agentList': null,
    'phoneType': 'EyeBeam',
    'webPhoneUrl': 'None',
    'localHostProxyVersion': 'v20221130_1736',
    'localHostProxyPort': 8888
  };

  this.insertAfter = function(newElement, targetElement) {
    var parent = targetElement.parentNode;
    if (parent.lastChild === targetElement) parent.appendChild(newElement);
    else parent.insertBefore(newElement, targetElement.nextSibling);
  };
  this.trim = function(str) { return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); };
  this.getIsConnected = function() { return isConnected; };
  this.getCallConnected = function() { return callConnected; };
  this.setCallConnected = function(value) { callConnected = value; };
  this.setCanSendVideoReInvite = function(value) { canSendVideoReInvite = value; };
  this.getCanSendVideoReInvite = function() { return canSendVideoReInvite; };
  this.heartbeatTimer = null;

  // 与 phone-bar-ex.html 一致：status 200 / 重连后重置心跳定时器
  this.setHeartbeat = function() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
      this.heartbeatTimer = null;
    }
    if (!_cc.callConfig.enableHeartBeat) return;
    var intervalSec = _cc.callConfig.heartBeatIntervalSecs || 16;
    this.heartbeatTimer = setInterval(function() {
      if (!_cc.callConfig.enableHeartBeat) return;
      if (!_cc.getIsConnected()) return;
      if (!ws || ws.readyState !== WebSocket.OPEN) {
        console.warn('[heartbeat] WebSocket未连接，停止心跳');
        if (_cc.heartbeatTimer) {
          clearInterval(_cc.heartbeatTimer);
          _cc.heartbeatTimer = null;
        }
        return;
      }
      console.debug('[heartbeat] 发送心跳');
      try {
        _cc.sendMsg({ action: 'setHearBeat', body: '{}' });
      } catch (e) {
        console.error('[heartbeat] 发送心跳失败:', e);
        if (_cc.heartbeatTimer) {
          clearInterval(_cc.heartbeatTimer);
          _cc.heartbeatTimer = null;
        }
      }
    }, intervalSec * 1000);
  };

  this.loadScript = function(destUrl, callbackFunc) {
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = destUrl;
    if (null != callbackFunc) script.onload = function() { callbackFunc(); };
    document.getElementsByTagName('head')[0].appendChild(script);
  };

  this.initConfig = function(config) {
    for (var element in config) this.callConfig[element] = config[element];
    var _loginToken = this.callConfig["loginToken"];
    if (typeof(_loginToken) == "undefined" || _loginToken === "") {
      alert("电话工具条：无法获取 loginToken!");
      return;
    }
    if (_cc.callConfig.enableWss) {
      wsuri = 'wss://' + this.callConfig.ipccServer +
        '/call-center/websocketServer?' +
        '&loginToken=' + this.callConfig.loginToken;
    } else {
      wsuri = 'ws://129.28.164.235:1081/call-center/websocketServer?' +
        '&loginToken=' + this.callConfig.loginToken;
    }
    var ipccServerIpAddr = this.callConfig.ipccServer.split(":");
    if (this.callConfig.enableWss && this.checkIP(ipccServerIpAddr)) {
      var tipsError = "ERROR! 启用了wss之后，必须使用域名访问websocketServer! " + this.callConfig.ipccServer;
      alert(tipsError);
    }
    if (this.callConfig.enableHeartBeat) this.setHeartbeat();
    if (_cc.callConfig["useDefaultUi"]) this.initPhoneBarUI();
  };

  this.checkIP = function(ip) {
    var re = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/;
    return re.test(ip);
  };

  this.disconnect = function() {
    if (callConnected) {
      console.warn('[disconnect] 通话进行中，拒绝断开 IPPC 连接');
      return;
    }
    _cc._performDisconnect();
  };

  /** 强制断开（清理/切线路时使用，忽略通话态） */
  this.forceDisconnect = function() {
    callConnected = false;
    _cc._activeCallUuid = null;
    _cc._recoveringCall = false;
    _cc._performDisconnect();
  };

  this._performDisconnect = function() {
    this._intentionalDisconnect = true;
    if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; }
    if (!ws || ws.readyState !== WebSocket.OPEN) {
      if (ws) {
        ws.onmessage = null;
        ws.onclose = null;
        ws.onopen = null;
        ws.onerror = null;
        ws.close();
        ws = null;
      }
      isConnected = false;
      _connectPending = false;
      this.isCalling = false;
      return;
    }
    try {
      ws.onmessage = null;
      ws.onclose = null;
      ws.onopen = null;
      ws.onerror = null;
      var cmdInfo = {};
      cmdInfo.action = "setAgentStatus";
      cmdInfo.body = { "cmd": "disconnect", "args": { "msg": "disconnection opt triggered by js client." } };
      _cc.sendMsg(cmdInfo);
    } catch(e) { console.error('[disconnect] 发送断开指令失败:', e); }
    try { ws.close(); } catch(e) { console.error('[disconnect] 关闭WebSocket失败:', e); }
    ws = null;
    isConnected = false;
    _connectPending = false;
    this.isCalling = false;
  };

  /** 断开并等待 WebSocket 关闭，避免同 token 新连接与旧会话冲突导致 201 */
  this.disconnectAndWait = function(timeoutMs) {
    timeoutMs = timeoutMs || 2000;
    var _ccRef = this;
    return new Promise(function(resolve) {
      if (callConnected) {
        console.warn('[disconnectAndWait] 通话进行中，跳过断开');
        resolve(false);
        return;
      }
      var finished = false;
      function done() {
        if (finished) return;
        finished = true;
        _ccRef._intentionalDisconnect = true;
        _connectPending = false;
        isConnected = false;
        _ccRef.isCalling = false;
        resolve(true);
      }
      var timer = setTimeout(done, timeoutMs);
      if (_ccRef.heartbeatTimer) {
        clearInterval(_ccRef.heartbeatTimer);
        _ccRef.heartbeatTimer = null;
      }
      if (!ws) {
        clearTimeout(timer);
        done();
        return;
      }
      var closing = ws;
      var state = closing.readyState;
      if (state === WebSocket.CLOSED) {
        ws = null;
        clearTimeout(timer);
        done();
        return;
      }
      try {
        closing.onmessage = null;
        closing.onopen = null;
        closing.onerror = null;
        closing.onclose = function() {
          if (ws === closing) ws = null;
          clearTimeout(timer);
          done();
        };
        if (state === WebSocket.OPEN) {
          try {
            closing.send(JSON.stringify({
              action: "setAgentStatus",
              body: { cmd: "disconnect", args: { msg: "disconnection opt triggered by js client." } }
            }));
          } catch (e) {}
        }
        closing.close();
      } catch (e) {
        if (ws === closing) ws = null;
        clearTimeout(timer);
        done();
      }
    });
  };

  this.getExtNum = function() { return this.callConfig.extnum; };
  this.getOpNum = function() { return this.callConfig.opnum; };
  this.getGroupId = function() { return this.callConfig.groupId; };
  this.getGroups = function() { return JSON.parse(JSON.stringify(this.callConfig.groups)); };
  this.findAgentByOpNum = function(opnum) {
    if (_cc.callConfig.agentList == null) return null;
    for (var key in _cc.callConfig.agentList) {
      let item = _cc.callConfig.agentList[key];
      if (item["opnum"] === opnum) {
        item["_arrayIndex"] = key;
        return item;
      }
    }
    return null;
  };
  this.updateAgentList = function(agentList) {
    for (var key in agentList) {
      let item = agentList[key];
      var existItem = this.findAgentByOpNum(item["opnum"]);
      if (existItem != null) {
        var newStatus = item["agentStatus"];
        var oldStatus = existItem["agentStatus"];
        var logoutTime = item["logoutTime"];
        if (logoutTime > 0) {
          _cc.callConfig.agentList.splice(existItem["_arrayIndex"], 1);
          continue;
        }
        if (newStatus != oldStatus) existItem["agentStatus"] = newStatus;
      } else {
        if (_cc.callConfig.agentList != null) _cc.callConfig.agentList.push(item);
      }
    }
  };

  function _isCurrentSocket(socket) {
    return ws === socket;
  }

  function _safeSendOnSocket(socket, jsonObject) {
    if (_kickedByOtherDevice) {
      console.warn('[sendMsg] 账号已在其他设备登录，拒绝发送');
      return false;
    }
    if (!socket) return false;
    var state = socket.readyState;
    if (state === WebSocket.OPEN) {
      try {
        socket.send(JSON.stringify(jsonObject));
        return true;
      } catch (e) {
        console.error('[sendMsg] 发送失败:', e);
        return false;
      }
    }
    if (state === WebSocket.CONNECTING && socket === ws) {
      _sendQueue.push(jsonObject);
      return true;
    }
    console.warn('[sendMsg] WebSocket 未就绪, readyState=', state);
    return false;
  }

  function _flushSendQueue(socket) {
    if (!_isCurrentSocket(socket) || socket.readyState !== WebSocket.OPEN) return;
    while (_sendQueue.length) {
      try {
        socket.send(JSON.stringify(_sendQueue.shift()));
      } catch (e) {
        console.error('[sendMsg] 刷新发送队列失败:', e);
        break;
      }
    }
  }

  this.resetKickedState = function() {
    _kickedByOtherDevice = false;
    _reconnect201RetryCount = 0;
  };

  this._handleKickedByOtherDevice = function(msg, sourceSocket) {
    if (sourceSocket && ws && ws !== sourceSocket) {
      if (isConnected && ws.readyState === WebSocket.OPEN) {
        console.warn('[IPCC] 已有有效连接，忽略过期连接的201');
        try {
          sourceSocket.onmessage = null;
          sourceSocket.onclose = null;
          sourceSocket.close();
        } catch (e) {}
        return;
      }
    }
    if (isConnected && ws && ws.readyState === WebSocket.OPEN && (!sourceSocket || sourceSocket === ws)) {
      console.error('[IPCC] 当前连接收到201，确认被踢下线');
    }
    console.error('[IPCC] 用户已在其他设备登录(201)，停止重连');
    _kickedByOtherDevice = true;
    _connectPending = false;
    _cc._intentionalDisconnect = true;
    isConnected = false;
    _sendQueue = [];
    if (_cc.heartbeatTimer) { clearInterval(_cc.heartbeatTimer); _cc.heartbeatTimer = null; }
    _cc.notifyAll(ccPhoneBarSocket.eventList.user_login_on_other_device, msg);
    var closing = sourceSocket || ws;
    if (closing) {
      try {
        closing.onmessage = null;
        closing.onclose = null;
        closing.onopen = null;
        closing.onerror = null;
        closing.close();
      } catch (e) {}
      if (ws === closing) ws = null;
    }
  };

  function _scheduleOpenAfterStale(mySeq, delayMs) {
    if (_staleCloseTimer) clearTimeout(_staleCloseTimer);
    _staleCloseTimer = setTimeout(function() {
      _staleCloseTimer = null;
      if (mySeq !== _reconnectSeq || _kickedByOtherDevice) {
        _connectPending = false;
        return;
      }
      _createWebSocketConnection();
    }, delayMs);
  }

  this.connect = function() {
    if (_kickedByOtherDevice) {
      console.warn('[connect] 已在其他设备登录，停止重连');
      return;
    }
    if (ws && ws.readyState === WebSocket.OPEN) {
      if (callConnected) {
        return;
      }
      _connectPending = false;
      return;
    }
    if (ws && ws.readyState === WebSocket.CONNECTING) {
      return;
    }
    if (_connectPending) {
      return;
    }
    _connectPending = true;
    _reconnectAttemptAt = Date.now();
    _reconnectSeq++;
    var mySeq = _reconnectSeq;
    this._intentionalDisconnect = false;

    if (ws) {
      var stale = ws;
      isConnected = false;
      _sendQueue = [];
      try {
        stale.onmessage = null;
        stale.onopen = null;
        stale.onerror = null;
        stale.onclose = function(evt) {
          if (ws === stale) ws = null;
          if (mySeq !== _reconnectSeq) {
            _connectPending = false;
            return;
          }
          _scheduleOpenAfterStale(mySeq, STALE_CLOSE_DELAY_MS);
        };
        if (stale.readyState === WebSocket.OPEN || stale.readyState === WebSocket.CONNECTING) {
          stale.close();
        } else if (ws === stale) {
          ws = null;
          _scheduleOpenAfterStale(mySeq, 400);
        }
      } catch (e) {
        console.error('[connect] 关闭旧连接失败:', e);
        if (ws === stale) ws = null;
        _scheduleOpenAfterStale(mySeq, STALE_CLOSE_DELAY_MS);
      }
      return;
    }
    _scheduleOpenAfterStale(mySeq, 100);
  };

  this.reconnect = function() {
    this.connect();
  };

  function _createWebSocketConnection() {
    if (!('WebSocket' in window)) {
      _connectPending = false;
      return;
    }
    var socket = new WebSocket(wsuri);
    ws = socket;
    _sendQueue = [];
    socket.onmessage = function(evt) {
      if (!_isCurrentSocket(socket)) return;
      var msg = JSON.parse(evt.data);
      var resp_status = msg["status"];
      switch (resp_status) {
        case 200:
          _everConnected = true;
          _connectPending = false;
          _reconnect201RetryCount = 0;
          isConnected = true;
          _cc.callConfig.extnum = msg.object["extnum"];
          _cc.callConfig.opnum = msg.object["opnum"];
          _cc.callConfig.groupId = msg.object["groupId"];
          _cc.callConfig.groups = msg.object["groups"];
          _cc.notifyAll(ccPhoneBarSocket.eventList.ws_connected, msg);
          if (callConnected || _cc._activeCallUuid) {
            _cc.restoreCallSession();
            setTimeout(function() {
              if (_cc.getIsConnected()) _cc._recoveringCall = false;
            }, RECOVERING_CALL_CLEAR_MS);
          } else {
            _safeSendOnSocket(socket, {
              action: "setAgentStatus",
              body: { cmd: "setStatus", args: { status: ccPhoneBarSocket.agentStatusEnum.busy } }
            });
          }
          _cc.setHeartbeat();
          break;
        default:
          if (parseInt(resp_status) === 201) {
            if (isConnected && ws && ws !== socket && ws.readyState === WebSocket.OPEN) {
              console.warn('[IPCC] 有效连接已建立，忽略迟到的201');
              try { socket.close(); } catch (e) {}
              return;
            }
            var isConnectRace = (Date.now() - _reconnectAttemptAt < CONNECT_RACE_WINDOW_MS) &&
              _reconnect201RetryCount < MAX_201_RETRY;
            if (isConnectRace && _isCurrentSocket(socket)) {
              _reconnect201RetryCount++;
              console.warn('[IPCC] 连接竞态201，第', _reconnect201RetryCount, '次自动重试');
              _connectPending = false;
              isConnected = false;
              try {
                socket.onmessage = null;
                socket.onclose = null;
                socket.onopen = null;
                socket.onerror = null;
                socket.close();
              } catch (e) {}
              if (ws === socket) ws = null;
              setTimeout(function() {
                if (!_kickedByOtherDevice && !(isConnected && ws && ws.readyState === WebSocket.OPEN)) {
                  _cc.connect();
                }
              }, RETRY_201_BASE_MS + _reconnect201RetryCount * RETRY_201_STEP_MS);
              return;
            }
            _cc._handleKickedByOtherDevice(msg, socket);
            return;
          }
          _cc._captureCallUuid(msg);
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.asr_result_generate)) {
            _cc._recoveringCall = false;
          }
          var hangupCode = parseInt(resp_status);
          var isHangupEvent = hangupCode === parseInt(ccPhoneBarSocket.eventList.caller_hangup) ||
            hangupCode === parseInt(ccPhoneBarSocket.eventList.callee_hangup) ||
            hangupCode === parseInt(ccPhoneBarSocket.eventList.CONFERENCE_MODERATOR_HANGUP);
          if (isHangupEvent) {
            _cc.notifyAll(resp_status, msg);
          }
          if (parseInt(resp_status) === 400) {
            var errText = (msg["msg"] || "").toString();
            if (errText.indexOf("method not support") >= 0) {
              console.debug('[SDK] 服务端不支持的方法，已忽略:', errText);
              break;
            }
            if (errText.indexOf("通话不存在") >= 0 || errText.indexOf("不存在") >= 0) {
              if (_cc._suppressSessionNotFound || !_cc.getCallConnected()) {
                console.debug('[SDK] 挂机后会话已不存在，已忽略:', errText);
              } else {
                console.warn('[SDK] 服务端无通话会话:', errText);
                _cc.notifyAll('call_session_not_found', msg);
              }
            } else {
              _cc.isCalling = false;
            }
          } else if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.server_error)) {
            _cc.isCalling = false;
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.caller_hangup) ||
            parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.CONFERENCE_MODERATOR_HANGUP)) {
            _cc.setCallConnected(false);
            _cc.setCanSendVideoReInvite(false);
            _cc.callConfig.agentList = null;
            _cc.unSubscribeAgentList();
            _cc.isCalling = false;
            _cc.clearActiveCallUuid();
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.caller_answered)) {
            // 外呼：600=分机已接通，客户仍在振铃；桥接期需标记通话活跃，避免误判挂机
            _cc.setCallConnected(true);
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.callee_hangup)) {
            _cc.isCalling = false;
            _cc.setCallConnected(false);
            _cc.clearActiveCallUuid();
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.extension_off_line)) {
            _cc.isCalling = false;
            if (_cc.getCallConnected()) {
              _cc.setCallConnected(false);
              _cc.clearActiveCallUuid();
            }
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.callee_answered)) {
            _cc.setCallConnected(true);
            _cc._recoveringCall = false;
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.OUTBOUND_START)) {
            _cc.setCallConnected(true);
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.callee_busy)) {
            console.warn("callee_busy or rejected");
            _cc.isCalling = false;
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.new_inbound_call)) {
            _cc.setCallConnected(true);
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.caller_answered) ||
            parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.callee_answered)) {
            if (msg["object"]["callType"] === "audio") {
              _cc.setCanSendVideoReInvite(true);
              _cc.notifyAll(ccPhoneBarSocket.eventList.on_audio_call_connected, "audio call connected.");
            }
            if (msg["object"]["callType"] === "video") {
              _cc.setCanSendVideoReInvite(false);
              _cc.notifyAll(ccPhoneBarSocket.eventList.on_video_call_connected, "video call connected.");
            }
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_hold)) _cc.changeUiOnHold();
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_unhold)) _cc.changeUiOnUnHold();
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_on_hold_hangup)) {
            _cc.changeUiOnUnHold();
            $("#holdBtn").removeClass('on');
            $("#callStatus").text("保持的通话已挂机.");
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_call_wait)) {
            $("#stopCallWait").show();
            $("#doConsultationBtn").hide();
            $("#callStatus").text("客户电话等待中.");
            _cc.showTransferAreaUI();
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_off_call_wait)) {
            $("#stopCallWait").hide();
            $("#transferCallWait").hide();
            _cc.hideTransferAreaUI();
            $("#callStatus").text("等待的电话已接回.");
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.inner_consultation_start)) {
            $("#callStatus").text("咨询已开始.");
            $("#transferCallWait").show();
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.inner_consultation_stop)) {
            $("#callStatus").text("咨询已结束.");
            $("#transferCallWait").hide();
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_on_call_wait_hangup)) {
            $("#stopCallWait").hide();
            $("#transferCallWait").hide();
            $("#callStatus").text("等待的客户已挂机.");
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.inner_consultation_start)) {
            $("#holdBtn").removeClass('on');
            $("#hangUpBtn").removeClass('on');
            $("#transferBtn").removeClass('on');
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.transfer_call_success)) {
            var extNum = msg["object"]["callee"];
            if (extNum === _cc.getExtNum()) {
              $("#holdBtn").addClass('on');
              $("#hangUpBtn").addClass('on');
              $("#transferBtn").addClass('on');
            }
          }
          if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.agent_status_data_changed)) {
            if (_cc.subscribeAgentListStarted) {
              if (_cc.callConfig.agentList == null) _cc.callConfig.agentList = JSON.parse(msg.object);
              else _cc.updateAgentList(JSON.parse(msg.object));
            }
          }
          if (!isHangupEvent) {
            _cc.notifyAll(resp_status, msg);
          }
          break;
      }
    };
    socket.onclose = function(evt) {
      if (!_isCurrentSocket(socket)) {
        return;
      }
      const intentional = _cc._intentionalDisconnect || _kickedByOtherDevice;
      _cc._intentionalDisconnect = false;
      _connectPending = false;
      isConnected = false;
      _sendQueue = [];
      if (_cc.heartbeatTimer) { clearInterval(_cc.heartbeatTimer); _cc.heartbeatTimer = null; }
      ws = null;

      if (_kickedByOtherDevice) {
        return;
      }

      if (!callConnected && !_cc._activeCallUuid) {
        _cc.isCalling = false;
      }
      if (intentional || (evt.wasClean && evt.code === 1000)) {
      } else {
        console.warn('[onclose] 异常断开，通知前端');
        _cc.notifyAll(ccPhoneBarSocket.eventList.ws_disconnected, "ipccserver 连接断开.");
      }
    };
    socket.onerror = function(evt) {
      if (!_isCurrentSocket(socket)) return;
      console.warn('[onerror] IPCC WebSocket 错误', evt);
    };
    socket.onopen = function(evt) {
      if (!_isCurrentSocket(socket)) return;
      _flushSendQueue(socket);
    };
  }

  this.sendMsg = function(jsonObject) {
    console.debug("ws.send:", jsonObject);
    return _safeSendOnSocket(ws, jsonObject);
  };
  this.changeUiOnHold = function() { $("#holdBtnLi").hide(); $("#unHoldBtnLi").show(); $("#unHoldBtn").addClass('on'); };
  this.changeUiOnUnHold = function() { $("#holdBtnLi").show(); $("#holdBtn").addClass('on'); $("#unHoldBtnLi").hide(); };
  this.hideTransferAreaUI = function() { var transferArea = document.getElementById("transfer_area"); if (transferArea) transferArea.style.display = "none"; };
  this.showTransferAreaUI = function() { var transferArea = document.getElementById("transfer_area"); if (transferArea) transferArea.style.display = "block"; };
  this.setStatusFree = function() { this.setStatus(ccPhoneBarSocket.agentStatusEnum.free); };
  this.setStatusBusy = function() { this.setStatus(ccPhoneBarSocket.agentStatusEnum.busy); };

  ccPhoneBarSocket.utils = {
    getQueryParam: function(paramName) {
      const url = window.location.href;
      const params = new URLSearchParams(new URL(url).search);
      return params.get(paramName);
    }
  };

  ccPhoneBarSocket.agentStatusEnum = {
    "justLogin": 1, "free": 2, "busy": 3, "busy_rest": 31,
    "busy_meeting": 32, "busy_training": 33, "incall": 4, "fill_form": 5, "conference": 6
  };
  ccPhoneBarSocket.videoLevels = {
    "Smooth": { "levelId": "42e00b", "description": "流畅" },
    "Smooth2": { "levelId": "42e00c", "description": "流畅+" },
    "Smooth3": { "levelId": "42e00d", "description": "流畅++" },
    "Clear": { "levelId": "42e014", "description": "清晰" },
    "Clear2": { "levelId": "42e015", "description": "清晰+" },
    "Clear3": { "levelId": "42e016", "description": "清晰++" },
    "HD": { "levelId": "42e01e", "description": "高清" },
    "HD2": { "levelId": "42e01f", "description": "高清+" }
  };
  ccPhoneBarSocket.eventList = {
    "on_audio_call_connected": "100", "on_video_call_connected": "101", "ws_connected": "200",
    "user_login_on_other_device": "201", "ws_disconnected": "202", "call_session_status_data_changed": "203",
    "agent_status_data_changed": "205", "request_args_error": "400", "unauthorized": "401",
    "server_error": "500", "server_error_audio_codec_not_match": "501", "caller_answered": "600",
    "caller_hangup": "601", "caller_busy": "602", "caller_not_login": "603", "caller_respond_timeout": "604",
    "callee_answered": "605", "callee_hangup": "606", "callee_ringing": "607", "status_changed": "608",
    "callee_busy": "609", "outbound_finished": "611", "PREDICTIVE_CALL_INBOUND": "612",
    "new_inbound_call": "613", "acd_group_queue_number": "615", "transfer_call_recv": 616,
    "lock_agent_fail": 617, "transfer_call_success": 618, "asr_result_generate": 619,
    "asr_process_end_agent": 620, "asr_process_end_customer": 621, "asr_process_started": 622,
    "customer_channel_hold": 623, "customer_channel_unhold": 624, "customer_on_hold_hangup": 625,
    "inner_consultation_request": 626, "customer_channel_call_wait": 627, "customer_channel_off_call_wait": 628,
    "customer_on_call_wait_hangup": 629, "extension_on_line": 630, "extension_off_line": 631,
    "inner_consultation_start": 632, "inner_consultation_stop": 633, "conference_repeat_callee": 660,
    "CONFERENCE_CALL_MODERATOR_TIMEOUT": 661, "CONFERENCE_MEMBER_ANSWERED": 662,
    "CONFERENCE_MEMBER_HANGUP": 663, "CONFERENCE_MEMBER_MUTED_SUCCESS": 666,
    "CONFERENCE_MEMBER_MUTED_FAILED": 665, "CONFERENCE_MEMBER_UNMUTED_SUCCESS": 667,
    "CONFERENCE_MEMBER_UNMUTED_FAILED": 668, "CONFERENCE_MEMBER_NOT_EXISTS": "669",
    "CONFERENCE_MODERATOR_RESET": "670", "CONFERENCE_MODERATOR_ANSWERED": "671",
    "CONFERENCE_MODERATOR_HANGUP": "672", "CONFERENCE_MEMBER_VMUTED_SUCCESS": "674",
    "CONFERENCE_MEMBER_UnVMUTED_SUCCESS": "676", "CONFERENCE_MEMBER_UnVMUTED_FAILED": "677",
    "CONFERENCE_TRANSFER_SUCCESS_FROM_EXISTED_CALL": "678", "OUTBOUND_START": "679"
  };

  this.createIframe = function(src) {
    var _iframe = document.createElement("iframe");
    _iframe.style.width = '0';
    _iframe.style.height = '0';
    _iframe.style.margin = '0';
    _iframe.style.padding = '0';
    _iframe.style.overflow = 'hidden';
    _iframe.style.border = 'none';
    _iframe.src = src;
    document.body.appendChild(_iframe);
    _cc.iframe = _iframe;
  };
  this.openSoftPhone = function() {
    var softPhoneUrl = "http://127.0.0.1:" + _cc.callConfig["localHostProxyPort"] +
      "/autoSetExtension?server=" + encodeURIComponent(_cc.callConfig["fsServer"].split(':')[0]) +
      "&port=" + _cc.callConfig["fsServer"].split(':')[1] +
      "&extnum=" + _cc.callConfig["extnum"] +
      "&pass=" + encodeURIComponent(_cc.callConfig["extPassword"]) +
      "&phoneType=" + _cc.callConfig["phoneType"] +
      "&version=" + encodeURIComponent(_cc.callConfig["localHostProxyVersion"]) +
      "&webPhoneUrl=" + encodeURIComponent(_cc.callConfig["webPhoneUrl"]);
    _cc.iframe.src = softPhoneUrl;
  };

  this.setStatus = function(status) {
    this.sendMsg({
      action: "setAgentStatus",
      body: { cmd: "setStatus", args: { status: status } }
    });
  };
  this.logOff = function() {
    this.sendMsg({
      action: "setAgentStatus",
      body: { cmd: "disconnect", args: { cause: "disconnect request from js client." } }
    });
  };
  this.stopCallWaitBtnClickUI = function() {
    this.sendMsg({
      action: "callWait",
      body: { cmd: "stop", args: {} }
    });
  };
  this.transferCallWaitBtnClickUI = function() { this.callControl("transferCallWait", {}); };
  this.consultationBtnClickUI = function() {
    let transferType = "outer";
    let phoneNumber = $("#externalPhoneNumber").val().trim();
    if (phoneNumber === "") {
      transferType = "inner";
      var groupId = $("#transfer_to_groupIds").val();
      if ($.trim(groupId) == "") { alert("请选择业务组!"); $("#transfer_to_groupIds").focus(); return; }
      var member = $("#transfer_to_member").val();
      if ($.trim(member) == "") { alert("请选择要咨询的坐席成员!"); $("#transfer_to_member").focus(); return; }
      var selectText = $('#transfer_to_member option:selected').text();
      if (selectText.indexOf("空闲") == -1) { alert("请选择空闲的坐席成员!"); $("#transfer_to_member").focus(); return; }
      if (member == this.getOpNum()) { alert("不能咨询自己，请选择其他坐席成员!"); return; }
      phoneNumber = member;
    }
    this.callControl("consultation", { "to": phoneNumber, "transferType": transferType });
  };
  this.transferBtnClickUI = function() {
    let transferType = "outer";
    let phoneNumber = $("#externalPhoneNumber").val().trim();
    if (phoneNumber === "") {
      transferType = "inner";
      var groupId = $("#transfer_to_groupIds").val();
      if ($.trim(groupId) === "") { alert("请选择转接的业务组!"); $("#transfer_to_groupIds").focus(); return; }
      var member = $("#transfer_to_member").val();
      if ($.trim(member) === "") { alert("请选择转接的坐席成员!"); $("#transfer_to_member").focus(); return; }
      var selectText = $('#transfer_to_member option:selected').text();
      if (selectText.indexOf("空闲") === -1) { alert("请选择空闲的坐席成员!"); $("#transfer_to_member").focus(); return; }
      if (member === this.getOpNum()) { alert("不能转给自己，请选择其他坐席成员!"); return; }
      phoneNumber = member;
    }
    this.transferCall(phoneNumber, transferType);
  };
  this.transferCall = function(userCodeOrPhone, transferType) {
    if (transferType === "inner") {
      if (userCodeOrPhone !== this.getOpNum()) this.callControl("transferCall", { "to": userCodeOrPhone, "transferType": "inner" });
      else console.error("cant not transfer call to yourself.");
    } else {
      this.callControl("transferCall", { "to": userCodeOrPhone, "transferType": "outer" });
    }
  };
  this.hangup = function() {
    var args = {};
    if (_cc._activeCallUuid) args.uuid = _cc._activeCallUuid;
    _cc._suppressSessionNotFound = true;
    setTimeout(function() { _cc._suppressSessionNotFound = false; }, 4000);
    var sent = this.callControl("endSession", args);
    this.isCalling = false;
    if (sent !== false) {
      setTimeout(function() {
        _cc.setCallConnected(false);
        _cc.clearActiveCallUuid();
      }, 200);
    } else {
      _cc.setCallConnected(false);
      _cc.clearActiveCallUuid();
    }
  };
  this.callControl = function(action, argsObject) {
    if (!ws || ws.readyState !== WebSocket.OPEN) {
      console.warn('[callControl] WebSocket 未连接, cmd=', action);
      return false;
    }
    var sessionControl = {};
    sessionControl.action = "call";
    sessionControl.body = { "cmd": action, "args": argsObject || {} };
    return this.sendMsg(sessionControl);
  };
  this.checkCallConfirmed = function() {
    if (!_cc.getIsConnected()) {  return false; }
    if (!_cc.getCallConnected()) {  return false; }
    return true;
  };
  this.sendVideoFile = function(mp4FilePath) {
    if (!_cc.checkCallConfirmed()) return false;
    if (typeof(mp4FilePath) == "undefined" || mp4FilePath.trim().length === 0) {  return false; }
    this.callControl("playMp4File", { "mp4FilePath": mp4FilePath });
    return true;
  };
  this.reInviteVideoCall = function() {
    if (!_cc.checkCallConfirmed()) return false;
    if (!_cc.getCanSendVideoReInvite()) {  return false; }
    this.callControl("reInviteVideo", {});
    return true;
  };
  this.call = function(phoneNumber, callType, videoLevel) {
    if (this.isCalling) {
      console.warn('[SDK] 正在呼叫中，请勿重复操作 (isCalling=true)');
      return false;
    }
    if (typeof(videoLevel) == "undefined" || videoLevel.trim().length === 0) {
      videoLevel = ccPhoneBarSocket.videoLevels.HD.levelId;
    }
    if (typeof(callType) == "undefined" || callType.trim().length === 0) {
      callType = "audio";
    }
    if (phoneNumber == null || phoneNumber.length === 0) {  return false; }
    if (phoneNumber.trim().length < 3) { alert('请输入正确格式的外呼号码！'); return false; }
    if (!_cc.getIsConnected()) {  return false; }
    let outboundInfo = {
      "gatewayList": _cc.callConfig.gatewayList,
      "destPhone": phoneNumber,
      "gatewayEncrypted": _cc.callConfig.gatewayEncrypted,
      "useSameAudioCodeForOutbound": _cc.callConfig.useSameAudioCodeForOutbound,
      "callType": callType,
      "videoLevel": videoLevel
    };
    this.callControl("startSession", outboundInfo);
    this.isCalling = true;
    return true;
  };
  this.callEx = function(phoneNumber) {
    if (phoneNumber == null || phoneNumber.length === 0) {  return; }
    if (!_cc.getIsConnected()) {
      _cc.connect();
      _cc.on(ccPhoneBarSocket.eventList.ws_connected, function() {
        _cc.off(ccPhoneBarSocket.eventList.ws_connected);
        _cc.call(phoneNumber);
      });
      return;
    }
    _cc.call(phoneNumber);
  };

  // 以下为UI工具条相关方法（仅当 useDefaultUi=true 时使用，不影响核心功能）
  ccPhoneBarSocket.findItemByCode = function(code) {
    for (var item in ccPhoneBarSocket.eventListWithTextInfo) {
      if (ccPhoneBarSocket.eventListWithTextInfo[item].code === code) return ccPhoneBarSocket.eventListWithTextInfo[item];
    }
  };
  ccPhoneBarSocket.eventListWithTextInfo = {
    "ws_connected": { "code": 200, msg: "已签入", btn_text: [{ id: "#onLineBtn", name: "签出" }], enabled_btn: ['#setFree', '#callBtn', '#onLineBtn', '#consultationBtn'] },
    "ws_disconnected": { "code": 202, msg: "服务器连接断开", btn_text: [{ id: "#onLineBtn", name: "签入" }], enabled_btn: ['#onLineBtn'] },
    "user_login_on_other_device": { "code": 201, msg: "用户已在其他设备登录", btn_text: [{ id: "#onLineBtn", name: "签入" }], enabled_btn: ['#onLineBtn'] },
    "request_args_error": { "code": 400, msg: "客户端请求参数错误", btn_text: [], enabled_btn: [] },
    "server_error": { "code": 500, msg: "服务器内部错误", btn_text: [], enabled_btn: [] },
    "caller_answered": { "code": 600, msg: "分机已接通", btn_text: [], enabled_btn: ['#resetStatus', '#hangUpBtn', '#transferBtn', '#holdBtn', '#consultationBtn'] },
    "caller_hangup": { "code": 601, msg: "分机已挂断", btn_text: [], enabled_btn: ['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn'] },
    "caller_busy": { "code": 602, msg: "分机忙,上一通电话未挂断", btn_text: [], enabled_btn: ['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn'] },
    "caller_not_login": { "code": 603, msg: "分机未登录，请检查", btn_text: [], enabled_btn: ['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn'] },
    "caller_respond_timeout": { "code": 604, msg: "分机未应答超时，请重新打开分机", btn_text: [], enabled_btn: ['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn'] },
    "callee_answered": { "code": 605, msg: "被叫已接通", btn_text: [], enabled_btn: ['#resetStatus', '#hangUpBtn', '#transferBtn', '#holdBtn', '#consultationBtn'] },
    "callee_hangup": { "code": 606, msg: "通话结束", btn_text: [], enabled_btn: ['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn'] },
    "callee_ringing": { "code": 607, msg: "被叫振铃中", btn_text: [], enabled_btn: ['#resetStatus', '#hangUpBtn', '#transferBtn', '#consultationBtn'] },
    "status_changed": { "code": 608, msg: "状态已改变", btn_text: [], enabled_btn: [] },
    "free": { "code": 0, msg: "空闲中", btn_text: [], enabled_btn: ['#setBusy', '#onLineBtn', '#consultationBtn'] },
    "busy": { "code": 1, msg: "忙碌", btn_text: [], enabled_btn: ['#setFree', '#onLineBtn', '#callBtn', '#consultationBtn'] },
    "customer_channel_hold": { "code": 623, msg: "通话已保持.", btn_text: [], enabled_btn: ['#setFree', '#callBtn', '#unHoldBtn', '#consultationBtn'] },
    "customer_channel_unhold": { "code": 624, msg: "通话已接回.", btn_text: [], enabled_btn: ['#hangUpBtn', '#holdBtn'] }
  };
  ccPhoneBarSocket.phone_buttons = ['#setFree', '#setBusy', '#callBtn', '#hangUpBtn', '#resetStatus', '#onLineBtn', '#transferBtn', '#holdBtn', '#unHoldBtn', '#consultationBtn'];
  this.updatePhoneBar = function(msg, status_key) {
    if (!_cc.callConfig.useDefaultUi) {  return; }
    if (msg) $("#callStatus").text(msg.msg);
    var status_info = ccPhoneBarSocket.findItemByCode(status_key);
    if (!status_info) return;
    if (status_info.code === ccPhoneBarSocket.eventListWithTextInfo.status_changed.code) {
      if (msg.object.status === ccPhoneBarSocket.agentStatusEnum.free) status_info = ccPhoneBarSocket.eventListWithTextInfo.free;
      else status_info = ccPhoneBarSocket.eventListWithTextInfo.busy;
    }
    var msgSet = status_info.msg;
    if (msgSet && msgSet.length > 0) $("#callStatus").text(msgSet);
    var btn_text = status_info.btn_text;
    var enabled_btn = status_info.enabled_btn;
    if (btn_text) $.each(btn_text, function(i, d) { $(d.id).next().text(d.name); });
    if (enabled_btn.length === 0) return;
    var all_btn = ccPhoneBarSocket.phone_buttons;
    for (var i in all_btn) {
      if ($.inArray(all_btn[i], enabled_btn) < 0) $(all_btn[i]).removeClass('on');
      else $(enabled_btn[$.inArray(all_btn[i], enabled_btn)]).addClass('on');
    }
  };
  this.initPhoneBarUI = function() {
    window.onbeforeunload = function() { if (!confirm('关闭网页将导致您无法接听电话，确定要关闭吗 ?')) return false; };
    $("#unHoldBtnLi").hide();
    if (!_cc.callConfig.useDefaultUi) {  return; }
    $('#conferenceBtn').on('click', function() {
      if (!_cc.getIsConnected()) {  return; }
      var confObjId = document.getElementById("conference_area");
      if (confObjId.style.display === "block") confObjId.style.display = "none";
      else confObjId.style.display = "block";
    });
    $('#callBtn').on('click', function() {
      if ($(this).hasClass('on')) {
        var destPhone = $.trim($("#ccphoneNumber").val());
        var videoLevel = document.getElementById("videoLevelSelect").value;
        var callType = document.forms[0].callType.value;
        _cc.call(destPhone, callType, videoLevel);
      }
    });
    $('#setFree').on('click', function() { if ($(this).hasClass('on')) _cc.setStatus(ccPhoneBarSocket.agentStatusEnum.free); });
    $('#setBusy').on('click', function() { if ($(this).hasClass('on')) _cc.setStatus(ccPhoneBarSocket.agentStatusEnum.busy); });
    $('#setBusySubList').on('change', function() { let itemValue = $('#setBusySubList').val(); _cc.setStatus(itemValue); });
    $('#hangUpBtn').on('click', function() { if ($(this).hasClass('on')) _cc.hangup(); });
    $('#holdBtn').on('click', function() { if ($(this).hasClass('on')) _cc.holdCall(); });
    $('#unHoldBtn').on('click', function() { if ($(this).hasClass('on')) _cc.unHoldCall(); });
    $("#doTransferBtn").hide();
    function populateGroupIdOptions() {
      var groups = _cc.callConfig.groups;
      if (groups) {
        var selectObj = document.getElementById("transfer_to_groupIds");
        if (selectObj && selectObj.tagName.toLowerCase() === 'select') {
          var firstOption = selectObj.options[0];
          selectObj.innerHTML = '';
          selectObj.add(firstOption);
          for (var i = 0; i < groups.length; i++) {
            var group = groups[i];
            var option = document.createElement("option");
            option.value = group.groupId;
            option.text = group.bizGroupName;
            selectObj.add(option);
          }
          if (groups.length > 0 && selectObj.options.length > 1) selectObj.selectedIndex = 1;
        }
      }
    }
    $('#transferBtn').on('click', function() {
      if ($(this).hasClass('on')) {
        if (!_cc.getIsConnected()) {  return; }
        var transferArea = document.getElementById("transfer_area");
        if (transferArea.style.display === "block") {
          transferArea.style.display = "none";
          _cc.unSubscribeAgentList();
          $("#doTransferBtn").hide();
          $("#doConsultationBtn").hide();
        } else {
          transferArea.style.display = "block";
          populateGroupIdOptions();
          $("#doTransferBtn").show();
          $("#doConsultationBtn").hide();
        }
      }
    });
    $("#stopCallWait").hide();
    $("#transferCallWait").hide();
    $("#doConsultationBtn").hide();
    $('#consultationBtn').on('click', function() {
      if ($(this).hasClass('on')) {
        if (!_cc.getIsConnected()) {  return; }
        var transferArea = document.getElementById("transfer_area");
        if (transferArea.style.display === "block") {
          transferArea.style.display = "none";
          _cc.unSubscribeAgentList();
          $("#doConsultationBtn").hide();
          $("#doTransferBtn").hide();
        } else {
          transferArea.style.display = "block";
          populateGroupIdOptions();
          $("#doConsultationBtn").show();
          $("#doTransferBtn").hide();
        }
      }
    });
    $('#onLineBtn').on('click', function() {
      if ($(this).hasClass('on')) {
        if (_cc.getIsConnected()) _cc.disconnect();
        else _cc.connect();
      } else alert('当前不允许签出!');
    });
    $('#resetStatus').on('click', function() { window.onbeforeunload = null; location.reload(); });
    $("#ccphoneNumber").keydown(function(e) {
      if (e.which === 13) {
        var destPhone = $.trim($("#ccphoneNumber").val());
        var videoLevel = document.getElementById("videoLevelSelect").value;
        var callType = document.forms[0].callType.value;
        _cc.call(destPhone, callType, videoLevel);
        return false;
      }
    });
    $(document).keyup(function(e) {
      if (e.which === 27) {
        if (_cc.getIsConnected()) {
          if (_cc.callConfig["useDefaultUi"]) { if ($('#hangUpBtn').hasClass('on')) _cc.hangup(); }
          else _cc.hangup();
        }
      }
    });
  };
  this.holdCall = function() {
    this.sendMsg({ action: "callHold", body: { cmd: "hold", args: {} } });
  };
  this.unHoldCall = function() {
    this.sendMsg({ action: "callHold", body: { cmd: "unhold", args: {} } });
  };
  this.subscribeAgentList = function() {
    this.sendMsg({ action: "pollAgentList", body: { cmd: "subscribe", args: {} } });
    _cc.subscribeAgentListStarted = true;
  };
  this.unSubscribeAgentList = function() {
    if (_cc.subscribeAgentListStarted) {
      _cc.subscribeAgentListStarted = false;
      _cc.callConfig.agentList = null;
      this.sendMsg({ action: "pollAgentList", body: { cmd: "unSubscribe", args: {} } });
    }
  };
  // 会议相关方法（保持不变）
  this.conferenceStartBtnUI = function(customerName) {
    var callType = document.getElementById("conf_call_type").value;
    var confTemplate = document.getElementById("conf_template").value;
    var layOut = document.getElementById("conf_layout").value;
    _cc.setStatusBusy();
    $("#callBtn").removeClass('on');
    $("#setFree").removeClass('on');
    $("#onLineBtn").removeClass('on');
    document.getElementById("startConference").setAttribute("disabled", "true");
    if (_cc.getCallConnected()) {
      let tips = "是否把当前通话转换为会议 ?";
      if (confirm(tips)) _cc.transferToConference(layOut, confTemplate, callType, customerName);
      else document.getElementById("startConference").removeAttribute("disabled");
    } else _cc.conferenceStart(layOut, confTemplate, callType);
  };
  this.conferenceAddMemberBtnUI = function(reInvite, memberPhoneParam, memberNameParam) {
    var memberName = "", memberPhone = "", memberCallType = $.trim(document.getElementById("member_call_type").value), memberVideoLevel = $.trim(document.getElementById("member_video_level").value);
    if (reInvite === 0) {
      memberName = $("#member_name").val();
      memberPhone = $("#member_phone").val();
      if (memberName.length === 0 || $.trim(memberName) === "") { alert('请填写参会者姓名!'); return; }
      if (memberPhone.length === 0 || $.trim(memberPhone) === "") { alert('请填写参会者手机号!'); return; }
      memberName = $.trim(memberName);
      memberPhone = $.trim(memberPhone);
      var templateObj = document.getElementById("conf_member_template");
      if (document.getElementById("conf_member_" + memberPhone) != null) { alert('会议成员已经存在，请不要重复添加!'); return; }
      var memberHtmlItem = templateObj.innerHTML;
      memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_name}", "gm"), memberName);
      memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_phone}", "gm"), memberPhone);
      memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_status}", "gm"), "即将呼叫");
      var li = document.createElement("li");
      li.setAttribute("id", "conf_member_" + memberPhone);
      li.setAttribute("class", "conf_member_item_row");
      li.innerHTML = memberHtmlItem;
      _cc.insertAfter(li, templateObj);
      $("#member_name").val('');
      $("#member_phone").val('');
    } else { memberName = memberNameParam; memberPhone = memberPhoneParam; }
    var memberItemId = "#conf_member_" + memberPhone;
    $(".conf_mute", $(memberItemId)).find("img").hide();
    $(".conf_vmute", $(memberItemId)).find("img").hide();
    $(".conf_re_invite", $(memberItemId)).hide();
    $(".conf_status", $(memberItemId)).html("即将呼叫");
    _cc.conferenceAddMembers([{ "name": memberName, "phone": memberPhone, "callType": memberCallType, "videoLevel": memberVideoLevel }]);
  };
  this.conferenceAddMemberFromExistCall = function(memberName, memberPhone) {
    if (memberName.length === 0 || $.trim(memberName) === "") { alert('请填写参会者姓名!'); return; }
    if (memberPhone.length === 0 || $.trim(memberPhone) === "") { alert('请填写参会者手机号!'); return; }
    memberName = $.trim(memberName);
    memberPhone = $.trim(memberPhone);
    var templateObj = document.getElementById("conf_member_template");
    if (document.getElementById("conf_member_" + memberPhone) != null) { alert('会议成员已经存在，请不要重复添加!'); return; }
    var memberHtmlItem = templateObj.innerHTML;
    memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_name}", "gm"), memberName);
    memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_phone}", "gm"), memberPhone);
    memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_status}", "gm"), "即将呼叫");
    var li = document.createElement("li");
    li.setAttribute("id", "conf_member_" + memberPhone);
    li.setAttribute("class", "conf_member_item_row");
    li.innerHTML = memberHtmlItem;
    _cc.insertAfter(li, templateObj);
    var memberItemId = "#conf_member_" + memberPhone;
    $(".conf_mute", $(memberItemId)).find("img").show();
    $(".conf_vmute", $(memberItemId)).find("img").show();
    $(".conf_re_invite", $(memberItemId)).hide();
    $(".conf_status", $(memberItemId)).html("通话中").css("color", "green");
  };
  this.transferToConference = function(layOut, confTemplate, callType, customerName) {
    _cc.callControl("transferToConference", { "layOut": layOut, "callType": callType, "confTemplate": confTemplate, "customerName": customerName });
  };
  this.conferenceStart = function(layOut, confTemplate, callType) {
    this.sendMsg({
      action: "conference",
      body: { method: "startconf", args: { layOut: layOut, callType: callType, confTemplate: confTemplate } }
    });
  };
  this.conferenceVMuteMember = function(members) { this.conferenceControl("vmute", typeof members === "string" ? [{ "phone": members }] : members); };
  this.conferenceUnVMuteMember = function(members) { this.conferenceControl("unvmute", typeof members === "string" ? [{ "phone": members }] : members); };
  this.conferenceMuteMember = function(members) { this.conferenceControl("mute", typeof members === "string" ? [{ "phone": members }] : members); };
  this.conferenceUnMuteMember = function(members) { this.conferenceControl("unmute", typeof members === "string" ? [{ "phone": members }] : members); };
  this.conferenceAddMembers = function(members) { this.conferenceControl("add", members); };
  this.conferenceRemoveMembers = function(members) {
    if (typeof members === "string") {
      var memberItemObj = $("#conf_member_" + members);
      if ($(".conf_status", memberItemObj).text() === "通话中") this.conferenceControl("remove", [{ "phone": members }]);
      memberItemObj.remove();
    } else this.conferenceControl("remove", members);
  };
  this.conferenceEnd = function() { this.conferenceControl("endconf", []); };
  this.conferenceControl = function(action, phoneList) {
    this.sendMsg({
      action: "conference",
      body: { method: action, memberList: phoneList }
    });
  };
  this.callMonitorDataPull = function() {
    this.sendMsg({ action: "monitorData", body: {} });
  };
  this.inboundCallQueuePull = function() {
    this.sendMsg({ action: "inboundMonitorData", body: {} });
  };
  this.monitorControl = function(action, argsObject) {
    this.sendMsg({
      action: "callMonitor",
      body: { cmd: action, args: argsObject }
    });
  };
  this.callMonitorStart = function(callId) {
    if (callId == null || callId.length === 0) {  return; }
    if (!_cc.getIsConnected()) {  return; }
    this.monitorControl("startMonitoring", { 'callSpyId': callId });
  };
  this.callMonitorEnd = function() { if (!_cc.getIsConnected()) {  return; } this.monitorControl("endMonitoring", {}); };
}

export default ccPhoneBarSocket;
