2 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 * The Storage module manages client-side data storage.
21 RX_TYPE = /^type=(\w+)/i,
22 RX_VALUE = /&value=(.*)/i;
25 _logOverwriteError = function(fxName) {
26 Y.log('Exception in YAHOO.util.Storage.?? - must be extended by a storage engine'.replace('??', fxName).replace('??', this.getName ? this.getName() : 'Unknown'), 'error');
30 * The Storage class is an HTML 5 storage API clone, used to wrap individual storage implementations with a common API.
32 * @namespace YAHOO.util
34 * @param sLocation {String} Required. The storage location.
35 * @parm sName {String} Required. The engine name.
36 * @param oConf {Object} Required. A configuration object.
38 Storage = function(sLocation, sName, oConf) {
40 Y.env._id_counter += 1;
42 // protected variables
43 that._cfg = Lang.isObject(oConf) ? oConf : {};
44 that._location = sLocation;
49 that.createEvent(Storage.CE_READY, {scope: that, fireOnce: true});
50 that.createEvent(Storage.CE_CHANGE, {scope: that});
52 that.subscribe(Storage.CE_READY, function() {
57 Storage.CE_READY = 'YUIStorageReady';
58 Storage.CE_CHANGE = 'YUIStorageChange';
63 * The event name for when the storage item is ready.
68 CE_READY: Storage.CE_READY,
71 * The event name for when the storage item has changed.
76 CE_CHANGE: Storage.CE_CHANGE,
79 * The configuration of the engine.
87 * The name of this engine.
95 * The location for this instance.
103 * The current length of the keys.
111 * This engine singleton has been initialized already.
119 * Clears any existing key/value pairs.
129 * Fetches the data stored and the provided key.
131 * @param sKey {String} Required. The key used to reference this value (DOMString in HTML 5 spec).
132 * @return {String|NULL} The value stored at the provided key (DOMString in HTML 5 spec).
135 getItem: function(sKey) {
136 Y.log("Fetching item at " + sKey);
137 var oItem = this._getItem(sKey);
138 return Lang.isValue(oItem) ? this._getValue(oItem) : null; // required by HTML 5 spec
142 * Fetches the storage object's name; should be overwritten by storage engine.
144 * @return {String} The name of the data storage object.
147 getName: function() {return this._name;},
150 * Tests if the key has been set (not in HTML 5 spec); should be overwritten by storage engine.
152 * @param sKey {String} Required. The key to search for.
153 * @return {Boolean} True when key has been set.
156 hasKey: function(sKey) {
157 return Lang.isString(sKey) && this._hasKey(sKey);
161 * Retrieve the key stored at the provided index; should be overwritten by storage engine.
163 * @param nIndex {Number} Required. The index to retrieve (unsigned long in HTML 5 spec).
164 * @return {String} Required. The key at the provided index (DOMString in HTML 5 spec).
167 key: function(nIndex) {
168 Y.log("Fetching key at " + nIndex);
170 if (Lang.isNumber(nIndex) && -1 < nIndex && this.length > nIndex) {
171 var value = this._key(nIndex);
172 if (value) {return value;}
175 // this is thrown according to the HTML5 spec
176 throw('INDEX_SIZE_ERR - Storage.setItem - The provided index (' + nIndex + ') is not available');
180 * Remove an item from the data storage.
182 * @param sKey {String} Required. The key to remove (DOMString in HTML 5 spec).
185 removeItem: function(sKey) {
186 Y.log("removing " + sKey);
190 if (that.hasKey(sKey)) {
191 oOldValue = that._getItem(sKey);
192 if (! oOldValue) {oOldValue = null;}
193 that._removeItem(sKey);
194 that.fireEvent(Storage.CE_CHANGE, new Util.StorageEvent(that, sKey, oOldValue, null, Util.StorageEvent.TYPE_REMOVE_ITEM));
197 // HTML 5 spec says to do nothing
202 * Adds an item to the data storage.
204 * @param sKey {String} Required. The key used to reference this value (DOMString in HTML 5 spec).
205 * @param oData {Object} Required. The data to store at key (DOMString in HTML 5 spec).
207 * @throws QUOTA_EXCEEDED_ERROR
209 setItem: function(sKey, oData) {
210 Y.log("SETTING " + oData + " to " + sKey);
212 if (Lang.isString(sKey)) {
214 oOldValue = that._getItem(sKey);
216 if (! oOldValue) {oOldValue = null;}
218 if (that._setItem(sKey, that._createValue(oData))) {
219 that.fireEvent(Storage.CE_CHANGE, new Util.StorageEvent(that, sKey, oOldValue, oData,
220 that.hasKey(sKey) ? Util.StorageEvent.TYPE_UPDATE_ITEM : Util.StorageEvent.TYPE_ADD_ITEM));
223 // that is thrown according to the HTML5 spec
224 throw('QUOTA_EXCEEDED_ERROR - Storage.setItem - The choosen storage method (' +
225 that.getName() + ') has exceeded capacity');
229 // HTML 5 spec says to do nothing
234 * Implementation of the clear login; should be overwritten by storage engine.
239 _logOverwriteError('_clear');
244 * Converts the object into a string, with meta data (type), so it can be restored later.
245 * @method _createValue
246 * @param o {Object} Required. An object to store.
249 _createValue: function(o) {
250 var sType = (Lang.isNull(o) || Lang.isUndefined(o)) ? ('' + o) : typeof o;
251 return 'type=' + sType + '&value=' + encodeURIComponent('' + o);
255 * Implementation of the getItem login; should be overwritten by storage engine.
257 * @param sKey {String} Required. The key used to reference this value.
258 * @return {String|NULL} The value stored at the provided key.
261 _getItem: function(sKey) {
262 _logOverwriteError('_getItem');
267 * Converts the stored value into its appropriate type.
269 * @param s {String} Required. The stored value.
272 _getValue: function(s) {
273 var sType = s.match(RX_TYPE)[1],
274 sValue = s.match(RX_VALUE)[1];
277 case 'boolean': return 'true' == sValue;
278 case 'number': return parseFloat(sValue);
279 case 'null': return null;
280 default: return decodeURIComponent(sValue);
285 * Implementation of the key logic; should be overwritten by storage engine.
287 * @param nIndex {Number} Required. The index to retrieve (unsigned long in HTML 5 spec).
288 * @return {String|NULL} Required. The key at the provided index (DOMString in HTML 5 spec).
291 _key: function(nIndex) {
292 _logOverwriteError('_key');
297 * Implementation to fetch evaluate the existence of a key.
299 _hasKey: function(sKey) {
300 return null !== this._getItem(sKey);
304 * Implementation of the removeItem login; should be overwritten by storage engine.
305 * @method _removeItem
306 * @param sKey {String} Required. The key to remove.
309 _removeItem: function(sKey) {
310 _logOverwriteError('_removeItem');
315 * Implementation of the setItem login; should be overwritten by storage engine.
317 * @param sKey {String} Required. The key used to reference this value.
318 * @param oData {Object} Required. The data to storage at key.
319 * @return {Boolean} True when successful, false when size QUOTA exceeded.
322 _setItem: function(sKey, oData) {
323 _logOverwriteError('_setItem');
328 Lang.augmentProto(Storage, Util.EventProvider);
330 Util.Storage = Storage;
335 * The StorageManager class is a singleton that registers DataStorage objects and returns instances of those objects.
336 * @class StorageManager
337 * @namespace YAHOO.util
341 // internal shorthand
342 var Util = YAHOO.util,
346 _locationEngineMap = {}, // cached engines
347 _registeredEngineSet = [], // set of available engines
348 _registeredEngineMap = {}, // map of available engines
351 * Fetches a storage constructor if it is available, otherwise returns NULL.
353 * @param fnClass {Function} Required. The storage constructor to test.
354 * @return {Function} An available storage constructor or NULL.
357 _getClass = function(fnClass) {
358 return (fnClass && fnClass.isAvailable()) ? fnClass : null;
362 * Fetches the storage engine from the cache, or creates and caches it.
363 * @method _getStorageEngine
364 * @param sLocation {String} Required. The location to store.
365 * @param fnClass {Function} Required. A pointer to the engineType Class.
366 * @param oConf {Object} Optional. Additional configuration for the data source engine.
369 _getStorageEngine = function(sLocation, fnClass, oConf) {
370 var engine = _locationEngineMap[sLocation + fnClass.ENGINE_NAME];
373 engine = new fnClass(sLocation, oConf);
374 _locationEngineMap[sLocation + fnClass.ENGINE_NAME] = engine;
381 * Ensures that the location is valid before returning it or a default value.
382 * @method _getValidLocation
383 * @param sLocation {String} Required. The location to evaluate.
386 _getValidLocation = function(sLocation) {
388 case Util.StorageManager.LOCATION_LOCAL:
389 case Util.StorageManager.LOCATION_SESSION:
392 default: return Util.StorageManager.LOCATION_SESSION;
397 Util.StorageManager = {
400 * The storage location - session; data cleared at the end of a user's session.
401 * @property LOCATION_SESSION
405 LOCATION_SESSION: 'sessionStorage',
408 * The storage location - local; data cleared on demand.
409 * @property LOCATION_LOCAL
413 LOCATION_LOCAL: 'localStorage',
416 * Fetches the desired engine type or first available engine type.
418 * @param engineType {String} Optional. The engine type, see engines.
419 * @param sLocation {String} Optional. The storage location - LOCATION_SESSION & LOCATION_LOCAL; default is LOCAL.
420 * @param oConf {Object} Optional. Additional configuration for the getting the storage engine.
422 * engine: {Object} configuration parameters for the desired engine
423 * force: {Boolean} force the <code>engineType</code> or fail
424 * order: {Array} an array of storage engine names; the desired order to try engines}
428 get: function(engineType, sLocation, oConf) {
429 var oCfg = Lang.isObject(oConf) ? oConf : {},
430 fnClass = _getClass(_registeredEngineMap[engineType]),
433 if (! fnClass && ! oCfg.force) {
435 j = oCfg.order.length;
437 for (i = 0; i < j && ! fnClass; i += 1) {
438 fnClass = _getClass(oCfg.order[i]);
443 j = _registeredEngineSet.length;
445 for (i = 0; i < j && ! fnClass; i += 1) {
446 fnClass = _getClass(_registeredEngineSet[i]);
452 return _getStorageEngine(_getValidLocation(sLocation), fnClass, oCfg.engine);
455 throw('YAHOO.util.StorageManager.get - No engine available, please include an engine before calling this function.');
459 * Estimates the size of the string using 1 byte for each alpha-numeric character and 3 for each non-alpha-numeric character.
460 * @method getByteSize
461 * @param s {String} Required. The string to evaulate.
462 * @return {Number} The estimated string size.
465 getByteSize: function(s) {
466 return encodeURIComponent('' + s).length;
470 * Registers a engineType Class with the StorageManager singleton; first in is the first out.
472 * @param engineConstructor {Function} Required. The engine constructor function, see engines.
473 * @return {Boolean} When successfully registered.
476 register: function(engineConstructor) {
477 if (Lang.isFunction(engineConstructor) && Lang.isFunction(engineConstructor.isAvailable) &&
478 Lang.isString(engineConstructor.ENGINE_NAME)) {
479 _registeredEngineMap[engineConstructor.ENGINE_NAME] = engineConstructor;
480 _registeredEngineSet.push(engineConstructor);
488 YAHOO.register("StorageManager", Util.SWFStore, {version: "2.9.0", build: "2800"});
493 * The StorageEvent class manages the storage events by emulating the HTML 5 implementation.
494 * @namespace YAHOO.util
495 * @class StorageEvent
497 * @param oStorageArea {Object} Required. The Storage object that was affected.
498 * @param sKey {String} Required. The key being changed; DOMString in HTML 5 spec.
499 * @param oOldValue {Mixed} Required. The old value of the key being changed; DOMString in HTML 5 spec.
500 * @param oNewValue {Mixed} Required. The new value of the key being changed; DOMString in HTML 5 spec.
501 * @param sType {String} Required. The storage event type.
503 function StorageEvent(oStorageArea, sKey, oOldValue, oNewValue, sType) {
505 this.oldValue = oOldValue;
506 this.newValue = oNewValue;
507 this.url = window.location.href;
508 this.window = window; // todo: think about the CAJA and innocent code
509 this.storageArea = oStorageArea;
513 YAHOO.lang.augmentObject(StorageEvent, {
514 TYPE_ADD_ITEM: 'addItem',
515 TYPE_REMOVE_ITEM: 'removeItem',
516 TYPE_UPDATE_ITEM: 'updateItem'
519 StorageEvent.prototype = {
522 * The 'key' attribute represents the key being changed.
531 * The 'newValue' attribute represents the new value of the key being changed.
540 * The 'oldValue' attribute represents the old value of the key being changed.
549 * The 'source' attribute represents the WindowProxy object of the browsing context of the document whose key changed.
558 * The 'storageArea' attribute represents the Storage object that was affected.
559 * @property storageArea
567 * The 'type' attribute represents the Storage event type.
576 * The 'url' attribute represents the address of the document whose key changed.
585 YAHOO.util.StorageEvent = StorageEvent;
589 var Util = YAHOO.util;
592 * The StorageEngineKeyed class implements the interface necessary for managing keys.
593 * @namespace YAHOO.util
594 * @class StorageEngineKeyed
596 * @extend YAHOO.util.Storage
598 Util.StorageEngineKeyed = function() {
599 Util.StorageEngineKeyed.superclass.constructor.apply(this, arguments);
604 YAHOO.lang.extend(Util.StorageEngineKeyed, Util.Storage, {
607 * A collection of keys applicable to the current location. This should never be edited by the developer.
615 * A map of keys to their applicable position in keys array. This should never be edited by the developer.
623 * Adds the key to the set.
625 * @param sKey {String} Required. The key to evaluate.
628 _addKey: function(sKey) {
629 if (!this._keyMap.hasOwnProperty(sKey)) {
630 this._keys.push(sKey);
631 this._keyMap[sKey] = this.length;
632 this.length = this._keys.length;
637 * Implementation to clear the values from the storage engine.
638 * @see YAHOO.util.Storage._clear
646 * Evaluates if a key exists in the keys array; indexOf does not work in all flavors of IE.
647 * @method _indexOfKey
648 * @param sKey {String} Required. The key to evaluate.
651 _indexOfKey: function(sKey) {
652 var i = this._keyMap[sKey];
653 return undefined === i ? -1 : i;
657 * Implementation to fetch a key from the storage engine.
658 * @see YAHOO.util.Storage.key
660 _key: function(nIndex) {return this._keys[nIndex];},
663 * Removes a key from the keys array.
664 * @method _removeItem
665 * @param sKey {String} Required. The key to remove.
668 _removeItem: function(sKey) {
670 j = that._indexOfKey(sKey),
671 rest = that._keys.slice(j + 1),
674 delete that._keyMap[sKey];
676 // update values in keymap that are greater than current position
677 for (k in that._keyMap) {
678 if (j < that._keyMap[k]) {
679 that._keyMap[k] -= 1;
683 that._keys.length = j;
684 that._keys = that._keys.concat(rest);
685 that.length = that._keys.length;
691 * - 5MB in FF and Safari, 10MB in IE 8
692 * - only FF 3.5 recovers session storage after a browser crash
695 * - how can we not use cookies to handle session
698 // internal shorthand
699 var Util = YAHOO.util,
703 * Required for IE 8 to make synchronous.
705 _beginTransaction = function(driver) {
710 * Required for IE 8 to make synchronous.
712 _commitTransaction = function(driver) {
717 * The StorageEngineHTML5 class implements the HTML5 storage engine.
718 * @namespace YAHOO.util
719 * @class StorageEngineHTML5
721 * @extend YAHOO.util.Storage
722 * @param sLocation {String} Required. The storage location.
723 * @param oConf {Object} Required. A configuration object.
725 StorageEngineHTML5 = function(sLocation, oConf) {
727 oDriver = window[sLocation];
729 StorageEngineHTML5.superclass.constructor.call(that, sLocation, StorageEngineHTML5.ENGINE_NAME, oConf);// not set, are cookies available
731 // simplifieds the begin/commit functions, if not using IE; this provides a massive performance boost
732 if (! oDriver.begin) {_beginTransaction = function() {}; }
733 if (! oDriver.commit) {_commitTransaction = function() {}; }
735 that.length = oDriver.length;
736 that._driver = oDriver;
737 that.fireEvent(Util.Storage.CE_READY);
740 Lang.extend(StorageEngineHTML5, Util.Storage, {
745 * Implementation to clear the values from the storage engine.
746 * @see YAHOO.util.Storage._clear
749 var that = this, i, sKey;
751 if (that._driver.clear) {
752 that._driver.clear();
754 // for FF 3, fixed in FF 3.5
756 for (i = that.length; 0 <= i; i -= 1) {
758 that._removeItem(sKey);
764 * Implementation to fetch an item from the storage engine.
765 * @see YAHOO.util.Storage._getItem
767 _getItem: function(sKey) {
768 var o = this._driver.getItem(sKey);
769 return Lang.isObject(o) ? o.value : o; // for FF 3, fixed in FF 3.5
773 * Implementation to fetch a key from the storage engine.
774 * @see YAHOO.util.Storage._key
776 _key: function(nIndex) {return this._driver.key(nIndex);},
779 * Implementation to remove an item from the storage engine.
780 * @see YAHOO.util.Storage._removeItem
782 _removeItem: function(sKey) {
783 var oDriver = this._driver;
784 _beginTransaction(oDriver);
785 oDriver.removeItem(sKey);
786 _commitTransaction(oDriver);
787 this.length = oDriver.length;
791 * Implementation to remove an item from the storage engine.
792 * @see YAHOO.util.Storage._setItem
794 _setItem: function(sKey, oValue) {
795 var oDriver = this._driver;
798 _beginTransaction(oDriver);
799 oDriver.setItem(sKey, oValue);
800 _commitTransaction(oDriver);
801 this.length = oDriver.length;
810 StorageEngineHTML5.ENGINE_NAME = 'html5';
812 StorageEngineHTML5.isAvailable = function() {
814 return ('localStorage' in window) && window['localStorage'] !== null &&
815 ('sessionStorage' in window) && window['sessionStorage'] !== null;
819 In FireFox and maybe other browsers, you can disable storage in the configuration settings, which
820 will cause an error to be thrown instead of evaluating the simple if/else statement.
826 Util.StorageManager.register(StorageEngineHTML5);
827 Util.StorageEngineHTML5 = StorageEngineHTML5;
831 * - SQLite limitations - http://www.sqlite.org/limits.html
832 * - DB Best Practices - http://code.google.com/apis/gears/gears_faq.html#bestPracticeDB
833 * - the user must approve before gears can be used
834 * - each SQL query has a limited number of characters (9948 bytes), data will need to be spread across rows
835 * - no query should insert or update more than 9948 bytes of data in a single statement or GEARs will throw:
836 * [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]
839 * - we may want to implement additional functions for the gears only implementation
840 * - how can we not use cookies to handle session location
843 // internal shorthand
844 var Util = YAHOO.util,
846 SQL_STMT_LIMIT = 9948,
847 TABLE_NAME = 'YUIStorageEngine',
852 eURI = encodeURIComponent,
853 dURI = decodeURIComponent,
856 * The StorageEngineGears class implements the Google Gears storage engine.
857 * @namespace YAHOO.util
858 * @class StorageEngineGears
860 * @extend YAHOO.util.Storage
861 * @param sLocation {String} Required. The storage location.
862 * @param oConf {Object} Required. A configuration object.
864 StorageEngineGears = function(sLocation, oConf) {
867 isSessionStorage, sessionKey, rs;
869 StorageEngineGears.superclass.constructor.call(that, sLocation, StorageEngineGears.ENGINE_NAME, oConf);
872 // create the database
873 _driver = google.gears.factory.create(StorageEngineGears.GEARS);
874 // the replace regex fixes http://yuilibrary.com/projects/yui2/ticket/2529411, all ascii characters are allowede except / : * ? " < > | ; ,
875 _driver.open(window.location.host.replace(/[\/\:\*\?"\<\>\|;,]/g, '') + '-' + StorageEngineGears.DATABASE);
876 _driver.execute('CREATE TABLE IF NOT EXISTS ' + TABLE_NAME + ' (key TEXT, location TEXT, value TEXT)');
879 isSessionStorage = Util.StorageManager.LOCATION_SESSION === that._location;
880 sessionKey = Util.Cookie.get('sessionKey' + StorageEngineGears.ENGINE_NAME);
883 _driver.execute('BEGIN');
884 _driver.execute('DELETE FROM ' + TABLE_NAME + ' WHERE location="' + eURI(Util.StorageManager.LOCATION_SESSION) + '"');
885 _driver.execute('COMMIT');
888 rs = _driver.execute('SELECT key FROM ' + TABLE_NAME + ' WHERE location="' + eURI(that._location) + '"');
892 // iterate on the rows and map the keys
893 while (rs.isValidRow()) {
894 var fld = dURI(rs.field(0));
907 // this is session storage, ensure that the session key is set
908 if (isSessionStorage) {
909 Util.Cookie.set('sessionKey' + StorageEngineGears.ENGINE_NAME, true);
912 that.fireEvent(Util.Storage.CE_READY);
915 Lang.extend(StorageEngineGears, Util.StorageEngineKeyed, {
918 * Implementation to clear the values from the storage engine.
919 * @see YAHOO.util.Storage._clear
922 StorageEngineGears.superclass._clear.call(this);
923 _driver.execute('BEGIN');
924 _driver.execute('DELETE FROM ' + TABLE_NAME + ' WHERE location="' + eURI(this._location) + '"');
925 _driver.execute('COMMIT');
929 * Implementation to fetch an item from the storage engine.
930 * @see YAHOO.util.Storage._getItem
932 _getItem: function(sKey) {
933 var rs = _driver.execute('SELECT value FROM ' + TABLE_NAME + ' WHERE key="' + eURI(sKey) + '" AND location="' + eURI(this._location) + '"'),
937 while (rs.isValidRow()) {
938 value += rs.field(0);
945 return value ? dURI(value) : null;
949 * Implementation to remove an item from the storage engine.
950 * @see YAHOO.util.Storage._removeItem
952 _removeItem: function(sKey) {
953 StorageEngineGears.superclass._removeItem.call(this, sKey);
954 _driver.execute('BEGIN');
955 _driver.execute('DELETE FROM ' + TABLE_NAME + ' WHERE key="' + eURI(sKey) + '" AND location="' + eURI(this._location) + '"');
956 _driver.execute('COMMIT');
960 * Implementation to remove an item from the storage engine.
961 * @see YAHOO.util.Storage._setItem
963 _setItem: function(sKey, oData) {
967 var sEscapedKey = eURI(sKey),
968 sEscapedLocation = eURI(this._location),
969 sEscapedValue = eURI(oData), // escaped twice, maybe not necessary
971 nLen = SQL_STMT_LIMIT - (sEscapedKey + sEscapedLocation).length,
974 // the length of the value exceeds the available space
975 if (nLen < sEscapedValue.length) {
976 for (j = sEscapedValue.length; i < j; i += nLen) {
977 aValues.push(sEscapedValue.substr(i, nLen));
980 aValues.push(sEscapedValue);
983 // Google recommends using INSERT instead of update, because it is faster
984 _driver.execute('BEGIN');
985 _driver.execute('DELETE FROM ' + TABLE_NAME + ' WHERE key="' + sEscapedKey + '" AND location="' + sEscapedLocation + '"');
986 for (i = 0, j = aValues.length; i < j; i += 1) {
987 _driver.execute('INSERT INTO ' + TABLE_NAME + ' VALUES ("' + sEscapedKey + '", "' + sEscapedLocation + '", "' + aValues[i] + '")');
989 _driver.execute('COMMIT');
995 // releases the engine when the page unloads
996 Util.Event.on('unload', function() {
997 if (_driver) {_driver.close();}
1000 StorageEngineGears.ENGINE_NAME = 'gears';
1001 StorageEngineGears.GEARS = 'beta.database';
1002 StorageEngineGears.DATABASE = 'yui.database';
1004 StorageEngineGears.isAvailable = function() {
1005 if (('google' in window) && ('gears' in window.google)) {
1007 // this will throw an exception if the user denies gears
1008 google.gears.factory.create(StorageEngineGears.GEARS);
1012 // no need to do anything
1019 Util.StorageManager.register(StorageEngineGears);
1020 Util.StorageEngineGears = StorageEngineGears;
1024 * - only 100,000 bytes of data may be stored this way
1025 * - data is publicly available on user machine
1028 * - data can be shared across browsers
1029 * - how can we not use cookies to handle session location
1032 // internal shorthand
1037 StorageManager = Util.StorageManager,
1040 * The minimum width required to be able to display the settings panel within the SWF.
1042 MINIMUM_WIDTH = 215,
1045 * The minimum height required to be able to display the settings panel within the SWF.
1047 MINIMUM_HEIGHT = 138,
1049 RX_STORAGE_PREFIX = new RegExp('^(' + StorageManager.LOCATION_SESSION + '|' + StorageManager.LOCATION_LOCAL + ')'),
1055 * Creates a location bound key.
1057 _getKey = function(that, sKey) {
1058 return that._location + sKey;
1062 * Initializes the engine, if it isn't already initialized.
1064 _initDriver = function(oCfg) {
1066 if (! Lang.isString(oCfg.swfURL)) {oCfg.swfURL = StorageEngineSWF.SWFURL;}
1067 if (! oCfg.containerID) {
1068 var bd = document.getElementsByTagName('body')[0],
1069 container = bd.appendChild(document.createElement('div'));
1070 oCfg.containerID = Dom.generateId(container);
1073 if (! oCfg.attributes) {oCfg.attributes = {};}
1074 if (! oCfg.attributes.flashVars) {oCfg.attributes.flashVars = {};}
1075 oCfg.attributes.flashVars.allowedDomain = document.location.hostname;
1076 oCfg.attributes.flashVars.useCompression = 'true';
1077 oCfg.attributes.version = 9.115;
1078 _driver = new Y.widget.SWF(oCfg.containerID, oCfg.swfURL, oCfg.attributes);
1080 // subscribe to save for info
1081 _driver.subscribe('save', function(o) {
1082 Y.log(o.message, 'info');
1085 // subscribe to errors
1086 _driver.subscribe('quotaExceededError', function(o) {
1087 Y.log(o.message, 'error');
1089 _driver.subscribe('inadequateDimensions', function(o) {
1090 Y.log(o.message, 'error');
1092 _driver.subscribe('error', function(o) {
1093 Y.log(o.message, 'error');
1095 _driver.subscribe('securityError', function(o) {
1096 Y.log(o.message, 'error');
1102 * The StorageEngineSWF class implements the SWF storage engine.
1103 * @namespace YAHOO.util
1104 * @class StorageEngineSWF
1105 * @uses YAHOO.widget.SWF
1107 * @extend YAHOO.util.Storage
1108 * @param sLocation {String} Required. The storage location.
1109 * @param oConf {Object} Required. A configuration object.
1111 StorageEngineSWF = function(sLocation, oConf) {
1113 StorageEngineSWF.superclass.constructor.call(that, sLocation, StorageEngineSWF.ENGINE_NAME, oConf);
1115 _initDriver(that._cfg);
1117 var _onContentReady = function() {
1118 that._swf = _driver._swf;
1119 _driver.initialized = true;
1121 var isSessionStorage = StorageManager.LOCATION_SESSION === that._location,
1122 sessionKey = Util.Cookie.get('sessionKey' + StorageEngineSWF.ENGINE_NAME),
1123 i, key, isKeySessionStorage;
1125 for (i = _driver.callSWF("getLength", []) - 1; 0 <= i; i -= 1) {
1126 key = _driver.callSWF("getNameAt", [i]);
1127 isKeySessionStorage = isSessionStorage && (-1 < key.indexOf(StorageManager.LOCATION_SESSION));
1129 // this is session storage, but the session key is not set, so remove item
1130 if (isKeySessionStorage && ! sessionKey) {
1131 _driver.callSWF("removeItem", [key]);
1133 else if (isSessionStorage === isKeySessionStorage) {
1134 // the key matches the storage type, add to key collection
1139 // this is session storage, ensure that the session key is set
1140 if (isSessionStorage) {
1141 Util.Cookie.set('sessionKey' + StorageEngineSWF.ENGINE_NAME, true);
1144 that.fireEvent(Util.Storage.CE_READY);
1147 // evaluate immediately, SWF is already loaded
1148 if (_driver.initialized) {
1152 // evaluates when the SWF is loaded
1153 _driver.addListener("contentReady", _onContentReady);
1157 Lang.extend(StorageEngineSWF, Util.StorageEngineKeyed, {
1159 * The underlying SWF of the engine, exposed so developers can modify the adapter behavior.
1167 * Implementation to clear the values from the storage engine.
1168 * @see YAHOO.util.Storage._clear
1170 _clear: function() {
1171 for (var i = this._keys.length - 1, sKey; 0 <= i; i -= 1) {
1172 sKey = this._keys[i];
1173 _driver.callSWF("removeItem", [sKey]);
1175 // since keys are used to clear, we call the super function second
1176 StorageEngineSWF.superclass._clear.call(this);
1180 * Implementation to fetch an item from the storage engine.
1181 * @see YAHOO.util.Storage._getItem
1183 _getItem: function(sKey) {
1184 var sLocationKey = _getKey(this, sKey);
1185 return _driver.callSWF("getValueOf", [sLocationKey]);
1189 * Implementation to fetch a key from the storage engine.
1190 * @see YAHOO.util.Storage.key
1192 _key: function(index) {
1193 return StorageEngineSWF.superclass._key.call(this, index).replace(RX_STORAGE_PREFIX, '');
1197 * Implementation to remove an item from the storage engine.
1198 * @see YAHOO.util.Storage._removeItem
1200 _removeItem: function(sKey) {
1201 Y.log("removing SWF key: " + sKey);
1202 var sLocationKey = _getKey(this, sKey);
1203 StorageEngineSWF.superclass._removeItem.call(this, sLocationKey);
1204 _driver.callSWF("removeItem", [sLocationKey]);
1208 * Implementation to remove an item from the storage engine.
1209 * @see YAHOO.util.Storage._setItem
1211 _setItem: function(sKey, oData) {
1212 var sLocationKey = _getKey(this, sKey), swfNode;
1214 if (_driver.callSWF("setItem", [sLocationKey, oData])) {
1215 this._addKey(sLocationKey);
1221 right if the FLASH SLO size needs to be adjusted, then this request and all future requests fail
1222 should we queue these up and poll for when there is enough space?
1224 swfNode = Dom.get(_driver._id);
1225 if (MINIMUM_WIDTH > Dom.getStyle(swfNode, 'width').replace(/\D+/g, '')) {Dom.setStyle(swfNode, 'width', MINIMUM_WIDTH + 'px');}
1226 if (MINIMUM_HEIGHT > Dom.getStyle(swfNode, 'height').replace(/\D+/g, '')) {Dom.setStyle(swfNode, 'height', MINIMUM_HEIGHT + 'px');}
1227 Y.log("attempting to show settings. are dimensions adequate? " + _driver.callSWF("hasAdequateDimensions"));
1228 return _driver.callSWF("displaySettings", []);
1233 StorageEngineSWF.SWFURL = "swfstore.swf";
1234 StorageEngineSWF.ENGINE_NAME = 'swf';
1236 StorageEngineSWF.isAvailable = function() {
1237 return (6 <= Y.env.ua.flash && Y.widget.SWF);
1240 StorageManager.register(StorageEngineSWF);
1241 Util.StorageEngineSWF = StorageEngineSWF;
1243 YAHOO.register("storage", YAHOO.util.Storage, {version: "2.9.0", build: "2800"});