Created
February 19, 2010 18:00
-
-
Save rgrove/308988 to your computer and use it in GitHub Desktop.
A persistent local key/value data store for YUI3 similar to HTML5's localStorage. Works in IE5+, Firefox 2+, Safari 3.1+, Chrome 4+, and Opera 10.5+.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Implements a persistent local key/value data store similar to HTML5's | |
* localStorage. Should work in IE5+, Firefox 2+, Safari 3.1+, Chrome 4+, and | |
* Opera 10.5+. | |
* | |
* @module storage-lite | |
*/ | |
YUI.add('storage-lite', function (Y) { | |
// -- Shorthand ---------------------------------------------------------------- | |
var d = Y.config.doc, | |
w = Y.config.win, | |
JSON = Y.JSON, | |
StorageLite = Y.namespace('StorageLite'), | |
// -- Private Constants -------------------------------------------------------- | |
DB_NAME = 'yui_storage_lite', | |
DB_DISPLAYNAME = 'YUI StorageLite data', | |
DB_MAXSIZE = 1048576, | |
DB_VERSION = '1.0', | |
EVT_STORAGE_READY = 'ready', | |
MODE_NOOP = 0, | |
MODE_HTML5 = 1, | |
MODE_GECKO = 2, | |
MODE_DB = 3, | |
MODE_USERDATA = 4, | |
USERDATA_PATH = 'yui_storage_lite', | |
USERDATA_NAME = 'data', | |
// -- Private Variables -------------------------------------------------------- | |
data = {}, | |
storageDriver, | |
storageMode; | |
// -- Implementation ----------------------------------------------------------- | |
// Determine the best available storage mode. | |
if (w.localStorage) { | |
storageMode = MODE_HTML5; | |
} else if (w.globalStorage) { | |
storageMode = MODE_GECKO; | |
} else if (w.openDatabase && navigator.userAgent.indexOf('Chrome') === -1) { | |
storageMode = MODE_DB; | |
} else if (Y.UA.ie >= 5) { | |
storageMode = MODE_USERDATA; | |
} else { | |
storageMode = MODE_NOOP; | |
} | |
Y.StorageFullError = function (message) { | |
Y.StorageFullError.superclass.constructor.call(message); | |
this.name = 'StorageFullError'; | |
this.message = message || 'Maximum storage capacity reached'; | |
if (Y.UA.ie) { | |
this.description = this.message; | |
} | |
}; | |
Y.extend(Y.StorageFullError, Error); | |
/** | |
* Provides a persistent local key/value data store similar to HTML5's | |
* localStorage. | |
* | |
* @class StorageLite | |
* @static | |
*/ | |
// -- Public Events ------------------------------------------------------------ | |
Y.augment(StorageLite, Y.EventTarget); | |
/** | |
* Fired when the storage interface is loaded and ready for use. | |
* | |
* @event ready | |
*/ | |
StorageLite.publish(EVT_STORAGE_READY, {fireOnce: true}); | |
Y.mix(StorageLite, { | |
// -- Public Methods ------------------------------------------------------- | |
/** | |
* Removes all items from the data store. | |
* | |
* @method clear | |
*/ | |
clear: function () {}, | |
/** | |
* Returns the item with the specified key, or <code>null</code> if the item | |
* was not found. | |
* | |
* @method getItem | |
* @param {String} key | |
* @param {bool} json (optional) <code>true</code> if the item is a JSON | |
* string and should be parsed before being returned | |
* @return {Object|null} item or <code>null</code> if not found | |
*/ | |
getItem: function (key, json) { return null; }, | |
/** | |
* Returns the number of items in the data store. | |
* | |
* @method length | |
* @return {Number} number of items in the data store | |
*/ | |
length: function () { return 0; }, | |
/** | |
* Removes the item with the specified key. | |
* | |
* @method removeItem | |
* @param {String} key | |
*/ | |
removeItem: function (key) {}, | |
/** | |
* Stores an item under the specified key. If the key already exists in the | |
* data store, it will be replaced. | |
* | |
* @method setItem | |
* @param {String} key | |
* @param {Object} value | |
* @param {bool} json (optional) <code>true</code> if the item should be | |
* serialized to a JSON string before being stored | |
*/ | |
setItem: function (key, value) {} | |
}); | |
if (storageMode === MODE_HTML5 || storageMode === MODE_GECKO) { | |
// Common methods shared by the HTML5 and Gecko implementations. | |
Y.mix(StorageLite, { | |
length: function () { | |
return storageDriver.length; | |
}, | |
removeItem: function (key) { | |
storageDriver.removeItem(key); | |
}, | |
setItem: function (key, value, json) { | |
storageDriver.setItem(key, json ? JSON.stringify(value) : value); | |
} | |
}, true); | |
if (storageMode === MODE_HTML5) { | |
// HTML5 localStorage methods. Currently supported by IE8, Firefox 3.5+, | |
// Safari 4+, Chrome 4+, and Opera 10.5+. | |
storageDriver = w.localStorage; | |
Y.mix(StorageLite, { | |
clear: function () { | |
storageDriver.clear(); | |
}, | |
getItem: function (key, json) { | |
try { | |
return json ? JSON.parse(storageDriver.getItem(key)) : | |
storageDriver.getItem(key); | |
} catch (ex) { | |
return null; | |
} | |
} | |
}, true); | |
} else if (storageMode === MODE_GECKO) { | |
// Gecko globalStorage methods. Supported by Firefox 2 and 3.0. | |
storageDriver = w.globalStorage[w.location.hostname]; | |
Y.mix(StorageLite, { | |
clear: function () { | |
for (var key in storageDriver) { | |
if (storageDriver.hasOwnProperty(key)) { | |
storageDriver.removeItem(key); | |
delete storageDriver[key]; | |
} | |
} | |
}, | |
getItem: function (key, json) { | |
try { | |
return json ? JSON.parse(storageDriver[key].value) : | |
storageDriver[key].value; | |
} catch (ex) { | |
return null; | |
} | |
} | |
}, true); | |
} | |
StorageLite.fire(EVT_STORAGE_READY); | |
} else if (storageMode === MODE_DB || storageMode === MODE_USERDATA) { | |
// Common methods shared by the database and userdata implementations. | |
Y.mix(StorageLite, { | |
clear: function () { | |
data = {}; | |
StorageLite._save(); | |
}, | |
getItem: function (key, json) { | |
return data.hasOwnProperty(key) ? data[key] : null; | |
}, | |
length: function () { | |
var count = 0, key; | |
for (key in data) { | |
if (data.hasOwnProperty(key)) { | |
count += 1; | |
} | |
} | |
return count; | |
}, | |
removeItem: function (key) { | |
delete data[key]; | |
StorageLite._save(); | |
}, | |
setItem: function (key, value, json) { | |
data[key] = value; | |
StorageLite._save(); | |
} | |
}, true); | |
if (storageMode === MODE_DB) { | |
// Database storage methods. Supported by Safari 3.1 and 3.2. | |
storageDriver = w.openDatabase(DB_NAME, DB_VERSION, DB_DISPLAYNAME, DB_MAXSIZE); | |
Y.mix(StorageLite, { | |
_save: function () { | |
storageDriver.transaction(function (t) { | |
t.executeSql("REPLACE INTO " + DB_NAME + " (name, value) VALUES ('data', ?)", [JSON.stringify(data)]); | |
}); | |
} | |
}, true); | |
storageDriver.transaction(function (t) { | |
t.executeSql("CREATE TABLE IF NOT EXISTS " + DB_NAME + "(name TEXT PRIMARY KEY, value TEXT NOT NULL)"); | |
t.executeSql("SELECT value FROM " + DB_NAME + " WHERE name = 'data'", [], function (t, results) { | |
if (results.rows.length) { | |
try { | |
data = JSON.parse(results.rows.item(0).value); | |
} catch (ex) { | |
data = {}; | |
} | |
} | |
StorageLite.fire(EVT_STORAGE_READY); | |
}); | |
}); | |
} else if (storageMode === MODE_USERDATA) { | |
// userData storage methods. Supported by IE5, 6, and 7. | |
storageDriver = d.createElement('span'); | |
storageDriver.addBehavior('#default#userData'); | |
Y.mix(StorageLite, { | |
_save: function () { | |
var _data = JSON.stringify(data); | |
try { | |
storageDriver.setAttribute(USERDATA_NAME, _data); | |
storageDriver.save(USERDATA_PATH); | |
} catch (ex) { | |
throw new Y.StorageFullError(); | |
} | |
} | |
}, true); | |
Y.on('domready', function () { | |
d.body.appendChild(storageDriver); | |
storageDriver.load(USERDATA_PATH); | |
try { | |
data = JSON.parse(storageDriver.getAttribute(USERDATA_NAME)); | |
} catch (ex) { | |
data = {}; | |
} | |
StorageLite.fire(EVT_STORAGE_READY); | |
}); | |
} | |
} else { | |
// Fire the ready event for browsers that only support the noop mode. | |
StorageLite.fire(EVT_STORAGE_READY); | |
} | |
}, '1.0.0', { | |
requires: ['event-custom', 'event-custom-complex', 'json'] | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment