Home Reference Source

src/datapipe/DataChannelDataPipe.js

import {bindMethods} from '../utils/bindMethods';

import {DataPipe} from './DataPipe';
import {DataPipeStatus} from './DataPipeStatus';
import {DataPipeType} from './DataPipeType';

export class DataChannelDataPipe extends DataPipe {

	constructor(id, type, stack, participants, label) {
		super(id, type, stack, participants, label);
		if (participants.length !== 2) {
			throw new TypeError('participants parameter must have 2 elements');
		}
		bindMethods(this, ['onStatus', 'onQsCalling', 'onQsEstablished', 'onQsEndCall', 'onQsData']);
		this.rcv = 0;
		this.handleCalling = this.type === DataPipeType.OUTGOING;
		this.makeBindings();
	}

	_connectOutgoing() {
		this.status = DataPipeStatus.CONNECTING;
		this.stack.call(this.participants[1], {data: true});
		return this.when(DataPipeStatus.CONNECTED);
	}

	_connectIncoming() {
		this.status = DataPipeStatus.CONNECTING;
		this.stack.answer(this.internalId, {data: true});
		return this.when(DataPipeStatus.CONNECTED);
	}

	/**
	 * @Override
	 */
	connect() {
		if (this.status !== DataPipeStatus.UNCONNECTED) {
			return Promise.reject(new Error('invalid-state'));
		} else if (this.type == DataPipeType.OUTGOING) {
			return this._connectOutgoing();
		} else {
			return this._connectIncoming();
		}
	}

	_reject() {
		this.stack.hangup(this.internalId);
		return this.when(DataPipeStatus.DISCONNECTED);
	}

	/**
	 * @Override
	 */
	disconnect() {
		if (this.status !== DataPipeStatus.CONNECTING && this.status !== DataPipeStatus.CONNECTED) {
			return Promise.reject(new Error('invalid-state'));
		}
		return this._reject();
	}

	/**
	 * @Override
	 */
	reject() {
		if (this.status !== DataPipeStatus.UNCONNECTED) {
			return Promise.reject(new Error('invalid-state'));
		} else if (this.type != DataPipeType.INCOMING) {
			return Promise.reject(new Error('invalid-state'));
		}
		return this._reject();
	}

	/**
	 * @Override
	 */
	send(data) {
		if (this.status !== DataPipeStatus.CONNECTED) {
			return Promise.reject(new Error('invalid-state'));
		}
		this.stack.sendDataChannel(this.internalId, data);
		return Promise.resolve(this);
	}

	/**
	 * @private
	 * @return {DataChannelDataPipe}
	 */
	makeBindings() {
		this.on('status', this.onStatus);
		if (this.type === DataPipeType.OUTGOING) {
			this.stack.on('qs-calling', this.onQsCalling);
		}
		this.stack.on('qs-established', this.onQsEstablished);
		this.stack.on('qs-end-call qs-lost-call', this.onQsEndCall);
		this.stack.on('qs-data', this.onQsData);
		return this;
	}

	/**
	 * @private
	 * @return {DataChannelDataPipe}
	 */
	destroyBindings() {
		this.off('status', this.onStatus);
		if (this.type === DataPipeType.OUTGOING) {
			this.stack.off('qs-calling', this.onQsCalling);
		}
		this.stack.off('qs-established', this.onQsEstablished);
		this.stack.off('qs-end-call qs-lost-call', this.onQsEndCall);
		this.stack.off('qs-data', this.onQsData);
		return this;
	}

	/**
	 * Destroy bindings when disconnected state reached.
	 * @private
	 */
	onStatus() {
		if (this.status === DataPipeStatus.DISCONNECTED) {
			this.destroyBindings();
		}
	}

	/**
	 * @private
	 */
	onQsCalling(event) {
		if (this.handleCalling) {
			this.internalId = event.callid;
			this.handleCalling = false;
		}
	}

	/**
	 * @private
	 */
	onQsEstablished(event) {
		if (event.callid !== this.internalId) {
			return;
		}
		if (this.type === DataPipeType.OUTGOING) {
			this.stack.sendDataChannel(this.internalId, JSON.stringify({label: this.label}));
		}
	}

	/**
	 * @private
	 */
	onQsEndCall(event) {
		if (event.callid !== this.internalId) {
			return;
		}
		this.status = DataPipeStatus.DISCONNECTED;
	}

	/**
	 * @private
	 */
	onQsData(event) {
		if (event.id !== this.internalId) {
			return;
		}
		if (this.rcv === 0) {
			if (this.type == DataPipeType.INCOMING) {
				const data = JSON.parse(event.data);
				this.label = data.label;
				/* Send back same message as ACK */
				this.stack.sendDataChannel(this.internalId, event.data);
			}
			this.status = DataPipeStatus.CONNECTED;
		} else {
			this.emit('data', event.data);
		}
		this.rcv++;
	}
}