| |
| /* |
| * weinre is available under *either* the terms of the modified BSD license *or* the |
| * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. |
| * |
| * Copyright (c) 2010, 2011 IBM Corporation |
| */ |
| |
| require ./Ex |
| require ./Weinre |
| require ./EventListeners |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| class WebSocketXhr(url) |
| this.initialize(url) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| static |
| WebSocketXhr.CONNECTING = 0 |
| WebSocketXhr.OPEN = 1 |
| WebSocketXhr.CLOSING = 2 |
| WebSocketXhr.CLOSED = 3 |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method initialize(url) |
| this.readyState = WebSocketXhr.CONNECTING |
| this._url = url |
| this._urlChannel = null |
| this._queuedSends = [] |
| this._sendInProgress = true |
| |
| this._listeners = { |
| open: new EventListeners(), |
| message: new EventListeners(), |
| error: new EventListeners(), |
| close: new EventListeners() |
| } |
| |
| this._getChannel() |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method _getChannel |
| this._xhr(this._url, "POST", "", this._handleXhrResponseGetChannel) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method _handleXhrResponseGetChannel(xhr) |
| if (xhr.status != 200) return this._handleXhrResponseError(xhr) |
| |
| try { |
| var object = JSON.parse(xhr.responseText) |
| } |
| catch (e) { |
| this._fireEventListeners("error", {message: "non-JSON response from channel open request"}) |
| this.close() |
| return |
| } |
| |
| if (!object.channel) { |
| this._fireEventListeners("error", {message: "channel open request did not include a channel"}) |
| this.close() |
| return |
| } |
| |
| Weinre.connectorId = object.channel |
| |
| this._urlChannel = this._url + "/" + object.channel |
| this.readyState = WebSocketXhr.OPEN |
| |
| this._fireEventListeners("open", { message: "open" }) |
| |
| this._sendInProgress = false |
| this._sendQueued() |
| |
| this._readLoop() |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method _readLoop |
| if (this.readyState == WebSocketXhr.CLOSED) return |
| if (this.readyState == WebSocketXhr.CLOSING) return |
| |
| this._xhr(this._urlChannel, "GET", "", this._handleXhrResponseGet) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method _handleXhrResponseGet(xhr) |
| var self = this |
| |
| if (xhr.status != 200) return this._handleXhrResponseError(xhr) |
| |
| try { |
| var datum = JSON.parse(xhr.responseText) |
| } |
| catch (e) { |
| this.readyState = WebSocketXhr.CLOSED |
| |
| this._fireEventListeners("error", { |
| message: "non-JSON response from read request" |
| }) |
| return |
| } |
| |
| window.setTimeout(function() {self._readLoop()}, 0) |
| |
| datum.forEach(function(data) { |
| self._fireEventListeners("message", {data: data}) |
| }) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method send(data) |
| if (typeof data != "string") throw new Ex(arguments, this.constructor.name + "." + this.caller) |
| |
| this._queuedSends.push(data) |
| |
| if (this._sendInProgress) return |
| this._sendQueued(); |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method _sendQueued |
| if (this._queuedSends.length == 0) return |
| if (this.readyState == WebSocketXhr.CLOSED) return |
| if (this.readyState == WebSocketXhr.CLOSING) return |
| |
| datum = JSON.stringify(this._queuedSends) |
| this._queuedSends = [] |
| |
| this._sendInProgress = true |
| |
| this._xhr(this._urlChannel, "POST", datum, this._handleXhrResponseSend) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method _handleXhrResponseSend(xhr) |
| var httpSocket = this |
| |
| if (xhr.status != 200) return this._handleXhrResponseError(xhr) |
| |
| this._sendInProgress = false |
| window.setTimeout(function() {httpSocket._sendQueued()}, 0) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method close |
| this._sendInProgress = true |
| this.readyState = WebSocketXhr.CLOSING |
| this._fireEventListeners("close", { |
| message: "closing", |
| wasClean: true |
| }) |
| this.readyState = WebSocketXhr.CLOSED |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method addEventListener(type, listener, useCapture) |
| this._getListeners(type).add(listener, useCapture) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method removeEventListener(type, listener, useCapture) |
| this._getListeners(type).remove(listener, useCapture) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method _fireEventListeners(type, event) |
| if (this.readyState == WebSocketXhr.CLOSED) return |
| |
| event.target = this |
| this._getListeners(type).fire(event) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method _getListeners(type) |
| var listeners = this._listeners[type] |
| if (null == listeners) throw new Ex(arguments, "invalid event listener type: '" + type + "'") |
| |
| return listeners |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method _handleXhrResponseError(xhr) |
| if (xhr.status == 404) { |
| this.close() |
| return |
| } |
| |
| this._fireEventListeners("error", { |
| target: this, |
| status: xhr.status, |
| message: "error from XHR invocation: " + xhr.statusText |
| }) |
| |
| console.log("error from XHR invocation: " + xhr.status + ": " + xhr.statusText) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| method _xhr(url, method, data, handler) |
| if (null == handler) throw new Ex(arguments, "handler must not be null") |
| |
| var xhr = new XMLHttpRequest() |
| |
| xhr.httpSocket = this |
| xhr.httpSocketHandler = handler |
| xhr.onreadystatechange = _xhrEventHandler |
| |
| xhr.open(method, url, true) |
| xhr.setRequestHeader("Content-Type", "text/plain") |
| xhr.send(data) |
| |
| //----------------------------------------------------------------------------- |
| // |
| //----------------------------------------------------------------------------- |
| function _xhrEventHandler(event) |
| var xhr = event.target |
| if (xhr.readyState != 4) return |
| |
| xhr.httpSocketHandler.call(xhr.httpSocket, xhr) |