Yahoo! UI Library

Storage  2.9.0

Yahoo! UI Library > Storage > StorageEngineGears.js (source view)
Search:
 
Filters
/*
 * 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 Util = YAHOO.util,
	Lang = YAHOO.lang,
	SQL_STMT_LIMIT = 9948,
	TABLE_NAME = 'YUIStorageEngine',

	// local variables
	_driver = 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 sLocation {String} Required. The storage location.
	 * @param oConf {Object} Required. A configuration object.
	 */
	StorageEngineGears = function(sLocation, oConf) {
		var that = this,
            keyMap = {},
            isSessionStorage, sessionKey, rs;
        
		StorageEngineGears.superclass.constructor.call(that, sLocation, StorageEngineGears.ENGINE_NAME, oConf);

		if (! _driver) {
			// create the database
			_driver = google.gears.factory.create(StorageEngineGears.GEARS);
      // the replace regex fixes http://yuilibrary.com/projects/yui2/ticket/2529411, all ascii characters are allowede except / : * ? " < > | ; ,
			_driver.open(window.location.host.replace(/[\/\:\*\?"\<\>\|;,]/g, '') + '-' + StorageEngineGears.DATABASE);
			_driver.execute('CREATE TABLE IF NOT EXISTS ' + TABLE_NAME + ' (key TEXT, location TEXT, value TEXT)');
		}

		isSessionStorage = Util.StorageManager.LOCATION_SESSION === that._location;
		sessionKey = Util.Cookie.get('sessionKey' + StorageEngineGears.ENGINE_NAME);

		if (! sessionKey) {
			_driver.execute('BEGIN');
			_driver.execute('DELETE FROM ' + TABLE_NAME + ' WHERE location="' + eURI(Util.StorageManager.LOCATION_SESSION) + '"');
			_driver.execute('COMMIT');
		}

		rs = _driver.execute('SELECT key FROM ' + TABLE_NAME + ' WHERE location="' + eURI(that._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;
					that._addKey(fld);
				}

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

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

        that.fireEvent(Util.Storage.CE_READY);
	};

	Lang.extend(StorageEngineGears, Util.StorageEngineKeyed, {

		/*
		 * Implementation to clear the values from the storage engine.
		 * @see YAHOO.util.Storage._clear
		 */
		_clear: function() {
			StorageEngineGears.superclass._clear.call(this);
			_driver.execute('BEGIN');
			_driver.execute('DELETE FROM ' + TABLE_NAME + ' WHERE location="' + eURI(this._location) + '"');
			_driver.execute('COMMIT');
		},

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

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

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

		/*
		 * Implementation to remove an item from the storage engine.
		 * @see YAHOO.util.Storage._removeItem
		 */
		_removeItem: function(sKey) {
			YAHOO.log("removing gears key: " + sKey);
			StorageEngineGears.superclass._removeItem.call(this, sKey);
			_driver.execute('BEGIN');
			_driver.execute('DELETE FROM ' + TABLE_NAME + ' WHERE key="' + eURI(sKey) + '" AND location="' + eURI(this._location) + '"');
			_driver.execute('COMMIT');
		},

		/*
		 * Implementation to remove an item from the storage engine.
		 * @see YAHOO.util.Storage._setItem
		 */
		_setItem: function(sKey, oData) {
			YAHOO.log("SETTING " + oData + " to " + sKey);

			this._addKey(sKey);

			var sEscapedKey = eURI(sKey),
				sEscapedLocation = eURI(this._location),
				sEscapedValue = eURI(oData), // escaped twice, maybe not necessary
				aValues = [],
				nLen = SQL_STMT_LIMIT - (sEscapedKey + sEscapedLocation).length,
                i=0, j;

			// the length of the value exceeds the available space
			if (nLen < sEscapedValue.length) {
				for (j = sEscapedValue.length; i < j; i += nLen) {
					aValues.push(sEscapedValue.substr(i, nLen));
				}
			} else {
				aValues.push(sEscapedValue);
			}

			// Google recommends using INSERT instead of update, because it is faster
			_driver.execute('BEGIN');
			_driver.execute('DELETE FROM ' + TABLE_NAME + ' WHERE key="' + sEscapedKey + '" AND location="' + sEscapedLocation + '"');
			for (i = 0, j = aValues.length; i < j; i += 1) {
				_driver.execute('INSERT INTO ' + TABLE_NAME + ' VALUES ("' + sEscapedKey + '", "' + sEscapedLocation + '", "' + aValues[i] + '")');
			}
			_driver.execute('COMMIT');
			
			return true;
		}
	});

	// releases the engine when the page unloads
	Util.Event.on('unload', function() {
		if (_driver) {_driver.close();}
	});

    StorageEngineGears.ENGINE_NAME = 'gears';
	StorageEngineGears.GEARS = 'beta.database';
	StorageEngineGears.DATABASE = 'yui.database';

	StorageEngineGears.isAvailable = function() {
		if (('google' in window) && ('gears' in window.google)) {
			try {
				// this will throw an exception if the user denies gears
				google.gears.factory.create(StorageEngineGears.GEARS);
				return true;
			}
			catch (e) {
				// no need to do anything
			}
		}

		return false;
	};

    Util.StorageManager.register(StorageEngineGears);
	Util.StorageEngineGears = StorageEngineGears;
}());

Copyright © 2011 Yahoo! Inc. All rights reserved.