File:BufferedQueue.js

/**
 * @module Container
 * @namespace springroll.pbskids
 */
(function(undefined)
{
	// Import classes
	var localStorage = include('localStorage');
	var UUID;

	var $ = include('jQuery');

	/**
	 * Buffer event
	 * @class BufferedQueue
	 * @constructor
	 * @param {int} batchSize The number of events to buffer
	 * @param {string} keyPrefix The prefix key for local storage
	 * @param {string} eventServiceUrl The url
	 */
	var BufferedQueue = function(batchSize, keyPrefix, eventServiceUrl)
	{
		if (UUID === undefined)
		{
			UUID = include('springroll.pbskids.UUID');
		}

		/**
		 * If we're online
		 * @property {boolean} onlineStatus
		 */
		this.onlineStatus = true;

		/**
		 * The buffer timer check
		 * @property {int} timer
		 */
		this.timer = null;

		/**
		 * The buffer timer check
		 * @property {array} currentBatch
		 */
		this.currentBatch = [];

		/**
		 * The keys intransit
		 * @property {object} inTransitKeys
		 */
		this.inTransitKeys = {};

		/**
		 * The buffer key prefix
		 * @property {string} keyPrefix
		 */
		this.keyPrefix = keyPrefix;

		/**
		 * The number of events to buffer
		 * @property {int} batchSize
		 */
		this.batchSize = batchSize;

		/**
		 * Path to the end-point
		 * @property {String} eventServiceUrl
		 */
		this.eventServiceUrl = eventServiceUrl;

		/**
		 * The name of the localstorage name for the queue
		 * @property {String} queueName
		 */
		this.queueName = 'bufferQueue' + UUID.genV4().hexString;

		this.resetKey();
	};

	// Reference to the prototype
	var p = BufferedQueue.prototype;

	/**
	 * Process the batch of events
	 * @method processBatch
	 * @param {string} key The key to process
	 * @return {boolean} If the event is being processed
	 */
	p.processBatch = function(key)
	{
		var keys = this.inTransitKeys;
		if (keys[key] === undefined)
		{
			var queueName = this.queueName;
			keys[key] = 0;
			$.ajax(
			{
				url: this.eventServiceUrl,
				data: localStorage.getItem(key),
				type: "POST",
				contentType: "application/json",
				dataType: "json",
				success: function()
				{
					removeFromQueue(queueName, key);
					localStorage.removeItem(key);
					delete keys[key];
				},
				error: function(response)
				{
					if (DEBUG && window.console)
					{
						console.error("Response", response);
					}
					if (response.status == 400)
					{
						//400 is invalid
						//data response from server for events
						removeFromQueue(queueName, key);
						localStorage.removeItem(key);
					}
					delete keys[key];
				}
			});
			return true;
		}
		else
		{
			return false;
		}
	};

	/**
	 * Remove an item to the queue by key
	 * @method  removeFromQueue
	 * @private
	 * @param {String} queueName The name of the queue to remove from
	 * @param {String} key The key of the item to remove
	 */
	var removeFromQueue = function(queueName, key)
	{
		var bufferQueue = getBufferQueue(queueName);
		var index = bufferQueue.indexOf(key);
		if (index > -1)
		{
			bufferQueue.splice(index, 1);
			localStorage.setItem(queueName, JSON.stringify(bufferQueue));
		}
	};

	/**
	 * Add an item to the queue
	 * @method  addToQueue
	 * @private
	 * @param {String} queueName The name of the queue
	 * @param {String} key The key of the item to push
	 */
	var addToQueue = function(queueName, key)
	{
		var bufferQueue = getBufferQueue(queueName);
		var index = bufferQueue.indexOf(key);

		// Only add the key once
		if (index === -1)
		{
			bufferQueue.push(key);
			localStorage.setItem(queueName, JSON.stringify(bufferQueue));
		}
	};

	/**
	 * Add an item to the queue
	 * @method  getBufferQueue
	 * @private
	 * @param {String} queueName The name of the queue to retrieve
	 * @return {Array} The list of keys
	 */
	var getBufferQueue = function(queueName)
	{
		try
		{
			return JSON.parse(localStorage.getItem(queueName)) || [];
		}
		catch (e)
		{}
		return [];
	};

	/**
	 * Start the buffer timer
	 * @method startTimer
	 */
	p.startTimer = function()
	{
		this.stopTimer();
		this.timer = setInterval(
			this._onTimerTick.bind(this),
			1000
		);
	};

	/**
	 * When the timer updates
	 * @method onTimerTick
	 * @private
	 */
	p._onTimerTick = function()
	{
		if (!this.onlineStatus) return;

		var bufferQueue = getBufferQueue(this.queueName);

		for (var i = 0; i < bufferQueue.length; i++)
		{
			if (this.processBatch(bufferQueue[i]))
			{
				break;
			}
		}
	};

	/**
	 * Stop the buffer timer
	 * @method stopTimer
	 */
	p.stopTimer = function()
	{
		clearInterval(this.timer);
		this.timer = null;
	};

	/**
	 * Enable the queue
	 * @method enable
	 */
	p.enable = function()
	{
		this.inTransitKeys = {};
		this.currentBatch.length = 0;
		this.resetKey();
		this.startTimer();
	};

	/**
	 * Disable the queue
	 * @method disable
	 */
	p.disable = function()
	{
		this.stopTimer();
	};

	/**
	 * Autoflush the event queue
	 * @method setAutoFlush
	 * @param {Boolean} on If we should autoflush
	 */
	p.setAutoFlush = function(on)
	{
		if (!on)
		{
			this.stopTimer();
		}
		else if (!this.timer)
		{
			this.startTimer();
		}
	};

	/**
	 * Set the online status
	 * @method setOnline
	 * @param {Boolean} isOnline If we are online
	 */
	p.setOnline = function(isOnline)
	{
		this.onlineStatus = isOnline;
	};

	/**
	 * Push a new event
	 * @method pushEvent
	 * @param {object} event THe event data
	 */
	p.pushEvent = function(event)
	{
		this.inTransitKeys[this.bufferKey] = 0;
		this.currentBatch.push(event);

		localStorage.setItem(this.bufferKey, JSON.stringify(this.currentBatch));
		addToQueue(this.queueName, this.bufferKey);

		if (this.currentBatch.length >= this.batchSize)
		{
			this.currentBatch.length = 0;
			delete this.inTransitKeys[this.bufferKey];
			this.resetKey();
		}
	};

	/**
	 * Flush all the event
	 * @method flushAll
	 */
	p.flushAll = function()
	{
		this.currentBatch.length = 0;
		delete(this.inTransitKeys[this.bufferKey]);
		this.resetKey();

		if (!this.timer)
		{
			//timer is not running hence manually flush
			var bufferQueue = getBufferQueue(this.queueName);

			for (var i = 0; i < bufferQueue.length; i++)
			{
				this.processBatch(bufferQueue[i]);
			}
		}
	};

	/**
	 * Reset the buffer key
	 * @method resetKey
	 */
	p.resetKey = function()
	{
		this.bufferKey = this.keyPrefix + UUID.genV4().hexString;
	};

	/**
	 * End and clear the buffer
	 * @method end
	 */
	p.end = function()
	{
		this.currentBatch.length = 0;
		delete(this.inTransitKeys[this.bufferKey]);
		this.resetKey();
	};

	/**
	 * Destroy and don't use after this
	 * @method destroy
	 */
	p.destroy = function()
	{
		this.end();
		this.currentBatch = null;
		this.inTransitKeys = null;
		this.stopTimer();
	};

	// Assign to namespace
	namespace('springroll.pbskids').BufferedQueue = BufferedQueue;

}());