@cross-border-bridge/data-channel
Version:
DataChannel for Web application
180 lines (179 loc) • 6.5 kB
JavaScript
// Copyright © 2017 DWANGO Co., Ltd.
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
/**
* データ種別コード
*/
var DATA_TYPE_PUSH = 1;
var DATA_TYPE_REQUEST = 2;
var DATA_TYPE_RESPONSE = 3;
var DATA_TYPE_ERROR = 4;
/**
* DataChannel内部エラー
*/
var DataChannelError = (function (_super) {
__extends(DataChannelError, _super);
function DataChannelError() {
_super.apply(this, arguments);
}
return DataChannelError;
})(Error);
exports.DataChannelError = DataChannelError;
/**
* DataBus上で単純なリクエスト&レスポンス機構を提供する。
*/
var DataChannel = (function () {
function DataChannel(dataBus) {
this._handler = undefined;
this._handlers = [];
this._tagCount = 0;
this._waitingCallbacks = {};
this._timeoutObjects = {};
this._dataBus = dataBus;
}
/**
* DataChannelを破棄する際に実行する。
* このメソッドを実行するとすべてのハンドラが解放され、レスポンス待ちの処理についてはエラーが返る。
*/
DataChannel.prototype.destroy = function () {
var _this = this;
if (!this._dataBus)
return;
this.unregister();
this._dataBus = undefined;
this._handlers = undefined;
Object.keys(this._waitingCallbacks).forEach(function (tag) {
var error = new Error("plugin channel destroyed.");
error.type = "Closed";
_this.processCallback(tag, error);
});
this._waitingCallbacks = undefined;
};
/**
* このDataChannelが既に破棄されたかどうかを返す
*
* @return 破棄されていればtrue、されていなければfalse
*/
DataChannel.prototype.destroyed = function () {
return !this._dataBus;
};
/**
* メッセージハンドラの登録を行う
*
* @param handler メッセージ受信時に実行されるハンドラ
* @return ハンドラID
*/
DataChannel.prototype.addHandler = function (handler) {
if (!Object.keys(this._handlers).length) {
this.register();
}
if (this._handlers.indexOf(handler) === -1) {
this._handlers.push(handler);
}
};
/**
* メッセージハンドラの解除を行う
*
* @param handlerId ハンドラ登録時に取得したハンドラID
*/
DataChannel.prototype.removeHandler = function (handler) {
this._handlers = this._handlers.filter(function (h) { return h !== handler; });
if (!Object.keys(this._handlers).length) {
this.unregister();
}
};
/**
* 登録されているすべてのメッセージハンドラの解除を行う
*/
DataChannel.prototype.removeAllHandlers = function () {
if (!Object.keys(this._handlers).length)
return;
this._handlers = [];
this.unregister();
};
/**
* メッセージの送信を行う。
*
* @param packet メッセージの実データ。フォーマットによって内容は自由に定義できる
* @param callback メッセージに対してのレスポンスを受け取るコールバック。任意指定
* @param timeout レスポンスを待つ待機時間。待機時間を過ぎるとcallbackにtimeoutエラーが返る。未指定時はタイムアウトしない。
*/
DataChannel.prototype.send = function (packet, callback, timeout) {
var _this = this;
if (timeout === void 0) { timeout = 0; }
if (callback) {
this.register();
var tag = this.acquireTag();
if (0 < timeout) {
var timeoutObject = setTimeout(function () {
var error = new Error("send timeout.");
error.type = "Timeout";
_this.processCallback(tag, error);
}, timeout);
this._timeoutObjects[tag] = timeoutObject;
}
this._waitingCallbacks[tag] = callback;
this._dataBus.send(DATA_TYPE_REQUEST, [tag, packet]);
}
else {
this._dataBus.send(DATA_TYPE_PUSH, [packet]);
}
};
DataChannel.prototype.register = function () {
var _this = this;
if (this._handler)
return;
this._handler = function (dataType, data) {
switch (dataType) {
case DATA_TYPE_ERROR: {
var error = new DataChannelError();
error.type = data[1];
_this.processCallback(data[0], error);
return;
}
case DATA_TYPE_RESPONSE:
_this.processCallback(data[0], null, data[1]);
return;
case DATA_TYPE_PUSH:
_this._handlers.forEach(function (handler) {
handler(data[0]);
});
return;
case DATA_TYPE_REQUEST: {
var responseCallback = function (rpacket) {
_this._dataBus.send(DATA_TYPE_RESPONSE, [data[0], rpacket]);
};
_this._handlers.forEach(function (handler) {
handler(data[1], responseCallback);
});
return;
}
}
};
this._dataBus.addHandler(this._handler);
};
DataChannel.prototype.unregister = function () {
if (!this._handler)
return;
this._dataBus.removeHandler(this._handler);
this._handler = undefined;
};
DataChannel.prototype.processCallback = function (targetTag, error, packet) {
var callback = this._waitingCallbacks[targetTag];
if (callback) {
delete this._waitingCallbacks[targetTag];
delete this._timeoutObjects[targetTag];
callback(error, packet);
return true;
}
return false;
};
DataChannel.prototype.acquireTag = function () {
return "c:" + ++this._tagCount;
};
return DataChannel;
})();
exports.DataChannel = DataChannel;