# Yahoo! UI Library

### Storage 2.9.0

Yahoo! UI Library > Storage > StorageEngineGears.js (source view)
Search:
Filters Show Private Show Protected Show Deprecated

    /*
     * 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.