socket.js 4.83 KB
;(function (root, factory) {
  var ws = factory(root);
  if (typeof define === 'function' && define.amd) {
    define([], factory);
  } else if (typeof module !== 'undefined' && module.exports) {
    module.exports = factory();
  } else {
    root.ws = factory();
  }
}(this, function(root, undefined) {
  if (!'WebSocket' in window) return

  var _handlers = {}, wsocket,
    eventTarget = document.createElement('div'),
    settings = {
      // 是否重连
      autoConnect: true,
      // 自动重连延迟重连速度
      reConnectDelay: 1.5
    },
    func = function() {},
    // 对外泄露Api
    _api = {
      CONNECT: WebSocket.CONNECTING,
      OPEN: WebSocket.OPEN,
      CLOSING: WebSocket.CLOSING,
      CLOSED: WebSocket.CLOSED
    };

  /**
   * ws
   * @param {string} url 
   * @param {*} protocols 
   * @param {object} options 
   */
  function ws(url, protocols, options) {
    var self = this;
    // url
    this.url = url;
    // 状态
    this.readyState = WebSocket.CONNECTING;

    /**
     * http://tools.ietf.org/html/rfc6455
     * 服务器选择的子协定, 这是建立 WebSocket 对象时 protocols 参数里的其中一个字符串
     */
    this.protocols = protocols || null;

    // 绑定选项定义设置
    if (!options) options = {}
    for (var key in settings) {
      this[key] = typeof options[key] !== 'undefined'
        ? options[key]
        : settings[key];
    }

    // 公开 api
    for (var a in _api) this[a] = _api[a];

    // 事件处理程序
    eventTarget.addEventListener('open', function(event) { self !== window && self.onopen(event); });
    eventTarget.addEventListener('close', function(event) { self !== window && self.onclose(event); });
    eventTarget.addEventListener('connecting', function(event) { self !== window && self.onconnecting(event); });
    eventTarget.addEventListener('message', function(event) { self !== window && self.onmessage(event); });
    eventTarget.addEventListener('error', function(event) { self !== window && self.onerror(event); });

    // 公开事件目的 API
    this.addEventListener = eventTarget.addEventListener.bind(eventTarget);
    this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
    this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);

    if (this.autoConnect === true && this !== window) this.open();
    return this
  }

  /**
   * 产生一个事件, 与标准兼容, 兼容的浏览器和IE9~IE11?
   * http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563
   * https://msdn.microsoft.com/library/ff975299(v=vs.85).aspx
   * @param {string} eventName 
   * @param {*} args 
   */
  function generateEvent(eventName, args) {
    var evt = document.createEvent('CustomEvent');
    evt.initCustomEvent(eventName, false, false, args);
    return evt;
  }

  ws.prototype.onconnecting = func;
  ws.prototype.onopen = func;
  ws.prototype.onerror = func;
  ws.prototype.onmessage = func;

  /**
   * send
   * @param {*} data
   */
  ws.prototype.send = function(data) {
    if (this.wsocket) {
      this.wsocket.send(data);
    } else {
      throw 'INVAILD_STATE_ERR : Pausing to reconnect WebSocket';
    }
  }

  /**
   * close
   * 错误代码参考:https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
   * @param {*} code
   * @param {*} reason
   */
  ws.prototype.close = function(code, reason) {
    // 默认CLOSE_NORMAL 代码
    typeof code === 'undefined' && (code = 1000);
    this.wsocket && this.wsocket.close(code, reason)
  }

  /**
   * open
   */
  ws.prototype.open = function() {
    var self = this;
    if ("WebSocket" in window) {
      wsocket = new WebSocket(this.url, this.protocols || []);
    } else if ("MozWebSocket" in window) {
      wsocket = new MozWebSocket(this.url, this.protocols || []);
    } else {
      wsocket = new SockJS(this.url, this.protocols || []);
    }
    // wsocket = new WebSocket(this.url, this.protocols || []);
    eventTarget.dispatchEvent(generateEvent('connecting'));
    wsocket.onopen = function(event) {
      self.protocols = ws.protocols;
      self.readyState = WebSocket.OPEN;

      var e = generateEvent('open');
      eventTarget.dispatchEvent(e);
    }
    wsocket.onclose = function(event) {
      self.readyState = WebSocket.CLOSED;
      var e = generateEvent('connecting');
      e.code = event.code;
      e.reason = event.reason;
      e.wasClean = event.wasClean;
      eventTarget.dispatchEvent(e);

      eventTarget.dispatchEvent(generateEvent('close'));
    }
    wsocket.onmessage = function(event) {
      var e = generateEvent('message');
      e.data = event.data;
      eventTarget.dispatchEvent(e);
    }
    wsocket.onerror = function(event) {
      var e = generateEvent('error');
      eventTarget.dispatchEvent(e);
    }
    this.wsocket = wsocket;
    return this;
  }
  return ws;
}));