File:LoggingService.js

(function()
{
	/**
	 * Remote loggging of events
	 * @class LoggingService
	 * @constructor
	 * @param {string} [remoteHost] The hostname or IP Address of the machine
	 *        to log out the event data through WebSocket connection.
	 * @param {string} [remotePort=1025] The port for remote logging using
	 *        a WebSocket connection.
	 * @param {String} [channel] The name of the channel to start session.
	 */
	var LoggingService = function(remoteHost, remotePort, channel)
	{
		/**
		 * The Web socket connection for remote logging
		 * @property {WebSocket|MozWebSocket} socket
		 * @private
		 */
		this.socket = null;

		/**
		 * If the web socket connection is available for sending
		 * @property {boolean} connected
		 * @private
		 */
		this.connected = false;

		/**
		 * If the web socket connection is trying to connect
		 * @property {boolean} connecting
		 * @private
		 */
		this.connecting = false;

		/**
		 * The collection of events to buffer while connecting
		 * @property {array} socketBuffer
		 * @private
		 */
		this.socketBuffer = [];

		/**
		 * The current channel for remote logging
		 * @property {string} _channel
		 * @private
		 */
		this._channel = null;

		// Connect to the remote host if it's specified
		if (remoteHost)
		{
			this.connect(remoteHost, remotePort);

			// Set the channel
			if (channel)
			{
				this.channel = channel;
			}
		}
	};

	var p = LoggingService.prototype;

	/**
	 * Connect to the remote logging app
	 * @method connect
	 * @param {string} host Either the IP address or host
	 * @param {int} [port=1025] The port to use for remote logging.
	 */
	p.connect = function(host, port)
	{
		// Make sure WebSocket exists without prefixes for us
		if (("WebSocket" in window || "MozWebSocket" in window) && host)
		{
			port = port || 1025;

			window.WebSocket = WebSocket || MozWebSocket;

			// Bind handlers
			var onCloseRemote = this._onCloseRemote.bind(this);
			var onOpenRemote = this._onOpenRemote.bind(this);

			// Close the current connection if there is one
			onCloseRemote();

			// Start the connection
			this.connecting = true;

			try
			{
				var s = this.socket = new WebSocket("ws://" + host + ":" + port);
				s.onopen = onOpenRemote;
				s.onclose = onCloseRemote;
				s.onerror = onCloseRemote;
				return true;
			}
			catch (error)
			{
				onCloseRemote();
				if (DEBUG) console.error("Unable to connect to WebSocket");
				return false;
			}
		}
		return false;
	};

	/**
	 * The remote logging connection has been created
	 * @method onOpenRemote
	 * @private
	 */
	p._onOpenRemote = function()
	{
		this.connecting = false;
		this.connected = true;

		// Flush all buffered events once we're connected
		for (var i = 0; i < this.socketBuffer.length; i++)
		{
			this.socket.send(this.socketBuffer[i]);
		}

		// Clear the buffer
		this.socketBuffer.length = 0;
	};

	/**
	 * Callback for when the websocket is closed
	 * @method onCloseRemote
	 * @private
	 */
	p._onCloseRemote = function()
	{
		this.connecting = this.connected = false;

		if (this.socketBuffer)
		{
			this.socketBuffer.length = 0;
		}

		if (this.socket)
		{
			this.socket.close();
			this.socket.onopen = null;
			this.socket.onmessage = null;
			this.socket.onclose = null;
			this.socket.onerror = null;
			this.socket = null;
		}
	};

	/**
	 * Set the current channel
	 * @property {String} channel
	 */
	Object.defineProperty(p, 'channel',
	{
		set: function(channel)
		{
			this._channel = channel;
			this.send('session');
		},
		get: function()
		{
			return this._channel;
		}
	});

	/**
	 *  Send data to the remote logging application
	 *  @method send
	 *  @param {string} type Either "event" or "session"
	 *  @param {object} data The data object to send
	 */
	p.send = function(type, data)
	{
		var event = {
			type: type,
			channel: this._channel
		};

		if (!!data)
		{
			event.data = data;
		}

		// Convert to string to send
		event = JSON.stringify(event);

		if (this.connecting)
		{
			this.socketBuffer.push(event);
		}
		else if (this.connected)
		{
			this.socket.send(event);
		}
	};

	/**
	 * Don't use after this
	 * @method destroy
	 */
	p.destroy = function()
	{
		this._onCloseRemote();
		this.socketBuffer = null;
	};

	namespace('springroll.pbskids').LoggingService = LoggingService;

}());