File:SavedData.js

/**
 * @module Core
 * @namespace springroll
 */
(function(undefined)
{

	/**
	 * The SavedData functions use localStorage and sessionStorage, with a cookie fallback.
	 *
	 * @class SavedData
	 */
	var SavedData = {};

	/** 
	 * A constant to determine if we can use localStorage and 
	 * sessionStorage 
	 * @static
	 * @property {Boolean} WEB_STORAGE_SUPPORT
	 * @private
	 * @readOnly
	 */
	var WEB_STORAGE_SUPPORT = window.Storage !== undefined;

	/**
	 * A constant for cookie fallback for `SavedData.clear()` 
	 * @static
	 * @property {int} ERASE_COOKIE
	 * @private
	 * @readOnly
	 * @default -1
	 */
	var ERASE_COOKIE = -1;

	//in iOS, if the user is in Private Browsing, writing to localStorage throws an error.
	if (WEB_STORAGE_SUPPORT)
	{
		try
		{
			localStorage.setItem("LS_TEST", "test");
			localStorage.removeItem("LS_TEST");
		}
		catch (e)
		{
			WEB_STORAGE_SUPPORT = false;
		}
	}

	/**
	 * Remove a saved variable by name.
	 * @method remove
	 * @static
	 * @param {String} name The name of the value to remove
	 */
	SavedData.remove = function(name)
	{
		if (WEB_STORAGE_SUPPORT)
		{
			localStorage.removeItem(name);
			sessionStorage.removeItem(name);
		}
		else
			SavedData.write(name, "", ERASE_COOKIE);
	};

	/**
	 * Save a variable.
	 * @method write
	 * @static
	 * @param {String} name The name of the value to save
	 * @param {mixed} value The value to save. This will be run through JSON.stringify().
	 * @param {Boolean} [tempOnly=false] If the value should be saved only in the current browser session.
	 */
	SavedData.write = function(name, value, tempOnly)
	{
		if (WEB_STORAGE_SUPPORT)
		{
			if (tempOnly)
				sessionStorage.setItem(name, JSON.stringify(value));
			else
				localStorage.setItem(name, JSON.stringify(value));
		}
		else
		{
			var expires;
			if (tempOnly)
			{
				if (tempOnly !== ERASE_COOKIE)
					expires = ""; //remove when browser is closed
				else
					expires = "; expires=Thu, 01 Jan 1970 00:00:00 GMT"; //save cookie in the past for immediate removal
			}
			else
				expires = "; expires=" + new Date(2147483646000).toGMTString(); //THE END OF (32bit UNIX) TIME!

			document.cookie = name + "=" + escape(JSON.stringify(value)) + expires + "; path=/";
		}
	};

	/**
	 * Read the value of a saved variable
	 * @method read
	 * @static
	 * @param {String} name The name of the variable
	 * @return {mixed} The value (run through `JSON.parse()`) or null if it doesn't exist
	 */
	SavedData.read = function(name)
	{
		if (WEB_STORAGE_SUPPORT)
		{
			var value = localStorage.getItem(name) || sessionStorage.getItem(name);
			if (value)
				return JSON.parse(value, SavedData.reviver);
			else
				return null;
		}
		else
		{
			var nameEQ = name + "=",
				ca = document.cookie.split(';'),
				i = 0,
				c, len;

			for (i = 0, len = ca.length; i < len; i++)
			{
				c = ca[i];
				while (c.charAt(0) == ' ') c = c.substring(1, c.length);
				if (c.indexOf(nameEQ) === 0) return JSON.parse(unescape(c.substring(nameEQ.length, c.length)), SavedData.reviver);
			}
			return null;
		}
	};

	/**
	 * When restoring from JSON via `JSON.parse`, we may pass a reviver function.
	 * In our case, this will check if the object has a specially-named property (`__classname`).
	 * If it does, we will attempt to construct a new instance of that class, rather than using a
	 * plain old Object. Note that this recurses through the object.
	 * @method reviver
	 * @static
	 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
	 * @param  {String} key   each key name
	 * @param  {Object} value Object that we wish to restore
	 * @return {Object}       The object that was parsed - either cast to a class, or not
	 */
	SavedData.reviver = function(key, value)
	{
		if (value && typeof value.__classname == "string")
		{
			var _class = include(value.__classname, false);
			if (_class)
			{
				var rtn = new _class();
				//if we may call fromJSON, do so
				if (rtn.fromJSON)
				{
					rtn.fromJSON(value);
					//return the cast Object
					return rtn;
				}
			}
		}
		//return the object we were passed in
		return value;
	};

	// Assign to the global space
	namespace('springroll').SavedData = SavedData;

}());