cxWebphone.js

/**
 * cxWebphone
 * 
 * @module cxWebphone
 * @namespace cxWebphone
 * @class
 * @hideconstructor
 * @classdesc ConnexCS Webphone SDK Connector
 * 
 * ## Example ##
 * ```
 * var phone = cxWebphone('myId', 'https://webphone.mydomain.com');
 * ```
 */

function cxWebphone (containerId, src) {
	if (!containerId || typeof containerId !== 'string') throw new Error(`Can\'t find <div id="${containerId}"></div>`)
	const promiseCallback = {};
	var container = null
	var iframe = null
	_init();

	return { config, on, provision, call, answer, reject, hangup, mute, unmute, sendDTMF, register, unregister };
	/**
	 * Initalize the component (into an iframe).
	 *
	 * @param {string} ID of the container
	 * @return {string} Webphone Root URL
	 *
	 * @example
	 *     var phone = cxWebphone('myId', 'https://webphone.mydomain.com');
	 */
	
	function _init () {
		container = document.getElementById(containerId);
		container.innerHTML = '';
		if (!container) throw new Error(`Can\'t find <div id="${containerId}"></div>`)
		iframe = document.createElement('iframe');
		iframe.src = src;
		iframe.height = '300px'
		iframe.width = '400px'
		iframe.allow = "microphone"
		// iframe.style.display = "none";
		container.appendChild(iframe);

		window.addEventListener("message", _receiveMessage, false);
	}
	
	/**
	 * Pass config data to the component
	 *
	 * @param {{ username: string, password: string, realm: string, displayName: string, wsServer: string, cli: string }} Config Object
	 * @return {Promise} 
	 *
	 * @example
	 *     phone.config();
	 */

	function config(data) {
		return _postPromise('config', data);
	}


	/**
	 * Add Event Listener
	 *
	 * @param {('session-idle'|'session-initial'|'session-establising'|'session-established'|'session-terminating'|'session-terminated'|'register-initial'|'register-registered'|'register-unregistered'|'register-terminated'|'cdr')} name Event Name
	 * @param {requestCallback} callback Function to handle event
	 *
	 * @example
	 *     phone.on('session-ringing', myRingingFunction);
	 *     phone.on('session-answered', myAnsweredFunction);
	 *     phone.on('register-registered', myRegistrationFunction);
	 *     phone.on('register-unregistered', myUnregisterFunction);
	 */

	function on (name, fn) {
		container.addEventListener(name, fn, false);
	}

	/**
	 * Provision a new User
	 *
	 * @param {{ email: string }} Provision Object
	 * @param {function} Validation Callback
	 * @return {Promise}
	 *
	 * @example
	 *     phone.provision({ email: 'joe@blogs.com'});
	 */
	function provision (data, validationCallback) {
		if (!validationCallback) return _postPromise('provision', data);
		return _postPromise('provision', data)
			.then(validationCallback)
			.then(res => _postPromise('validate', res))
	}

	/**
	 * Start a call with destination
	 *
	 * @param {string} destination The destination endpoint that wish to call
	 * @return {Promise} 
	 *
	 * @example
	 *     phone.call('123456789');
	 */

	function call (destination) {
		return _postPromise('call', {destination});
	}

	/**
	 * Mute the local media of a call
	 *
	 * @return {Promise} 
	 *
	 * @example
	 *     phone.mute();
	 */

	function mute () {
		return _postPromise('mute');
	}

	/**
	 * Unmute the local media of a call
	 *
	 * @return {Promise} 
	 *
	 * @example
	 *     phone.unmute();
	 */

	function unmute () {
		return _postPromise('unmute');
	}

	/**
	 * Answer an incoming call
	 *
	 * @return {Promise} 
	 *
	 * @example
	 *     phone.answer();
	 */

	function answer() {
		return _postPromise('answer');
	}

	/**
	 * Reject an incoming call
	 *
	 * @return {Promise} 
	 *
	 * @example
	 *     phone.reject();
	 */

	function reject() {
		return _postPromise('reject');
	}

	/**
	 * Hangup a call
	 *
	 * @return {Promise} 
	 *
	 * @example
	 *     phone.hangup();
	 */

	function hangup() {
		return _postPromise('hangup');
	}

	/**
	 * Send a (RFC 4733) DTMF event to the active call.
	 *
	 * @param {string} tone The tone that you want to send to the active call when a button is pressed (digits: 0-9, symbols: # *)
	 * @return {Promise} 
	 *
	 * @example
	 *     phone.sendDTMF(tone);
	 */

	function sendDTMF(tone) {
		return _postPromise('sendDTMF', {tone});
	}

	/**
	 * Perform a SIP Registration
	 *
	 * @param {object} [data] Optional Object to be used for Standalone mode, leave empty when provisioning
	 * @param {string} data.displayName Friendly name
	 * @param {string} data.cli From Number
	 * @param {string} data.username Authentication Username
	 * @param {string} data.password Authentication Password
	 * @param {string} data.wsServer Server Address
	 * @param {string} data.realm Realm
	 * @return {Promise} 
	 *
	 * @example <caption>Standalone Example.</caption>
	 *     phone.register({
	 *        displayName: '',
	 *        cli: null,
	 *        username: '',
	 *        password: '',
	 *        wsServer:'',
	 *        realm: ''
	 *     });
	 * @example <caption>Provision Example.</caption>
	 *     phone.register();
	 */

	function register() {
		return _postPromise('register', data);
	}

	/**
	 * SIP Unregsiter
	 *
	 * @return {Promise} 
	 *
	 * @example
	 *     phone.unregister();
	 */

	function unregister() {
		return _postPromise('config', data);
	}

	function _postPromise (fn, data) {
		const id = Math.random() * 10000000;
		return new Promise((resolve, reject) => {
			promiseCallback[id] = {resolve, reject}
			iframe.contentWindow.postMessage({ id, containerId, fn, data, _type: 'webphone-sdk'}, src);
		})
	}
	
	function _receiveMessage (e) {
		console.log('e ', e)
		var wrap = e.data;
		if (!wrap.containerId || wrap.containerId != containerId || wrap.containerId === '*') return
		if (wrap.id && promiseCallback[wrap.id]) {
			if (wrap.error) {
				promiseCallback[wrap.id].reject(new Error(wrap.error))
			} else {
				promiseCallback[wrap.id].resolve(wrap.data)
			}
			delete promiseCallback[wrap.id]
		} else if (wrap.event) {
			console.log('wrap.event: ', wrap.event)
			const event = new CustomEvent(wrap.event, wrap.data);			
			container.dispatchEvent(event);
		}
	}
}

module.exports = cxWebphone