view rhodecode/public/js/yui/storage/storage.js @ 1073:289ff43cc190 beta

update YUI dev libs to latest version
author Marcin Kuzminski <marcin@python-works.com>
date Sun, 20 Feb 2011 20:58:07 +0100
parents 1e757ac98988
children
line wrap: on
line source

/*
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.com/yui/license.html
version: 2.8.2r1
*/
/**
 * The Storage module manages client-side data storage.
 * @module Storage
 */

(function() {

	// internal shorthand
var Y = YAHOO,
	YU = Y.util,
	YL = Y.lang,
	_logOverwriteError;

if (! YU.Storage) {
	_logOverwriteError = function(fxName) {
		Y.log('Exception in YAHOO.util.Storage.?? - must be extended by a storage engine'.replace('??', fxName).replace('??', this.getName ? this.getName() : 'Unknown'), 'error');
	};

	/**
	 * The Storage class is an HTML 5 storage API clone, used to wrap individual storage implementations with a common API.
	 * @class Storage
	 * @namespace YAHOO.util
	 * @constructor
	 * @param location {String} Required. The storage location.
	 * @parm name {String} Required. The engine name.
	 * @param conf {Object} Required. A configuration object.
	 */
	YU.Storage = function(location, name, conf) {
		var that = this;
		Y.env._id_counter += 1;

		// protected variables
		that._cfg = YL.isObject(conf) ? conf : {};
		that._location = location;
		that._name = name;
		that.isReady = false;

		// public variables
		that.createEvent(that.CE_READY, {scope: that});
		that.createEvent(that.CE_CHANGE, {scope: that});
		
		that.subscribe(that.CE_READY, function() {
			that.isReady = true;
		});
	};

	YU.Storage.prototype = {

		/**
		 * The event name for when the storage item is ready.
		 * @property CE_READY
		 * @type {String}
		 * @public
		 */
		CE_READY: 'YUIStorageReady',

		/**
		 * The event name for when the storage item has changed.
		 * @property CE_CHANGE
		 * @type {String}
		 * @public
		 */
		CE_CHANGE: 'YUIStorageChange',

		/**
		 * The delimiter uesed between the data type and the data.
		 * @property DELIMITER
		 * @type {String}
		 * @public
		 */
		DELIMITER: '__',

		/**
		 * The configuration of the engine.
		 * @property _cfg
		 * @type {Object}
		 * @protected
		 */
		_cfg: '',

		/**
		 * The name of this engine.
		 * @property _name
		 * @type {String}
		 * @protected
		 */
		_name: '',

		/**
		 * The location for this instance.
		 * @property _location
		 * @type {String}
		 * @protected
		 */
		_location: '',

		/**
		 * The current length of the keys.
		 * @property length
		 * @type {Number}
		 * @public
		 */
		length: 0,

		/**
		 * This engine singleton has been initialized already.
		 * @property isReady
		 * @type {String}
		 * @protected
		 */
		isReady: false,

		/**
		 * Clears any existing key/value pairs.
		 * @method clear
		 * @public
		 */
		clear: function() {
			this._clear();
			this.length = 0;
		},

		/**
		 * Fetches the data stored and the provided key.
		 * @method getItem
		 * @param key {String} Required. The key used to reference this value (DOMString in HTML 5 spec).
		 * @return {String|NULL} The value stored at the provided key (DOMString in HTML 5 spec).
		 * @public
		 */
		getItem: function(key) {
			Y.log("Fetching item at  " + key);
			var item = this._getItem(key);
			return YL.isValue(item) ? this._getValue(item) : null; // required by HTML 5 spec
		},

		/**
		 * Fetches the storage object's name; should be overwritten by storage engine.
		 * @method getName
		 * @return {String} The name of the data storage object.
		 * @public
		 */
		getName: function() {return this._name;},

		/**
		 * Tests if the key has been set (not in HTML 5 spec); should be overwritten by storage engine.
		 * @method hasKey
		 * @param key {String} Required. The key to search for.
		 * @return {Boolean} True when key has been set.
		 * @public
		 */
		hasKey: function(key) {
			return YL.isString(key) && this._hasKey(key);
		},

		/**
		 * Retrieve the key stored at the provided index; should be overwritten by storage engine.
		 * @method key
		 * @param index {Number} Required. The index to retrieve (unsigned long in HTML 5 spec).
		 * @return {String} Required. The key at the provided index (DOMString in HTML 5 spec).
		 * @public
		 */
		key: function(index) {
			Y.log("Fetching key at " + index);

			if (YL.isNumber(index) && -1 < index && this.length > index) {
				var value = this._key(index);
				if (value) {return value;}
			}

			// this is thrown according to the HTML5 spec
			throw('INDEX_SIZE_ERR - Storage.setItem - The provided index (' + index + ') is not available');
		},

		/**
		 * Remove an item from the data storage.
		 * @method setItem
		 * @param key {String} Required. The key to remove (DOMString in HTML 5 spec).
		 * @public
		 */
		removeItem: function(key) {
			Y.log("removing " + key);
			
			if (this.hasKey(key)) {
                var oldValue = this._getItem(key);
                if (! oldValue) {oldValue = null;}
                this._removeItem(key);
				this.fireEvent(this.CE_CHANGE, new YU.StorageEvent(this, key, oldValue, null, YU.StorageEvent.TYPE_REMOVE_ITEM));
			}
			else {
				// HTML 5 spec says to do nothing
			}
		},

		/**
		 * Adds an item to the data storage.
		 * @method setItem
		 * @param key {String} Required. The key used to reference this value (DOMString in HTML 5 spec).
		 * @param data {Object} Required. The data to store at key (DOMString in HTML 5 spec).
		 * @public
		 * @throws QUOTA_EXCEEDED_ERROR
		 */
		setItem: function(key, data) {
			Y.log("SETTING " + data + " to " + key);
			
			if (YL.isString(key)) {
				var eventType = this.hasKey(key) ? YU.StorageEvent.TYPE_UPDATE_ITEM : YU.StorageEvent.TYPE_ADD_ITEM,
					oldValue = this._getItem(key);
				if (! oldValue) {oldValue = null;}

				if (this._setItem(key, this._createValue(data))) {
					this.fireEvent(this.CE_CHANGE, new YU.StorageEvent(this, key, oldValue, data, eventType));
				}
				else {
					// this is thrown according to the HTML5 spec
					throw('QUOTA_EXCEEDED_ERROR - Storage.setItem - The choosen storage method (' +
						  this.getName() + ') has exceeded capacity');
				}
			}
			else {
				// HTML 5 spec says to do nothing
			}
		},

		/**
		 * Implementation of the clear login; should be overwritten by storage engine.
		 * @method _clear
		 * @protected
		 */
		_clear: function() {
			_logOverwriteError('_clear');
			return '';
		},

		/**
		 * Converts the object into a string, with meta data (type), so it can be restored later.
		 * @method _createValue
		 * @param s {Object} Required. An object to store.
		 * @protected
		 */
		_createValue: function(s) {
			var type = (YL.isNull(s) || YL.isUndefined(s)) ? ('' + s) : typeof s;
			return 'string' === type ? s : type + this.DELIMITER + s;
		},

		/**
		 * Implementation of the getItem login; should be overwritten by storage engine.
		 * @method _getItem
		 * @param key {String} Required. The key used to reference this value.
		 * @return {String|NULL} The value stored at the provided key.
		 * @protected
		 */
		_getItem: function(key) {
			_logOverwriteError('_getItem');
			return '';
		},

		/**
		 * Converts the stored value into its appropriate type.
		 * @method _getValue
		 * @param s {String} Required. The stored value.
		 * @protected
		 */
		_getValue: function(s) {
			var a = s ? s.split(this.DELIMITER) : [];
			if (1 == a.length) {return s;}

			switch (a[0]) {
				case 'boolean': return 'true' === a[1];
				case 'number': return parseFloat(a[1]);
				case 'null': return null;
				default: return a[1];
			}
		},

		/**
		 * Implementation of the key logic; should be overwritten by storage engine.
		 * @method _key
		 * @param index {Number} Required. The index to retrieve (unsigned long in HTML 5 spec).
		 * @return {String|NULL} Required. The key at the provided index (DOMString in HTML 5 spec).
		 * @protected
		 */
		_key: function(index) {
			_logOverwriteError('_key');
			return '';
		},

		/*
		 * Implementation to fetch evaluate the existence of a key.
		 * @see YAHOO.util.Storage._hasKey
		 */
		_hasKey: function(key) {
			return null !== this._getItem(key);
		},

		/**
		 * Implementation of the removeItem login; should be overwritten by storage engine.
		 * @method _removeItem
		 * @param key {String} Required. The key to remove.
		 * @protected
		 */
		_removeItem: function(key) {
			_logOverwriteError('_removeItem');
			return '';
		},

		/**
		 * Implementation of the setItem login; should be overwritten by storage engine.
		 * @method _setItem
		 * @param key {String} Required. The key used to reference this value.
		 * @param data {Object} Required. The data to storage at key.
		 * @return {Boolean} True when successful, false when size QUOTA exceeded.
		 * @protected
		 */
		_setItem: function(key, data) {
			_logOverwriteError('_setItem');
			return '';
		}
	};

	YL.augmentProto(YU.Storage, YU.EventProvider);
}

}());
/**
 * The StorageManager class is a singleton that registers DataStorage objects and returns instances of those objects.
 * @class StorageManager
 * @namespace YAHOO.util
 * @static
 */
(function() {
	// internal shorthand
var Y = YAHOO.util,
	YL = YAHOO.lang,

	// private variables
	_locationEngineMap = {}, // cached engines
	_registeredEngineSet = [], // set of available engines
	_registeredEngineMap = {}, // map of available engines
	
	/**
	 * Fetches a storage constructor if it is available, otherwise returns NULL.
	 * @method _getClass
	 * @param klass {Function} Required. The storage constructor to test.
	 * @return {Function} An available storage constructor or NULL.
	 * @private
	 */
	_getClass = function(klass) {
		return (klass && klass.isAvailable()) ? klass : null;
	},

	/**
	 * Fetches the storage engine from the cache, or creates and caches it.
	 * @method _getStorageEngine
	 * @param location {String} Required. The location to store.
	 * @param klass {Function} Required. A pointer to the engineType Class.
	 * @param conf {Object} Optional. Additional configuration for the data source engine.
	 * @private
	 */
	_getStorageEngine = function(location, klass, conf) {
		var engine = _locationEngineMap[location + klass.ENGINE_NAME];

		if (! engine) {
			engine = new klass(location, conf);
			_locationEngineMap[location + klass.ENGINE_NAME] = engine;
		}

		return engine;
	},

	/**
	 * Ensures that the location is valid before returning it or a default value.
	 * @method _getValidLocation
	 * @param location {String} Required. The location to evaluate.
	 * @private
	 */
	_getValidLocation = function(location) {
		switch (location) {
			case Y.StorageManager.LOCATION_LOCAL:
			case Y.StorageManager.LOCATION_SESSION:
				return location;

			default: return Y.StorageManager.LOCATION_SESSION;
		}
	};

	// public namespace
	Y.StorageManager = {

        /**
         * The storage location - session; data cleared at the end of a user's session.
         * @property LOCATION_SESSION
         * @type {String}
         * @static
         */
		LOCATION_SESSION: 'sessionStorage',

        /**
         * The storage location - local; data cleared on demand.
         * @property LOCATION_LOCAL
         * @type {String}
         * @static
         */
		LOCATION_LOCAL: 'localStorage',

		/**
		 * Fetches the desired engine type or first available engine type.
		 * @method get
		 * @param engineType {String} Optional. The engine type, see engines.
		 * @param location {String} Optional. The storage location - LOCATION_SESSION & LOCATION_LOCAL; default is LOCAL.
		 * @param conf {Object} Optional. Additional configuration for the getting the storage engine.
		 * {
		 * 	engine: {Object} configuration parameters for the desired engine
		 * 	order: {Array} an array of storage engine names; the desired order to try engines}
		 * }
		 * @static
		 */
		get: function(engineType, location, conf) {
			var _cfg = YL.isObject(conf) ? conf : {},
				klass = _getClass(_registeredEngineMap[engineType]);

			if (! klass && ! _cfg.force) {
				var i, j;

				if (_cfg.order) {
					j = _cfg.order.length;

					for (i = 0; i < j && ! klass; i += 1) {
						klass = _getClass(_cfg.order[i]);
					}
				}

				if (! klass) {
					j = _registeredEngineSet.length;

					for (i = 0; i < j && ! klass; i += 1) {
						klass = _getClass(_registeredEngineSet[i]);
					}
				}
			}

			if (klass) {
				return _getStorageEngine(_getValidLocation(location), klass, _cfg.engine);
			}

			throw('YAHOO.util.StorageManager.get - No engine available, please include an engine before calling this function.');
		},

        /*
         * Estimates the size of the string using 1 byte for each alpha-numeric character and 3 for each non-alpha-numeric character.
         * @method getByteSize
         * @param s {String} Required. The string to evaulate.
         * @return {Number} The estimated string size.
         * @private
         */
        getByteSize: function(s) {
			return encodeURIComponent('' + s).length;
        },

		/**
		 * Registers a engineType Class with the StorageManager singleton; first in is the first out.
		 * @method register
		 * @param engineConstructor {Function} Required. The engine constructor function, see engines.
		 * @return {Boolean} When successfully registered.
		 * @static
		 */
		register: function(engineConstructor) {
			if (YL.isFunction(engineConstructor) && YL.isFunction(engineConstructor.isAvailable) && YL.isString(engineConstructor.ENGINE_NAME)) {
				_registeredEngineMap[engineConstructor.ENGINE_NAME] = engineConstructor;
				_registeredEngineSet.push(engineConstructor);
				return true;
			}

			return false;
		}
	};

	YAHOO.register("StorageManager", Y.SWFStore, {version: "2.8.2r1", build: "7"});
}());
(function() {

/**
 * The StorageEvent class manages the storage events by emulating the HTML 5 implementation.
 * @namespace YAHOO.util
 * @class StorageEvent
 * @constructor
 * @param storageArea {Object} Required. The Storage object that was affected.
 * @param key {String} Required. The key being changed; DOMString in HTML 5 spec.
 * @param oldValue {String} Required. The old value of the key being changed; DOMString in HTML 5 spec.
 * @param newValue {String} Required. The new value of the key being changed; DOMString in HTML 5 spec.
 * @param type {String} Required. The storage event type.
 */
YAHOO.util.StorageEvent = function(storageArea, key, oldValue, newValue, type) {
	this.key = key;
	this.oldValue = oldValue;
	this.newValue = newValue;
	this.url = window.location.href;
	this.window = window; // todo: think about the CAJA and innocent code
	this.storageArea = storageArea;
	this.type = type;
};

YAHOO.lang.augmentObject(YAHOO.util.StorageEvent, {
	TYPE_ADD_ITEM: 'addItem',
	TYPE_REMOVE_ITEM: 'removeItem',
	TYPE_UPDATE_ITEM: 'updateItem'
});

YAHOO.util.StorageEvent.prototype = {

    /**
     * The 'key' attribute represents the key being changed.
     * @property key
     * @type {String}
     * @static
     * @readonly
     */
    key: null,

    /**
     * The 'newValue' attribute represents the new value of the key being changed.
     * @property newValue
     * @type {String}
     * @static
     * @readonly
     */
    newValue: null,

    /**
     * The 'oldValue' attribute represents the old value of the key being changed.
     * @property oldValue
     * @type {String}
     * @static
     * @readonly
     */
    oldValue: null,

    /**
     * The 'source' attribute represents the WindowProxy object of the browsing context of the document whose key changed.
     * @property source
     * @type {Object}
     * @static
     * @readonly
     */
    source: null,

    /**
     * The 'storageArea' attribute represents the Storage object that was affected.
     * @property storageArea
     * @type {Object}
     * @static
     * @readonly
     */
    storageArea: null,

    /**
     * The 'type' attribute represents the Storage event type.
     * @property type
     * @type {Object}
     * @static
     * @readonly
     */
    type: null,

    /**
     * The 'url' attribute represents the address of the document whose key changed.
     * @property url
     * @type {String}
     * @static
     * @readonly
     */
    url: null
};
	
}());
(function() {
var Y = YAHOO.util,
	YL = YAHOO.lang;

	/**
	 * The StorageEngineKeyed class implements the interface necessary for managing keys.
	 * @namespace YAHOO.util
	 * @class StorageEngineKeyed
	 * @constructor
	 * @extend YAHOO.util.Storage
	 */
	Y.StorageEngineKeyed = function() {
		Y.StorageEngineKeyed.superclass.constructor.apply(this, arguments);
		this._keys = [];
		this._keyMap = {};
	};

	YL.extend(Y.StorageEngineKeyed, Y.Storage, {

		/**
		 * A collection of keys applicable to the current location. This should never be edited by the developer.
		 * @property _keys
		 * @type {Array}
		 * @protected
		 */
		_keys: null,

		/**
		 * A map of keys to their applicable position in keys array. This should never be edited by the developer.
		 * @property _keyMap
		 * @type {Object}
		 * @protected
		 */
		_keyMap: null,

		/**
		 * Adds the key to the set.
		 * @method _addKey
		 * @param key {String} Required. The key to evaluate.
		 * @protected
		 */
		_addKey: function(key) {
			this._keyMap[key] = this.length;
			this._keys.push(key);
			this.length = this._keys.length;
		},

		/**
		 * Evaluates if a key exists in the keys array; indexOf does not work in all flavors of IE.
		 * @method _indexOfKey
		 * @param key {String} Required. The key to evaluate.
		 * @protected
		 */
		_indexOfKey: function(key) {
			var i = this._keyMap[key];
			return undefined === i ? -1 : i;
		},

		/**
		 * Removes a key from the keys array.
		 * @method _removeKey
		 * @param key {String} Required. The key to remove.
		 * @protected
		 */
		_removeKey: function(key) {
			var j = this._indexOfKey(key),
				rest = this._keys.slice(j + 1);

			delete this._keyMap[key];

			for (var k in this._keyMap) {
				if (j < this._keyMap[k]) {
					this._keyMap[k] -= 1;
				}
			}
			
			this._keys.length = j;
			this._keys = this._keys.concat(rest);
			this.length = this._keys.length;
		}
	});
}());
/*
 * HTML limitations:
 *  - 5MB in FF and Safari, 10MB in IE 8
 *  - only FF 3.5 recovers session storage after a browser crash
 *
 * Thoughts:
 *  - how can we not use cookies to handle session
 */
(function() {
	// internal shorthand
var Y = YAHOO.util,
	YL = YAHOO.lang,

	/*
	 * Required for IE 8 to make synchronous.
	 */
	_beginTransaction = function(engine) {
		if (engine.begin) {engine.begin();}
	},

	/*
	 * Required for IE 8 to make synchronous.
	 */
	_commitTransaction = function(engine) {
		if (engine.commit) {engine.commit();}
	};

	/**
	 * The StorageEngineHTML5 class implements the HTML5 storage engine.
	 * @namespace YAHOO.util
	 * @class StorageEngineHTML5
	 * @constructor
	 * @extend YAHOO.util.Storage
	 * @param location {String} Required. The storage location.
	 * @param conf {Object} Required. A configuration object.
	 */
	Y.StorageEngineHTML5 = function(location, conf) {
		var _this = this;
		Y.StorageEngineHTML5.superclass.constructor.call(_this, location, Y.StorageEngineHTML5.ENGINE_NAME, conf);// not set, are cookies available
		_this._engine = window[location];
		_this.length = _this._engine.length;
		YL.later(250, _this, function() { // temporary solution so that CE_READY can be subscribed to after this object is created
			_this.fireEvent(_this.CE_READY);
		});
	};

	YAHOO.lang.extend(Y.StorageEngineHTML5, Y.Storage, {

		_engine: null,

		/*
		 * Implementation to clear the values from the storage engine.
		 * @see YAHOO.util.Storage._clear
		 */
		_clear: function() {
			var _this = this;
			if (_this._engine.clear) {
				_this._engine.clear();
			}
			// for FF 3, fixed in FF 3.5
			else {
				for (var i = _this.length, key; 0 <= i; i -= 1) {
					key = _this._key(i);
					_this._removeItem(key);
				}
			}
		},

		/*
		 * Implementation to fetch an item from the storage engine.
		 * @see YAHOO.util.Storage._getItem
		 */
		_getItem: function(key) {
			var o = this._engine.getItem(key);
			return YL.isObject(o) ? o.value : o; // for FF 3, fixed in FF 3.5
		},

		/*
		 * Implementation to fetch a key from the storage engine.
		 * @see YAHOO.util.Storage._key
		 */
		_key: function(index) {return this._engine.key(index);},

		/*
		 * Implementation to remove an item from the storage engine.
		 * @see YAHOO.util.Storage._removeItem
		 */
		_removeItem: function(key) {
			var _this = this;
			_beginTransaction(_this._engine);
			_this._engine.removeItem(key);
			_commitTransaction(_this._engine);
			_this.length = _this._engine.length;
		},

		/*
		 * Implementation to remove an item from the storage engine.
		 * @see YAHOO.util.Storage._setItem
		 */
		_setItem: function(key, value) {
			var _this = this;
			
			try {
				_beginTransaction(_this._engine);
				_this._engine.setItem(key, value);
				_commitTransaction(_this._engine);
				_this.length = _this._engine.length;
				return true;
			}
			catch (e) {
				return false;
			}
		}
	}, true);

	Y.StorageEngineHTML5.ENGINE_NAME = 'html5';
	Y.StorageEngineHTML5.isAvailable = function() {
		return window.localStorage;
	};
    Y.StorageManager.register(Y.StorageEngineHTML5);
}());
/*
 * Gears limitation:
 *  - SQLite limitations - http://www.sqlite.org/limits.html
 *  - DB Best Practices - http://code.google.com/apis/gears/gears_faq.html#bestPracticeDB
 * 	- the user must approve before gears can be used
 *  - each SQL query has a limited number of characters (9948 bytes), data will need to be spread across rows
 *  - no query should insert or update more than 9948 bytes of data in a single statement or GEARs will throw:
 *  	[Exception... "'Error: SQL statement is too long.' when calling method: [nsIDOMEventListener::handleEvent]" nsresult: "0x8057001c (NS_ERROR_XPC_JS_THREW_JS_OBJECT)" location: "<unknown>" data: no]
 *
 * Thoughts:
 *  - we may want to implement additional functions for the gears only implementation
 *  - how can we not use cookies to handle session location
 */
(function() {
	// internal shorthand
var Y = YAHOO.util,
	YL = YAHOO.lang,
	_SQL_STMT_LIMIT = 9948,
	_TABLE_NAME = 'YUIStorageEngine',

	// local variables
	_engine = null,

	eURI = encodeURIComponent,
	dURI = decodeURIComponent;

	/**
	 * The StorageEngineGears class implements the Google Gears storage engine.
	 * @namespace YAHOO.util
	 * @class StorageEngineGears
	 * @constructor
	 * @extend YAHOO.util.Storage
	 * @param location {String} Required. The storage location.
	 * @param conf {Object} Required. A configuration object.
	 */
	Y.StorageEngineGears = function(location, conf) {
		var _this = this;
		Y.StorageEngineGears.superclass.constructor.call(_this, location, Y.StorageEngineGears.ENGINE_NAME, conf);

		if (! _engine) {
			// create the database
			_engine = google.gears.factory.create(Y.StorageEngineGears.GEARS);
			_engine.open(window.location.host + '-' + Y.StorageEngineGears.DATABASE);
			_engine.execute('CREATE TABLE IF NOT EXISTS ' + _TABLE_NAME + ' (key TEXT, location TEXT, value TEXT)');
		}

		var isSessionStorage = Y.StorageManager.LOCATION_SESSION === _this._location,
			sessionKey = Y.Cookie.get('sessionKey' + Y.StorageEngineGears.ENGINE_NAME);

		if (! sessionKey) {
			_engine.execute('BEGIN');
			_engine.execute('DELETE FROM ' + _TABLE_NAME + ' WHERE location="' + eURI(Y.StorageManager.LOCATION_SESSION) + '"');
			_engine.execute('COMMIT');
		}

		var rs = _engine.execute('SELECT key FROM ' + _TABLE_NAME + ' WHERE location="' + eURI(_this._location) + '"'),
			keyMap = {};
	
		try {
			// iterate on the rows and map the keys
			while (rs.isValidRow()) {
				var fld = dURI(rs.field(0));

				if (! keyMap[fld]) {
					keyMap[fld] = true;
					_this._addKey(fld);
				}

				rs.next();
			}
		}
		finally {
			rs.close();
		}

		// this is session storage, ensure that the session key is set
		if (isSessionStorage) {
			Y.Cookie.set('sessionKey' + Y.StorageEngineGears.ENGINE_NAME, true);
		}

		_this.length = _this._keys.length;
		YL.later(250, _this, function() { // temporary solution so that CE_READY can be subscribed to after this object is created
			_this.fireEvent(_this.CE_READY);
		});
	};

	YL.extend(Y.StorageEngineGears, Y.StorageEngineKeyed, {

		/*
		 * Implementation to clear the values from the storage engine.
		 * @see YAHOO.util.Storage._clear
		 */
		_clear: function() {
			_engine.execute('BEGIN');
			_engine.execute('DELETE FROM ' + _TABLE_NAME + ' WHERE location="' + eURI(this._location) + '"');
			_engine.execute('COMMIT');
			this._keys = [];
			this.length = 0;
		},

		/*
		 * Implementation to fetch an item from the storage engine.
		 * @see YAHOO.util.Storage._getItem
		 */
		_getItem: function(key) {
			var rs = _engine.execute('SELECT value FROM ' + _TABLE_NAME + ' WHERE key="' + eURI(key) + '" AND location="' + eURI(this._location) + '"'),
				value = '';

			try {
				while (rs.isValidRow()) {
					var temp = rs.field(0);
					value += rs.field(0);
					rs.next();
				}
			}
			finally {
				rs.close();
			}

			return value ? dURI(value) : null;
		},

		/*
		 * Implementation to fetch a key from the storage engine.
		 * @see YAHOO.util.Storage.key
		 */
		_key: function(index) {return this._keys[index];},

		/*
		 * Implementation to remove an item from the storage engine.
		 * @see YAHOO.util.Storage._removeItem
		 */
		_removeItem: function(key) {
			_engine.execute('BEGIN');
			_engine.execute('DELETE FROM ' + _TABLE_NAME + ' WHERE key="' + eURI(key) + '" AND location="' + eURI(this._location) + '"');
			_engine.execute('COMMIT');
			this._removeKey(key);
		},

		/*
		 * Implementation to remove an item from the storage engine.
		 * @see YAHOO.util.Storage._setItem
		 */
		_setItem: function(key, data) {

			if (! this.hasKey(key)) {
				this._addKey(key);
			}

			var _key = eURI(key),
				_location = eURI(this._location),
				_value = eURI(data),
				_values = [],
				_len = _SQL_STMT_LIMIT - (_key + _location).length;

			// the length of the value exceeds the available space
			if (_len < _value.length) {
				for (var i = 0, j = _value.length; i < j; i += _len) {
					_values.push(_value.substr(i, _len));
				}
			}
			else {
				_values.push(_value);
			}

			// Google recommends using INSERT instead of update, because it is faster
			_engine.execute('BEGIN');
			_engine.execute('DELETE FROM ' + _TABLE_NAME + ' WHERE key="' + eURI(key) + '" AND location="' + eURI(this._location) + '"');
			for (var m = 0, n = _values.length; m < n; m += 1) {
				_engine.execute('INSERT INTO ' + _TABLE_NAME + ' VALUES ("' + _key + '", "' + _location + '", "' + _values[m] + '")');
			}
			_engine.execute('COMMIT');
			
			return true;
		}
	});

	// releases the engine when the page unloads
	Y.Event.on('unload', function() {
		if (_engine) {_engine.close();}
	});
	Y.StorageEngineGears.ENGINE_NAME = 'gears';
	Y.StorageEngineGears.GEARS = 'beta.database';
	Y.StorageEngineGears.DATABASE = 'yui.database';
	Y.StorageEngineGears.isAvailable = function() {
		if (window.google && window.google.gears) {
			try {
				// this will throw an exception if the user denies gears
				google.gears.factory.create(Y.StorageEngineGears.GEARS);
				return true;
			}
			catch (e) {
				// no need to do anything
			}
		}

		return false;
	};
    Y.StorageManager.register(Y.StorageEngineGears);
}());
/*
 * SWF limitation:
 *  - only 100,000 bytes of data may be stored this way
 *  - data is publicly available on user machine
 *
 * Thoughts:
 *  - data can be shared across browsers
 *  - how can we not use cookies to handle session location
 */
(function() {
    // internal shorthand
var Y = YAHOO.util,
    YL = YAHOO.lang,
    YD = Y.Dom,
    
    /*
     * The minimum width required to be able to display the settings panel within the SWF.
     */ 
    MINIMUM_WIDTH = 215,

    /*
     * The minimum height required to be able to display the settings panel within the SWF.
     */ 
    MINIMUM_HEIGHT = 138,

    // local variables
    _engine = null,

    /*
     * Creates a location bound key.
     */
    _getKey = function(that, key) {
        return that._location + that.DELIMITER + key;
    },

    /*
     * Initializes the engine, if it isn't already initialized.
     */
    _initEngine = function(cfg) {
        if (! _engine) {
            if (! YL.isString(cfg.swfURL)) {cfg.swfURL = Y.StorageEngineSWF.SWFURL;}
            if (! cfg.containerID) {
                var bd = document.getElementsByTagName('body')[0],
                    container = bd.appendChild(document.createElement('div'));
                cfg.containerID = YD.generateId(container);
            }

            if (! cfg.attributes) {cfg.attributes  = {};}
            if (! cfg.attributes.flashVars) {cfg.attributes.flashVars = {};}
            cfg.attributes.flashVars.useCompression = 'true';
            cfg.attributes.version = 9.115;
            _engine = new YAHOO.widget.SWF(cfg.containerID, cfg.swfURL, cfg.attributes);
        }
    };

    /**
     * The StorageEngineSWF class implements the SWF storage engine.
     * @namespace YAHOO.util
     * @class StorageEngineSWF
     * @uses YAHOO.widget.SWF
     * @constructor
     * @extend YAHOO.util.Storage
     * @param location {String} Required. The storage location.
     * @param conf {Object} Required. A configuration object.
     */
    Y.StorageEngineSWF = function(location, conf) {
        var _this = this;
        Y.StorageEngineSWF.superclass.constructor.call(_this, location, Y.StorageEngineSWF.ENGINE_NAME, conf);
        
        _initEngine(_this._cfg);

        // evaluates when the SWF is loaded
		_engine.unsubscribe('contentReady'); // prevents local and session content ready callbacks from firing, when switching between context
        _engine.addListener("contentReady", function() {
            _this._swf = _engine._swf;
            _engine.initialized = true;
			
			var isSessionStorage = Y.StorageManager.LOCATION_SESSION === _this._location,
				sessionKey = Y.Cookie.get('sessionKey' + Y.StorageEngineSWF.ENGINE_NAME);

            for (var i = _engine.callSWF("getLength", []) - 1; 0 <= i; i -= 1) {
                var key = _engine.callSWF("getNameAt", [i]),
                    isKeySessionStorage = -1 < key.indexOf(Y.StorageManager.LOCATION_SESSION + _this.DELIMITER);

                // this is session storage, but the session key is not set, so remove item
                if (isSessionStorage && ! sessionKey) {
                    _engine.callSWF("removeItem", [key]);
                }
                // the key matches the storage type, add to key collection
                else if (isSessionStorage === isKeySessionStorage) {
                    _this._addKey(key);
                }
            }

            // this is session storage, ensure that the session key is set
            if (isSessionStorage) {
                Y.Cookie.set('sessionKey' + Y.StorageEngineSWF.ENGINE_NAME, true);
            }

            _this.length = _this._keys.length;
            _this.fireEvent(_this.CE_READY);
        });
        
        // required for pages with both a session and local storage
        if (_engine.initialized) {_engine.fireEvent('contentReady');}
    };

    YL.extend(Y.StorageEngineSWF, Y.StorageEngineKeyed, {
        /**
         * The underlying SWF of the engine, exposed so developers can modify the adapter behavior.
         * @property _swf
         * @type {Object}
         * @protected
         */
        _swf: null,

        /*
         * Implementation to clear the values from the storage engine.
         * @see YAHOO.util.Storage._clear
         */
        _clear: function() {
            for (var i = this._keys.length - 1; 0 <= i; i -= 1) {
                var key = this._keys[i];
                _engine.callSWF("removeItem", [key]);
            }

            this._keys = [];
            this.length = 0;
        },

        /*
         * Implementation to fetch an item from the storage engine.
         * @see YAHOO.util.Storage._getItem
         */
        _getItem: function(key) {
            var _key = _getKey(this, key);
            return _engine.callSWF("getValueOf", [_key]);
        },

        /*
         * Implementation to fetch a key from the storage engine.
         * @see YAHOO.util.Storage.key
         */
        _key: function(index) {
            return (this._keys[index] || '').replace(/^.*?__/, '');
        },

        /*
         * Implementation to remove an item from the storage engine.
         * @see YAHOO.util.Storage._removeItem
         */
        _removeItem: function(key) {
            var _key = _getKey(this, key);
            _engine.callSWF("removeItem", [_key]);
            this._removeKey(_key);
        },

        /*
         * Implementation to remove an item from the storage engine.
         * @see YAHOO.util.Storage._setItem
         */
        _setItem: function(key, data) {
            var _key = _getKey(this, key), swfNode;

            // setting the value returns false if the value didn't change,
            // so I changed this to clear the key if it exists so that the
            // fork below works.
            if (_engine.callSWF("getValueOf", [_key])) {
                this._removeItem(key);
            }

            this._addKey(_key);

            if (_engine.callSWF("setItem", [_key, data])) {
                return true;
            } else {

                // @TODO we should not assume that a false return means that
                // the quota has been exceeded.  this dialog should only be
                // displayed if the quotaExceededError event fired.
                swfNode = YD.get(_engine._id);
                if (MINIMUM_WIDTH > YD.getStyle(swfNode, 'width').replace(/\D+/g, '')) {
                    YD.setStyle(swfNode, 'width', MINIMUM_WIDTH + 'px');
                }
                if (MINIMUM_HEIGHT > YD.getStyle(swfNode, 'height').replace(/\D+/g, '')) {
                    YD.setStyle(swfNode, 'height', MINIMUM_HEIGHT + 'px');
                }
                return _engine.callSWF("displaySettings", []);
            }
        }
    });

    Y.StorageEngineSWF.SWFURL = "swfstore.swf";
    Y.StorageEngineSWF.ENGINE_NAME = 'swf';
    Y.StorageEngineSWF.isAvailable = function() {
        return (6 <= YAHOO.env.ua.flash && YAHOO.widget.SWF);
    };
    Y.StorageManager.register(Y.StorageEngineSWF);
}());
YAHOO.register("storage", YAHOO.util.Storage, {version: "2.8.2r1", build: "7"});