/*
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.com/yui/license.html
version: 2.8.1
*/
/**
* 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.1", build: "19"});
}());
(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) {
YAHOO.log("removing " + 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) {
YAHOO.log("SETTING " + data + " to " + key);
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.1", build: "19"});