]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/yui/build/storage/storage.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / yui / build / storage / storage.js
1 /*
2 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 2.9.0
6 */
7 /**
8  * The Storage module manages client-side data storage.
9  * @module Storage
10  */
11
12 (function() {
13
14         // internal shorthand
15 var Y = YAHOO,
16         Util = Y.util,
17         Lang = Y.lang,
18         _logOverwriteError,
19     Storage,
20
21         RX_TYPE = /^type=(\w+)/i,
22         RX_VALUE = /&value=(.*)/i;
23
24 if (! Util.Storage) {
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');
27         };
28
29         /**
30          * The Storage class is an HTML 5 storage API clone, used to wrap individual storage implementations with a common API.
31          * @class Storage
32          * @namespace YAHOO.util
33          * @constructor
34          * @param sLocation {String} Required. The storage location.
35          * @parm sName {String} Required. The engine name.
36          * @param oConf {Object} Required. A configuration object.
37          */
38         Storage = function(sLocation, sName, oConf) {
39                 var that = this;
40                 Y.env._id_counter += 1;
41
42                 // protected variables
43                 that._cfg = Lang.isObject(oConf) ? oConf : {};
44                 that._location = sLocation;
45                 that._name = sName;
46                 that.isReady = false;
47
48                 // public variables
49                 that.createEvent(Storage.CE_READY, {scope: that, fireOnce: true});
50                 that.createEvent(Storage.CE_CHANGE, {scope: that});
51                 
52                 that.subscribe(Storage.CE_READY, function() {
53                         that.isReady = true;
54                 });
55         };
56
57     Storage.CE_READY = 'YUIStorageReady';
58     Storage.CE_CHANGE = 'YUIStorageChange';
59
60         Storage.prototype = {
61
62                 /**
63                  * The event name for when the storage item is ready.
64                  * @property CE_READY
65                  * @type {String}
66                  * @public
67                  */
68                 CE_READY: Storage.CE_READY,
69
70                 /**
71                  * The event name for when the storage item has changed.
72                  * @property CE_CHANGE
73                  * @type {String}
74                  * @public
75                  */
76                 CE_CHANGE: Storage.CE_CHANGE,
77
78                 /**
79                  * The configuration of the engine.
80                  * @property _cfg
81                  * @type {Object}
82                  * @protected
83                  */
84                 _cfg: '',
85
86                 /**
87                  * The name of this engine.
88                  * @property _name
89                  * @type {String}
90                  * @protected
91                  */
92                 _name: '',
93
94                 /**
95                  * The location for this instance.
96                  * @property _location
97                  * @type {String}
98                  * @protected
99                  */
100                 _location: '',
101
102                 /**
103                  * The current length of the keys.
104                  * @property length
105                  * @type {Number}
106                  * @public
107                  */
108                 length: 0,
109
110                 /**
111                  * This engine singleton has been initialized already.
112                  * @property isReady
113                  * @type {String}
114                  * @protected
115                  */
116                 isReady: false,
117
118                 /**
119                  * Clears any existing key/value pairs.
120                  * @method clear
121                  * @public
122                  */
123                 clear: function() {
124                         this._clear();
125                         this.length = 0;
126                 },
127
128                 /**
129                  * Fetches the data stored and the provided key.
130                  * @method getItem
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).
133                  * @public
134                  */
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
139                 },
140
141                 /**
142                  * Fetches the storage object's name; should be overwritten by storage engine.
143                  * @method getName
144                  * @return {String} The name of the data storage object.
145                  * @public
146                  */
147                 getName: function() {return this._name;},
148
149                 /**
150                  * Tests if the key has been set (not in HTML 5 spec); should be overwritten by storage engine.
151                  * @method hasKey
152                  * @param sKey {String} Required. The key to search for.
153                  * @return {Boolean} True when key has been set.
154                  * @public
155                  */
156                 hasKey: function(sKey) {
157                         return Lang.isString(sKey) && this._hasKey(sKey);
158                 },
159
160                 /**
161                  * Retrieve the key stored at the provided index; should be overwritten by storage engine.
162                  * @method key
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).
165                  * @public
166                  */
167                 key: function(nIndex) {
168                         Y.log("Fetching key at " + nIndex);
169
170                         if (Lang.isNumber(nIndex) && -1 < nIndex && this.length > nIndex) {
171                                 var value = this._key(nIndex);
172                                 if (value) {return value;}
173                         }
174
175                         // this is thrown according to the HTML5 spec
176                         throw('INDEX_SIZE_ERR - Storage.setItem - The provided index (' + nIndex + ') is not available');
177                 },
178
179                 /**
180                  * Remove an item from the data storage.
181                  * @method removeItem
182                  * @param sKey {String} Required. The key to remove (DOMString in HTML 5 spec).
183                  * @public
184                  */
185                 removeItem: function(sKey) {
186                         Y.log("removing " + sKey);
187             var that = this,
188                 oOldValue;
189                         
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));
195                         }
196                         else {
197                                 // HTML 5 spec says to do nothing
198                         }
199                 },
200
201                 /**
202                  * Adds an item to the data storage.
203                  * @method setItem
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).
206                  * @public
207                  * @throws QUOTA_EXCEEDED_ERROR
208                  */
209                 setItem: function(sKey, oData) {
210                         Y.log("SETTING " + oData + " to " + sKey);
211                         
212                         if (Lang.isString(sKey)) {
213                                 var that = this,
214                     oOldValue = that._getItem(sKey);
215
216                                 if (! oOldValue) {oOldValue = null;}
217
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));
221                                 }
222                                 else {
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');
226                                 }
227                         }
228                         else {
229                                 // HTML 5 spec says to do nothing
230                         }
231                 },
232
233                 /**
234                  * Implementation of the clear login; should be overwritten by storage engine.
235                  * @method _clear
236                  * @protected
237                  */
238                 _clear: function() {
239                         _logOverwriteError('_clear');
240                         return '';
241                 },
242
243                 /**
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.
247                  * @protected
248                  */
249                 _createValue: function(o) {
250                         var sType = (Lang.isNull(o) || Lang.isUndefined(o)) ? ('' + o) : typeof o;
251                         return 'type=' + sType + '&value=' + encodeURIComponent('' + o);
252                 },
253
254                 /**
255                  * Implementation of the getItem login; should be overwritten by storage engine.
256                  * @method _getItem
257                  * @param sKey {String} Required. The key used to reference this value.
258                  * @return {String|NULL} The value stored at the provided key.
259                  * @protected
260                  */
261                 _getItem: function(sKey) {
262                         _logOverwriteError('_getItem');
263                         return '';
264                 },
265
266                 /**
267                  * Converts the stored value into its appropriate type.
268                  * @method _getValue
269                  * @param s {String} Required. The stored value.
270                  * @protected
271                  */
272                 _getValue: function(s) {
273                         var sType = s.match(RX_TYPE)[1],
274                                 sValue = s.match(RX_VALUE)[1];
275
276                         switch (sType) {
277                                 case 'boolean': return 'true' == sValue;
278                                 case 'number': return parseFloat(sValue);
279                                 case 'null': return null;
280                                 default: return decodeURIComponent(sValue);
281                         }
282                 },
283
284                 /**
285                  * Implementation of the key logic; should be overwritten by storage engine.
286                  * @method _key
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).
289                  * @protected
290                  */
291                 _key: function(nIndex) {
292                         _logOverwriteError('_key');
293                         return '';
294                 },
295
296                 /*
297                  * Implementation to fetch evaluate the existence of a key.
298                  */
299                 _hasKey: function(sKey) {
300                         return null !== this._getItem(sKey);
301                 },
302
303                 /**
304                  * Implementation of the removeItem login; should be overwritten by storage engine.
305                  * @method _removeItem
306                  * @param sKey {String} Required. The key to remove.
307                  * @protected
308                  */
309                 _removeItem: function(sKey) {
310                         _logOverwriteError('_removeItem');
311                         return '';
312                 },
313
314                 /**
315                  * Implementation of the setItem login; should be overwritten by storage engine.
316                  * @method _setItem
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.
320                  * @protected
321                  */
322                 _setItem: function(sKey, oData) {
323                         _logOverwriteError('_setItem');
324                         return '';
325                 }
326         };
327
328         Lang.augmentProto(Storage, Util.EventProvider);
329
330     Util.Storage = Storage;
331 }
332
333 }());
334 /**
335  * The StorageManager class is a singleton that registers DataStorage objects and returns instances of those objects.
336  * @class StorageManager
337  * @namespace YAHOO.util
338  * @static
339  */
340 (function() {
341         // internal shorthand
342 var Util = YAHOO.util,
343         Lang = YAHOO.lang,
344
345         // private variables
346         _locationEngineMap = {}, // cached engines
347         _registeredEngineSet = [], // set of available engines
348         _registeredEngineMap = {}, // map of available engines
349         
350         /**
351          * Fetches a storage constructor if it is available, otherwise returns NULL.
352          * @method _getClass
353          * @param fnClass {Function} Required. The storage constructor to test.
354          * @return {Function} An available storage constructor or NULL.
355          * @private
356          */
357         _getClass = function(fnClass) {
358                 return (fnClass && fnClass.isAvailable()) ? fnClass : null;
359         },
360
361         /**
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.
367          * @private
368          */
369         _getStorageEngine = function(sLocation, fnClass, oConf) {
370                 var engine = _locationEngineMap[sLocation + fnClass.ENGINE_NAME];
371
372                 if (! engine) {
373                         engine = new fnClass(sLocation, oConf);
374                         _locationEngineMap[sLocation + fnClass.ENGINE_NAME] = engine;
375                 }
376
377                 return engine;
378         },
379
380         /**
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.
384          * @private
385          */
386         _getValidLocation = function(sLocation) {
387                 switch (sLocation) {
388                         case Util.StorageManager.LOCATION_LOCAL:
389                         case Util.StorageManager.LOCATION_SESSION:
390                                 return sLocation;
391
392                         default: return Util.StorageManager.LOCATION_SESSION;
393                 }
394         };
395
396         // public namespace
397         Util.StorageManager = {
398
399         /**
400          * The storage location - session; data cleared at the end of a user's session.
401          * @property LOCATION_SESSION
402          * @type {String}
403          * @static
404          */
405                 LOCATION_SESSION: 'sessionStorage',
406
407         /**
408          * The storage location - local; data cleared on demand.
409          * @property LOCATION_LOCAL
410          * @type {String}
411          * @static
412          */
413                 LOCATION_LOCAL: 'localStorage',
414
415                 /**
416                  * Fetches the desired engine type or first available engine type.
417                  * @method get
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.
421                  * {
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}
425                  * }
426                  * @static
427                  */
428                 get: function(engineType, sLocation, oConf) {
429                         var oCfg = Lang.isObject(oConf) ? oConf : {},
430                                 fnClass = _getClass(_registeredEngineMap[engineType]),
431                 i , j;
432
433                         if (! fnClass && ! oCfg.force) {
434                                 if (oCfg.order) {
435                                         j = oCfg.order.length;
436
437                                         for (i = 0; i < j && ! fnClass; i += 1) {
438                                                 fnClass = _getClass(oCfg.order[i]);
439                                         }
440                                 }
441
442                                 if (! fnClass) {
443                                         j = _registeredEngineSet.length;
444
445                                         for (i = 0; i < j && ! fnClass; i += 1) {
446                                                 fnClass = _getClass(_registeredEngineSet[i]);
447                                         }
448                                 }
449                         }
450
451                         if (fnClass) {
452                                 return _getStorageEngine(_getValidLocation(sLocation), fnClass, oCfg.engine);
453                         }
454
455                         throw('YAHOO.util.StorageManager.get - No engine available, please include an engine before calling this function.');
456                 },
457
458         /*
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.
463          * @private
464          */
465         getByteSize: function(s) {
466                         return encodeURIComponent('' + s).length;
467         },
468
469                 /**
470                  * Registers a engineType Class with the StorageManager singleton; first in is the first out.
471                  * @method register
472                  * @param engineConstructor {Function} Required. The engine constructor function, see engines.
473                  * @return {Boolean} When successfully registered.
474                  * @static
475                  */
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);
481                                 return true;
482                         }
483
484                         return false;
485                 }
486         };
487
488         YAHOO.register("StorageManager", Util.SWFStore, {version: "2.9.0", build: "2800"});
489 }());
490 (function() {
491
492 /**
493  * The StorageEvent class manages the storage events by emulating the HTML 5 implementation.
494  * @namespace YAHOO.util
495  * @class StorageEvent
496  * @constructor
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.
502  */
503 function StorageEvent(oStorageArea, sKey, oOldValue, oNewValue, sType) {
504         this.key = sKey;
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;
510         this.type = sType;
511 }
512
513 YAHOO.lang.augmentObject(StorageEvent, {
514         TYPE_ADD_ITEM: 'addItem',
515         TYPE_REMOVE_ITEM: 'removeItem',
516         TYPE_UPDATE_ITEM: 'updateItem'
517 });
518
519 StorageEvent.prototype = {
520
521     /**
522      * The 'key' attribute represents the key being changed.
523      * @property key
524      * @type {String}
525      * @static
526      * @readonly
527      */
528     key: null,
529
530     /**
531      * The 'newValue' attribute represents the new value of the key being changed.
532      * @property newValue
533      * @type {Mixed}
534      * @static
535      * @readonly
536      */
537     newValue: null,
538
539     /**
540      * The 'oldValue' attribute represents the old value of the key being changed.
541      * @property oldValue
542      * @type {Mixed}
543      * @static
544      * @readonly
545      */
546     oldValue: null,
547
548     /**
549      * The 'source' attribute represents the WindowProxy object of the browsing context of the document whose key changed.
550      * @property source
551      * @type {Object}
552      * @static
553      * @readonly
554      */
555     source: null,
556
557     /**
558      * The 'storageArea' attribute represents the Storage object that was affected.
559      * @property storageArea
560      * @type {Object}
561      * @static
562      * @readonly
563      */
564     storageArea: null,
565
566     /**
567      * The 'type' attribute represents the Storage event type.
568      * @property type
569      * @type {Object}
570      * @static
571      * @readonly
572      */
573     type: null,
574
575     /**
576      * The 'url' attribute represents the address of the document whose key changed.
577      * @property url
578      * @type {String}
579      * @static
580      * @readonly
581      */
582     url: null
583 };
584
585 YAHOO.util.StorageEvent = StorageEvent;
586         
587 }());
588 (function() {
589 var Util = YAHOO.util;
590
591         /**
592          * The StorageEngineKeyed class implements the interface necessary for managing keys.
593          * @namespace YAHOO.util
594          * @class StorageEngineKeyed
595          * @constructor
596          * @extend YAHOO.util.Storage
597          */
598         Util.StorageEngineKeyed = function() {
599                 Util.StorageEngineKeyed.superclass.constructor.apply(this, arguments);
600                 this._keys = [];
601                 this._keyMap = {};
602         };
603
604         YAHOO.lang.extend(Util.StorageEngineKeyed, Util.Storage, {
605
606                 /**
607                  * A collection of keys applicable to the current location. This should never be edited by the developer.
608                  * @property _keys
609                  * @type {Array}
610                  * @protected
611                  */
612                 _keys: null,
613
614                 /**
615                  * A map of keys to their applicable position in keys array. This should never be edited by the developer.
616                  * @property _keyMap
617                  * @type {Object}
618                  * @protected
619                  */
620                 _keyMap: null,
621
622                 /**
623                  * Adds the key to the set.
624                  * @method _addKey
625                  * @param sKey {String} Required. The key to evaluate.
626                  * @protected
627                  */
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;
633                         }
634                 },
635
636                 /*
637                  * Implementation to clear the values from the storage engine.
638                  * @see YAHOO.util.Storage._clear
639                  */
640                 _clear: function() {
641                         this._keys = [];
642                         this.length = 0;
643                 },
644
645                 /**
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.
649                  * @protected
650                  */
651                 _indexOfKey: function(sKey) {
652                         var i = this._keyMap[sKey];
653                         return undefined === i ? -1 : i;
654                 },
655
656                 /*
657                  * Implementation to fetch a key from the storage engine.
658                  * @see YAHOO.util.Storage.key
659                  */
660                 _key: function(nIndex) {return this._keys[nIndex];},
661
662                 /**
663                  * Removes a key from the keys array.
664                  * @method _removeItem
665                  * @param sKey {String} Required. The key to remove.
666                  * @protected
667                  */
668                 _removeItem: function(sKey) {
669                         var that = this,
670                 j = that._indexOfKey(sKey),
671                                 rest = that._keys.slice(j + 1),
672                 k;
673
674                         delete that._keyMap[sKey];
675
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;
680                                 }
681                         }
682                         
683                         that._keys.length = j;
684                         that._keys = that._keys.concat(rest);
685                         that.length = that._keys.length;
686                 }
687         });
688 }());
689 /*
690  * HTML limitations:
691  *  - 5MB in FF and Safari, 10MB in IE 8
692  *  - only FF 3.5 recovers session storage after a browser crash
693  *
694  * Thoughts:
695  *  - how can we not use cookies to handle session
696  */
697 (function() {
698         // internal shorthand
699 var Util = YAHOO.util,
700         Lang = YAHOO.lang,
701
702         /*
703          * Required for IE 8 to make synchronous.
704          */
705         _beginTransaction = function(driver) {
706                 driver.begin();
707         },
708
709         /*
710          * Required for IE 8 to make synchronous.
711          */
712         _commitTransaction = function(driver) {
713                 driver.commit();
714         },
715
716         /**
717          * The StorageEngineHTML5 class implements the HTML5 storage engine.
718          * @namespace YAHOO.util
719          * @class StorageEngineHTML5
720          * @constructor
721          * @extend YAHOO.util.Storage
722          * @param sLocation {String} Required. The storage location.
723          * @param oConf {Object} Required. A configuration object.
724          */
725         StorageEngineHTML5 = function(sLocation, oConf) {
726                 var that = this,
727             oDriver = window[sLocation];
728         
729                 StorageEngineHTML5.superclass.constructor.call(that, sLocation, StorageEngineHTML5.ENGINE_NAME, oConf);// not set, are cookies available
730
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() {}; }
734
735                 that.length = oDriver.length;
736         that._driver = oDriver;
737         that.fireEvent(Util.Storage.CE_READY);
738         };
739
740         Lang.extend(StorageEngineHTML5, Util.Storage, {
741
742                 _driver: null,
743
744                 /*
745                  * Implementation to clear the values from the storage engine.
746                  * @see YAHOO.util.Storage._clear
747                  */
748                 _clear: function() {
749                         var that = this, i, sKey;
750
751                         if (that._driver.clear) {
752                                 that._driver.clear();
753                         }
754                         // for FF 3, fixed in FF 3.5
755                         else {
756                                 for (i = that.length; 0 <= i; i -= 1) {
757                                         sKey = that._key(i);
758                                         that._removeItem(sKey);
759                                 }
760                         }
761                 },
762
763                 /*
764                  * Implementation to fetch an item from the storage engine.
765                  * @see YAHOO.util.Storage._getItem
766                  */
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
770                 },
771
772                 /*
773                  * Implementation to fetch a key from the storage engine.
774                  * @see YAHOO.util.Storage._key
775                  */
776                 _key: function(nIndex) {return this._driver.key(nIndex);},
777
778                 /*
779                  * Implementation to remove an item from the storage engine.
780                  * @see YAHOO.util.Storage._removeItem
781                  */
782                 _removeItem: function(sKey) {
783                         var oDriver = this._driver;
784                         _beginTransaction(oDriver);
785                         oDriver.removeItem(sKey);
786                         _commitTransaction(oDriver);
787                         this.length = oDriver.length;
788                 },
789
790                 /*
791                  * Implementation to remove an item from the storage engine.
792                  * @see YAHOO.util.Storage._setItem
793                  */
794                 _setItem: function(sKey, oValue) {
795                         var oDriver = this._driver;
796
797                         try {
798                                 _beginTransaction(oDriver);
799                                 oDriver.setItem(sKey, oValue);
800                                 _commitTransaction(oDriver);
801                                 this.length = oDriver.length;
802                                 return true;
803                         }
804                         catch (e) {
805                                 return false;
806                         }
807                 }
808         }, true);
809
810         StorageEngineHTML5.ENGINE_NAME = 'html5';
811     
812         StorageEngineHTML5.isAvailable = function() {
813         try {
814             return ('localStorage' in window) && window['localStorage'] !== null &&
815                     ('sessionStorage' in window) && window['sessionStorage'] !== null;
816         }
817         catch (e) {
818             /*
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.
821              */
822             return false;
823         }
824     };
825
826     Util.StorageManager.register(StorageEngineHTML5);
827         Util.StorageEngineHTML5 = StorageEngineHTML5;
828 }());
829 /*
830  * Gears limitation:
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]
837  *
838  * Thoughts:
839  *  - we may want to implement additional functions for the gears only implementation
840  *  - how can we not use cookies to handle session location
841  */
842 (function() {
843         // internal shorthand
844 var Util = YAHOO.util,
845         Lang = YAHOO.lang,
846         SQL_STMT_LIMIT = 9948,
847         TABLE_NAME = 'YUIStorageEngine',
848
849         // local variables
850         _driver = null,
851
852         eURI = encodeURIComponent,
853         dURI = decodeURIComponent,
854
855         /**
856          * The StorageEngineGears class implements the Google Gears storage engine.
857          * @namespace YAHOO.util
858          * @class StorageEngineGears
859          * @constructor
860          * @extend YAHOO.util.Storage
861          * @param sLocation {String} Required. The storage location.
862          * @param oConf {Object} Required. A configuration object.
863          */
864         StorageEngineGears = function(sLocation, oConf) {
865                 var that = this,
866             keyMap = {},
867             isSessionStorage, sessionKey, rs;
868         
869                 StorageEngineGears.superclass.constructor.call(that, sLocation, StorageEngineGears.ENGINE_NAME, oConf);
870
871                 if (! _driver) {
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)');
877                 }
878
879                 isSessionStorage = Util.StorageManager.LOCATION_SESSION === that._location;
880                 sessionKey = Util.Cookie.get('sessionKey' + StorageEngineGears.ENGINE_NAME);
881
882                 if (! sessionKey) {
883                         _driver.execute('BEGIN');
884                         _driver.execute('DELETE FROM ' + TABLE_NAME + ' WHERE location="' + eURI(Util.StorageManager.LOCATION_SESSION) + '"');
885                         _driver.execute('COMMIT');
886                 }
887
888                 rs = _driver.execute('SELECT key FROM ' + TABLE_NAME + ' WHERE location="' + eURI(that._location) + '"');
889                 keyMap = {};
890         
891                 try {
892                         // iterate on the rows and map the keys
893                         while (rs.isValidRow()) {
894                                 var fld = dURI(rs.field(0));
895
896                                 if (! keyMap[fld]) {
897                                         keyMap[fld] = true;
898                                         that._addKey(fld);
899                                 }
900
901                                 rs.next();
902                         }
903                 } finally {
904                         rs.close();
905                 }
906
907                 // this is session storage, ensure that the session key is set
908                 if (isSessionStorage) {
909                         Util.Cookie.set('sessionKey' + StorageEngineGears.ENGINE_NAME, true);
910                 }
911
912         that.fireEvent(Util.Storage.CE_READY);
913         };
914
915         Lang.extend(StorageEngineGears, Util.StorageEngineKeyed, {
916
917                 /*
918                  * Implementation to clear the values from the storage engine.
919                  * @see YAHOO.util.Storage._clear
920                  */
921                 _clear: function() {
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');
926                 },
927
928                 /*
929                  * Implementation to fetch an item from the storage engine.
930                  * @see YAHOO.util.Storage._getItem
931                  */
932                 _getItem: function(sKey) {
933                         var rs = _driver.execute('SELECT value FROM ' + TABLE_NAME + ' WHERE key="' + eURI(sKey) + '" AND location="' + eURI(this._location) + '"'),
934                                 value = '';
935
936                         try {
937                                 while (rs.isValidRow()) {
938                                         value += rs.field(0);
939                                         rs.next();
940                                 }
941                         } finally {
942                                 rs.close();
943                         }
944
945                         return value ? dURI(value) : null;
946                 },
947
948                 /*
949                  * Implementation to remove an item from the storage engine.
950                  * @see YAHOO.util.Storage._removeItem
951                  */
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');
957                 },
958
959                 /*
960                  * Implementation to remove an item from the storage engine.
961                  * @see YAHOO.util.Storage._setItem
962                  */
963                 _setItem: function(sKey, oData) {
964
965                         this._addKey(sKey);
966
967                         var sEscapedKey = eURI(sKey),
968                                 sEscapedLocation = eURI(this._location),
969                                 sEscapedValue = eURI(oData), // escaped twice, maybe not necessary
970                                 aValues = [],
971                                 nLen = SQL_STMT_LIMIT - (sEscapedKey + sEscapedLocation).length,
972                 i=0, j;
973
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));
978                                 }
979                         } else {
980                                 aValues.push(sEscapedValue);
981                         }
982
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] + '")');
988                         }
989                         _driver.execute('COMMIT');
990                         
991                         return true;
992                 }
993         });
994
995         // releases the engine when the page unloads
996         Util.Event.on('unload', function() {
997                 if (_driver) {_driver.close();}
998         });
999
1000     StorageEngineGears.ENGINE_NAME = 'gears';
1001         StorageEngineGears.GEARS = 'beta.database';
1002         StorageEngineGears.DATABASE = 'yui.database';
1003
1004         StorageEngineGears.isAvailable = function() {
1005                 if (('google' in window) && ('gears' in window.google)) {
1006                         try {
1007                                 // this will throw an exception if the user denies gears
1008                                 google.gears.factory.create(StorageEngineGears.GEARS);
1009                                 return true;
1010                         }
1011                         catch (e) {
1012                                 // no need to do anything
1013                         }
1014                 }
1015
1016                 return false;
1017         };
1018
1019     Util.StorageManager.register(StorageEngineGears);
1020         Util.StorageEngineGears = StorageEngineGears;
1021 }());
1022 /*
1023  * SWF limitation:
1024  *      - only 100,000 bytes of data may be stored this way
1025  *  - data is publicly available on user machine
1026  *
1027  * Thoughts:
1028  *  - data can be shared across browsers
1029  *  - how can we not use cookies to handle session location
1030  */
1031 (function() {
1032         // internal shorthand
1033 var Y = YAHOO,
1034     Util = Y.util,
1035         Lang = Y.lang,
1036         Dom = Util.Dom,
1037         StorageManager = Util.StorageManager,
1038         
1039         /*
1040          * The minimum width required to be able to display the settings panel within the SWF.
1041          */     
1042         MINIMUM_WIDTH = 215,
1043
1044         /*
1045          * The minimum height required to be able to display the settings panel within the SWF.
1046          */     
1047         MINIMUM_HEIGHT = 138,
1048
1049         RX_STORAGE_PREFIX = new RegExp('^(' + StorageManager.LOCATION_SESSION + '|' + StorageManager.LOCATION_LOCAL + ')'),
1050
1051         // local variables
1052         _driver = null,
1053
1054         /*
1055          * Creates a location bound key.
1056          */
1057         _getKey = function(that, sKey) {
1058                 return that._location + sKey;
1059         },
1060
1061         /*
1062          * Initializes the engine, if it isn't already initialized.
1063          */
1064         _initDriver = function(oCfg) {
1065                 if (! _driver) {
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);
1071                         }
1072
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);
1079
1080                         // subscribe to save for info
1081                         _driver.subscribe('save', function(o) {
1082                                 Y.log(o.message, 'info');
1083                         });
1084
1085                         // subscribe to errors
1086                         _driver.subscribe('quotaExceededError', function(o) {
1087                                 Y.log(o.message, 'error');
1088                         });
1089                         _driver.subscribe('inadequateDimensions', function(o) {
1090                                 Y.log(o.message, 'error');
1091                         });
1092                         _driver.subscribe('error', function(o) {
1093                                 Y.log(o.message, 'error');
1094                         });
1095                         _driver.subscribe('securityError', function(o) {
1096                                 Y.log(o.message, 'error');
1097                         });
1098                 }
1099         },
1100
1101         /**
1102          * The StorageEngineSWF class implements the SWF storage engine.
1103          * @namespace YAHOO.util
1104          * @class StorageEngineSWF
1105          * @uses YAHOO.widget.SWF
1106          * @constructor
1107          * @extend YAHOO.util.Storage
1108          * @param sLocation {String} Required. The storage location.
1109          * @param oConf {Object} Required. A configuration object.
1110          */
1111         StorageEngineSWF = function(sLocation, oConf) {
1112                 var that = this;
1113                 StorageEngineSWF.superclass.constructor.call(that, sLocation, StorageEngineSWF.ENGINE_NAME, oConf);
1114                 
1115                 _initDriver(that._cfg);
1116                 
1117                 var _onContentReady = function() {
1118                         that._swf = _driver._swf;
1119                         _driver.initialized = true;
1120                         
1121                         var isSessionStorage = StorageManager.LOCATION_SESSION === that._location,
1122                                 sessionKey = Util.Cookie.get('sessionKey' + StorageEngineSWF.ENGINE_NAME),
1123                 i, key, isKeySessionStorage;
1124
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));
1128
1129                                 // this is session storage, but the session key is not set, so remove item
1130                                 if (isKeySessionStorage && ! sessionKey) {
1131                                         _driver.callSWF("removeItem", [key]);
1132                                 }
1133                                 else if (isSessionStorage === isKeySessionStorage) {
1134                     // the key matches the storage type, add to key collection
1135                                         that._addKey(key);
1136                                 }
1137                         }
1138
1139                         // this is session storage, ensure that the session key is set
1140                         if (isSessionStorage) {
1141                                 Util.Cookie.set('sessionKey' + StorageEngineSWF.ENGINE_NAME, true);
1142                         }
1143
1144                         that.fireEvent(Util.Storage.CE_READY);
1145                 };
1146                 
1147                 // evaluate immediately, SWF is already loaded
1148                 if (_driver.initialized) {
1149             _onContentReady();
1150                 }
1151                 else {
1152             // evaluates when the SWF is loaded
1153                         _driver.addListener("contentReady", _onContentReady);
1154                 }
1155         };
1156
1157         Lang.extend(StorageEngineSWF, Util.StorageEngineKeyed, {
1158                 /**
1159                  * The underlying SWF of the engine, exposed so developers can modify the adapter behavior.
1160                  * @property _swf
1161                  * @type {Object}
1162                  * @protected
1163                  */
1164                 _swf: null,
1165
1166                 /*
1167                  * Implementation to clear the values from the storage engine.
1168                  * @see YAHOO.util.Storage._clear
1169                  */
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]);
1174                         }
1175                         // since keys are used to clear, we call the super function second
1176                         StorageEngineSWF.superclass._clear.call(this);
1177                 },
1178
1179                 /*
1180                  * Implementation to fetch an item from the storage engine.
1181                  * @see YAHOO.util.Storage._getItem
1182                  */
1183                 _getItem: function(sKey) {
1184                         var sLocationKey = _getKey(this, sKey);
1185                         return _driver.callSWF("getValueOf", [sLocationKey]);
1186                 },
1187
1188                 /*
1189                  * Implementation to fetch a key from the storage engine.
1190                  * @see YAHOO.util.Storage.key
1191                  */
1192                 _key: function(index) {
1193                         return StorageEngineSWF.superclass._key.call(this, index).replace(RX_STORAGE_PREFIX, '');
1194                 },
1195
1196                 /*
1197                  * Implementation to remove an item from the storage engine.
1198                  * @see YAHOO.util.Storage._removeItem
1199                  */
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]);
1205                 },
1206
1207                 /*
1208                  * Implementation to remove an item from the storage engine.
1209                  * @see YAHOO.util.Storage._setItem
1210                  */
1211                 _setItem: function(sKey, oData) {
1212                         var sLocationKey = _getKey(this, sKey), swfNode;
1213
1214                         if (_driver.callSWF("setItem", [sLocationKey, oData])) {
1215                                 this._addKey(sLocationKey);
1216                                 return true;
1217                         }
1218                         else {
1219                 /*
1220                     note:
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?
1223                  */
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", []);
1229                         }
1230                 }
1231         });
1232
1233         StorageEngineSWF.SWFURL = "swfstore.swf";
1234         StorageEngineSWF.ENGINE_NAME = 'swf';
1235
1236     StorageEngineSWF.isAvailable = function() {
1237                 return (6 <= Y.env.ua.flash && Y.widget.SWF);
1238         };
1239
1240     StorageManager.register(StorageEngineSWF);
1241         Util.StorageEngineSWF = StorageEngineSWF;
1242 }());
1243 YAHOO.register("storage", YAHOO.util.Storage, {version: "2.9.0", build: "2800"});