mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-31 03:10:07 +00:00 
			
		
		
		
	minor edits...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									1a6f791111
								
							
						
					
					
						commit
						3bad5e56c5
					
				
							
								
								
									
										25
									
								
								ui/TODO.otl
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								ui/TODO.otl
									
									
									
									
									
								
							| @ -1,8 +1,15 @@ | ||||
| Priority work | ||||
| 	[_] 74% Preview II | ||||
| 	[_] 62% Preview II | ||||
| 		[_] 50% load folder | ||||
| 			[X] drag'n'drop | ||||
| 			[_] open dilog | ||||
| 		[_] 0% load state | ||||
| 			[_] auto: last state | ||||
| 			[_] manual | ||||
| 		[_] 0% save state | ||||
| 			[_] minomal: Local Storage | ||||
| 			[_] local JSON | ||||
| 			[_] minimal: Local Storage | ||||
| 			| XXX does not seem to work in CEF | ||||
| 			[_] local JSON (file) | ||||
| 			[_] CouchDB | ||||
| 		[_] 49% native client | ||||
| 			[_] 33% Windows (CEF / CEFPython) | ||||
| @ -127,14 +134,14 @@ Priority work | ||||
| 			[X] 100% actions | ||||
| 				[X] bug: shifting up to new ribbon pushes the current row down... | ||||
| 				| before starting on a fix, need to cleanup the code from old hacks and workarounds... | ||||
| 	[_] 29% Preview II (optional features) | ||||
| 	[_] 40% Preview II (optional features) | ||||
| 		[_] 0% PhoneGap + Android Issues: | ||||
| 			[_] scrolling in overlays does not work | ||||
| 			[_] half the keyboard is not working... | ||||
| 			[_] screen buttons are very laggy | ||||
| 			| while swipe works super fast... | ||||
| 			[_] .dblclick(...) does not work... | ||||
| 			[_] .dragable(...) does not work... | ||||
| 			[_] scrolling in overlays does not work | ||||
| 		[_] slideshow... | ||||
| 		[_] make keyboeard handler mode-aware... | ||||
| 		| this is needed to disable navigation keys in setup-mode, for example... | ||||
| @ -142,7 +149,7 @@ Priority work | ||||
| 			[X] JSON loader/unloader | ||||
| 			[_] file reader/writer | ||||
| 		[_] actual file loading (Target-specific) | ||||
| 		[_] flip ribbons relative to current -- reverse order | ||||
| 		[X] flip ribbons relative to current -- reverse order | ||||
| 		[_] fade transition in single image mode... | ||||
| 		[_] "show all promoted/demoted images" mode | ||||
| 		| display images below or above but in a more transparent  | ||||
| @ -174,8 +181,10 @@ Priority work | ||||
| 		[_] 50% return to current image / home (after drag) | ||||
| 			[_] button | ||||
| 			[X] keyboard | ||||
| 		[_] double tap/click to zoom (a-la iPad) | ||||
| 		| fit <-> actual pixels (max) | ||||
| 		[X] double tap/click to zoom (a-la iPad) | ||||
| 		| ribbon <-> single image mode | ||||
| 		| | ||||
| 		| XXX does not work in android... | ||||
| 		[_] pinch to zoom | ||||
| 		[X] BUG: rendering error when current ribbon images opacity is 1... | ||||
| 		| This happens when: | ||||
|  | ||||
| @ -29,6 +29,7 @@ body { | ||||
| 
 | ||||
| <script src="jquery.js"></script> | ||||
| <script src="jquery-ui-1.8.22.custom.min.js"></script> | ||||
| <script src="jstorage.js"></script> | ||||
| 
 | ||||
| <script src="jquery.touchSwipe.js"></script> | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										770
									
								
								ui/jstorage.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										770
									
								
								ui/jstorage.js
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,770 @@ | ||||
| /* | ||||
|  * ----------------------------- JSTORAGE ------------------------------------- | ||||
|  * Simple local storage wrapper to save data on the browser side, supporting | ||||
|  * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+ | ||||
|  * | ||||
|  * Copyright (c) 2010 - 2012 Andris Reinman, andris.reinman@gmail.com | ||||
|  * Project homepage: www.jstorage.info | ||||
|  * | ||||
|  * Licensed under MIT-style license: | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
|  (function(){ | ||||
|     var | ||||
|         /* jStorage version */ | ||||
|         JSTORAGE_VERSION = "0.2.3", | ||||
| 
 | ||||
|         /* detect a dollar object or create one if not found */ | ||||
|         $ = window.jQuery || window.$ || (window.$ = {}), | ||||
| 
 | ||||
|         /* check for a JSON handling support */ | ||||
|         JSON = { | ||||
|             parse: | ||||
|                 window.JSON && (window.JSON.parse || window.JSON.decode) || | ||||
|                 String.prototype.evalJSON && function(str){return String(str).evalJSON();} || | ||||
|                 $.parseJSON || | ||||
|                 $.evalJSON, | ||||
|             stringify: | ||||
|                 window.JSON && (window.JSON.stringify || window.JSON.encode) || | ||||
|                 Object.toJSON || | ||||
|                 $.toJSON | ||||
|         }; | ||||
| 
 | ||||
|     // Break if no JSON support was found
 | ||||
|     if(!JSON.parse || !JSON.stringify){ | ||||
|         throw new Error("No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page"); | ||||
|     } | ||||
| 
 | ||||
|     var | ||||
|         /* This is the object, that holds the cached values */ | ||||
|         _storage = {}, | ||||
| 
 | ||||
|         /* Actual browser storage (localStorage or globalStorage['domain']) */ | ||||
|         _storage_service = {jStorage:"{}"}, | ||||
| 
 | ||||
|         /* DOM element for older IE versions, holds userData behavior */ | ||||
|         _storage_elm = null, | ||||
| 
 | ||||
|         /* How much space does the storage take */ | ||||
|         _storage_size = 0, | ||||
| 
 | ||||
|         /* which backend is currently used */ | ||||
|         _backend = false, | ||||
| 
 | ||||
|         /* onchange observers */ | ||||
|         _observers = {}, | ||||
| 
 | ||||
|         /* timeout to wait after onchange event */ | ||||
|         _observerTimeout = false, | ||||
| 
 | ||||
|         /* last update time */ | ||||
|         _observerUpdate = 0, | ||||
| 
 | ||||
|         /* Next check for TTL */ | ||||
|         _ttl_timeout, | ||||
| 
 | ||||
|         /* crc32 table */ | ||||
|         _crc32Table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 "+ | ||||
|              "0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 "+ | ||||
|              "6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 "+ | ||||
|              "FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 "+ | ||||
|              "A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 "+ | ||||
|              "32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 "+ | ||||
|              "56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 "+ | ||||
|              "C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 "+ | ||||
|              "E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 "+ | ||||
|              "6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 "+ | ||||
|              "12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE "+ | ||||
|              "A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 "+ | ||||
|              "DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 "+ | ||||
|              "5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 "+ | ||||
|              "2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF "+ | ||||
|              "04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 "+ | ||||
|              "7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 "+ | ||||
|              "FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 "+ | ||||
|              "A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C "+ | ||||
|              "36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 "+ | ||||
|              "5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 "+ | ||||
|              "C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 "+ | ||||
|              "EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D "+ | ||||
|              "7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 "+ | ||||
|              "18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 "+ | ||||
|              "A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A "+ | ||||
|              "D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A "+ | ||||
|              "53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 "+ | ||||
|              "2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D", | ||||
| 
 | ||||
|         /** | ||||
|          * XML encoding and decoding as XML nodes can't be JSON'ized | ||||
|          * XML nodes are encoded and decoded if the node is the value to be saved | ||||
|          * but not if it's as a property of another object | ||||
|          * Eg. - | ||||
|          *   $.jStorage.set("key", xmlNode);        // IS OK
 | ||||
|          *   $.jStorage.set("key", {xml: xmlNode}); // NOT OK
 | ||||
|          */ | ||||
|         _XMLService = { | ||||
| 
 | ||||
|             /** | ||||
|              * Validates a XML node to be XML | ||||
|              * based on jQuery.isXML function | ||||
|              */ | ||||
|             isXML: function(elm){ | ||||
|                 var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement; | ||||
|                 return documentElement ? documentElement.nodeName !== "HTML" : false; | ||||
|             }, | ||||
| 
 | ||||
|             /** | ||||
|              * Encodes a XML node to string | ||||
|              * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
 | ||||
|              */ | ||||
|             encode: function(xmlNode) { | ||||
|                 if(!this.isXML(xmlNode)){ | ||||
|                     return false; | ||||
|                 } | ||||
|                 try{ // Mozilla, Webkit, Opera
 | ||||
|                     return new XMLSerializer().serializeToString(xmlNode); | ||||
|                 }catch(E1) { | ||||
|                     try {  // IE
 | ||||
|                         return xmlNode.xml; | ||||
|                     }catch(E2){} | ||||
|                 } | ||||
|                 return false; | ||||
|             }, | ||||
| 
 | ||||
|             /** | ||||
|              * Decodes a XML node from string | ||||
|              * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
 | ||||
|              */ | ||||
|             decode: function(xmlString){ | ||||
|                 var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) || | ||||
|                         (window.ActiveXObject && function(_xmlString) { | ||||
|                     var xml_doc = new ActiveXObject('Microsoft.XMLDOM'); | ||||
|                     xml_doc.async = 'false'; | ||||
|                     xml_doc.loadXML(_xmlString); | ||||
|                     return xml_doc; | ||||
|                 }), | ||||
|                 resultXML; | ||||
|                 if(!dom_parser){ | ||||
|                     return false; | ||||
|                 } | ||||
|                 resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml'); | ||||
|                 return this.isXML(resultXML)?resultXML:false; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|     ////////////////////////// PRIVATE METHODS ////////////////////////
 | ||||
| 
 | ||||
|     /** | ||||
|      * Initialization function. Detects if the browser supports DOM Storage | ||||
|      * or userData behavior and behaves accordingly. | ||||
|      */ | ||||
|     function _init(){ | ||||
|         /* Check if browser supports localStorage */ | ||||
|         var localStorageReallyWorks = false; | ||||
|         if("localStorage" in window){ | ||||
|             try { | ||||
|                 window.localStorage.setItem('_tmptest', 'tmpval'); | ||||
|                 localStorageReallyWorks = true; | ||||
|                 window.localStorage.removeItem('_tmptest'); | ||||
|             } catch(BogusQuotaExceededErrorOnIos5) { | ||||
|                 // Thanks be to iOS5 Private Browsing mode which throws
 | ||||
|                 // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
 | ||||
|             } | ||||
|         } | ||||
|         if(localStorageReallyWorks){ | ||||
|             try { | ||||
|                 if(window.localStorage) { | ||||
|                     _storage_service = window.localStorage; | ||||
|                     _backend = "localStorage"; | ||||
|                     _observerUpdate = _storage_service.jStorage_update; | ||||
|                 } | ||||
|             } catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */} | ||||
|         } | ||||
|         /* Check if browser supports globalStorage */ | ||||
|         else if("globalStorage" in window){ | ||||
|             try { | ||||
|                 if(window.globalStorage) { | ||||
|                     _storage_service = window.globalStorage[window.location.hostname]; | ||||
|                     _backend = "globalStorage"; | ||||
|                     _observerUpdate = _storage_service.jStorage_update; | ||||
|                 } | ||||
|             } catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */} | ||||
|         } | ||||
|         /* Check if browser supports userData behavior */ | ||||
|         else { | ||||
|             _storage_elm = document.createElement('link'); | ||||
|             if(_storage_elm.addBehavior){ | ||||
| 
 | ||||
|                 /* Use a DOM element to act as userData storage */ | ||||
|                 _storage_elm.style.behavior = 'url(#default#userData)'; | ||||
| 
 | ||||
|                 /* userData element needs to be inserted into the DOM! */ | ||||
|                 document.getElementsByTagName('head')[0].appendChild(_storage_elm); | ||||
| 
 | ||||
|                 _storage_elm.load("jStorage"); | ||||
| 
 | ||||
|                 var data = "{}"; | ||||
|                 try{ | ||||
|                     data = _storage_elm.getAttribute("jStorage"); | ||||
|                 }catch(E5){} | ||||
| 
 | ||||
|                 try{ | ||||
|                     _observerUpdate = _storage_elm.getAttribute("jStorage_update"); | ||||
|                 }catch(E6){} | ||||
| 
 | ||||
|                 _storage_service.jStorage = data; | ||||
|                 _backend = "userDataBehavior"; | ||||
|             }else{ | ||||
|                 _storage_elm = null; | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         _load_storage(); | ||||
| 
 | ||||
|         // remove dead keys
 | ||||
|         _handleTTL(); | ||||
| 
 | ||||
|         // start listening for changes
 | ||||
|         _setupObserver(); | ||||
|     } | ||||
| 
 | ||||
|     function _reloadData(){ | ||||
|         var data = "{}"; | ||||
| 
 | ||||
|         if(_backend == "userDataBehavior"){ | ||||
|             _storage_elm.load("jStorage"); | ||||
| 
 | ||||
|             try{ | ||||
|                 data = _storage_elm.getAttribute("jStorage"); | ||||
|             }catch(E5){} | ||||
| 
 | ||||
|             try{ | ||||
|                 _observerUpdate = _storage_elm.getAttribute("jStorage_update"); | ||||
|             }catch(E6){} | ||||
| 
 | ||||
|             _storage_service.jStorage = data; | ||||
|         } | ||||
| 
 | ||||
|         _load_storage(); | ||||
| 
 | ||||
|         // remove dead keys
 | ||||
|         _handleTTL(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets up a storage change observer | ||||
|      */ | ||||
|     function _setupObserver(){ | ||||
|         if(_backend == "localStorage" || _backend == "globalStorage"){ | ||||
|             if("addEventListener" in window){ | ||||
|                 window.addEventListener("storage", _storageObserver, false); | ||||
|             }else{ | ||||
|                 document.attachEvent("onstorage", _storageObserver); | ||||
|             } | ||||
|         }else if(_backend == "userDataBehavior"){ | ||||
|             setInterval(_storageObserver, 1000); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fired on any kind of data change, needs to check if anything has | ||||
|      * really been changed | ||||
|      */ | ||||
|     function _storageObserver(){ | ||||
|         var updateTime; | ||||
|         // cumulate change notifications with timeout
 | ||||
|         clearTimeout(_observerTimeout); | ||||
|         _observerTimeout = setTimeout(function(){ | ||||
| 
 | ||||
|             if(_backend == "localStorage" || _backend == "globalStorage"){ | ||||
|                 updateTime = _storage_service.jStorage_update; | ||||
|             }else if(_backend == "userDataBehavior"){ | ||||
|                 _storage_elm.load("jStorage"); | ||||
|                 try{ | ||||
|                     updateTime = _storage_elm.getAttribute("jStorage_update"); | ||||
|                 }catch(E5){} | ||||
|             } | ||||
| 
 | ||||
|             if(updateTime && updateTime != _observerUpdate){ | ||||
|                 _observerUpdate = updateTime; | ||||
|                 _checkUpdatedKeys(); | ||||
|             } | ||||
| 
 | ||||
|         }, 100); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Reloads the data and checks if any keys are changed | ||||
|      */ | ||||
|     function _checkUpdatedKeys(){ | ||||
|         var oldCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)), | ||||
|             newCrc32List; | ||||
| 
 | ||||
|         _reloadData(); | ||||
|         newCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)); | ||||
| 
 | ||||
|         var key, | ||||
|             updated = [], | ||||
|             removed = []; | ||||
| 
 | ||||
|         for(key in oldCrc32List){ | ||||
|             if(oldCrc32List.hasOwnProperty(key)){ | ||||
|                 if(!newCrc32List[key]){ | ||||
|                     removed.push(key); | ||||
|                     continue; | ||||
|                 } | ||||
|                 if(oldCrc32List[key] != newCrc32List[key]){ | ||||
|                     updated.push(key); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         for(key in newCrc32List){ | ||||
|             if(newCrc32List.hasOwnProperty(key)){ | ||||
|                 if(!oldCrc32List[key]){ | ||||
|                     updated.push(key); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         _fireObservers(updated, "updated"); | ||||
|         _fireObservers(removed, "deleted"); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fires observers for updated keys | ||||
|      * | ||||
|      * @param {Array|String} keys Array of key names or a key | ||||
|      * @param {String} action What happened with the value (updated, deleted, flushed) | ||||
|      */ | ||||
|     function _fireObservers(keys, action){ | ||||
|         keys = [].concat(keys || []); | ||||
|         if(action == "flushed"){ | ||||
|             keys = []; | ||||
|             for(var key in _observers){ | ||||
|                 if(_observers.hasOwnProperty(key)){ | ||||
|                     keys.push(key); | ||||
|                 } | ||||
|             } | ||||
|             action = "deleted"; | ||||
|         } | ||||
|         for(var i=0, len = keys.length; i<len; i++){ | ||||
|             if(_observers[keys[i]]){ | ||||
|                 for(var j=0, jlen = _observers[keys[i]].length; j<jlen; j++){ | ||||
|                     _observers[keys[i]][j](keys[i], action); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Publishes key change to listeners | ||||
|      */ | ||||
|     function _publishChange(){ | ||||
|         var updateTime = (+new Date()).toString(); | ||||
| 
 | ||||
|         if(_backend == "localStorage" || _backend == "globalStorage"){ | ||||
|             _storage_service.jStorage_update = updateTime; | ||||
|         }else if(_backend == "userDataBehavior"){ | ||||
|             _storage_elm.setAttribute("jStorage_update", updateTime); | ||||
|             _storage_elm.save("jStorage"); | ||||
|         } | ||||
| 
 | ||||
|         _storageObserver(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Loads the data from the storage based on the supported mechanism | ||||
|      */ | ||||
|     function _load_storage(){ | ||||
|         /* if jStorage string is retrieved, then decode it */ | ||||
|         if(_storage_service.jStorage){ | ||||
|             try{ | ||||
|                 _storage = JSON.parse(String(_storage_service.jStorage)); | ||||
|             }catch(E6){_storage_service.jStorage = "{}";} | ||||
|         }else{ | ||||
|             _storage_service.jStorage = "{}"; | ||||
|         } | ||||
|         _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0; | ||||
| 
 | ||||
|         if(!_storage.__jstorage_meta){ | ||||
|             _storage.__jstorage_meta = {}; | ||||
|         } | ||||
|         if(!_storage.__jstorage_meta.CRC32){ | ||||
|             _storage.__jstorage_meta.CRC32 = {}; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * This functions provides the "save" mechanism to store the jStorage object | ||||
|      */ | ||||
|     function _save(){ | ||||
|         try{ | ||||
|             _storage_service.jStorage = JSON.stringify(_storage); | ||||
|             // If userData is used as the storage engine, additional
 | ||||
|             if(_storage_elm) { | ||||
|                 _storage_elm.setAttribute("jStorage",_storage_service.jStorage); | ||||
|                 _storage_elm.save("jStorage"); | ||||
|             } | ||||
|             _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0; | ||||
|         }catch(E7){/* probably cache is full, nothing is saved this way*/} | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Function checks if a key is set and is string or numberic | ||||
|      * | ||||
|      * @param {String} key Key name | ||||
|      */ | ||||
|     function _checkKey(key){ | ||||
|         if(!key || (typeof key != "string" && typeof key != "number")){ | ||||
|             throw new TypeError('Key name must be string or numeric'); | ||||
|         } | ||||
|         if(key == "__jstorage_meta"){ | ||||
|             throw new TypeError('Reserved key name'); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Removes expired keys | ||||
|      */ | ||||
|     function _handleTTL(){ | ||||
|         var curtime, i, TTL, CRC32, nextExpire = Infinity, changed = false, deleted = []; | ||||
| 
 | ||||
|         clearTimeout(_ttl_timeout); | ||||
| 
 | ||||
|         if(!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != "object"){ | ||||
|             // nothing to do here
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         curtime = +new Date(); | ||||
|         TTL = _storage.__jstorage_meta.TTL; | ||||
| 
 | ||||
|         CRC32 = _storage.__jstorage_meta.CRC32; | ||||
|         for(i in TTL){ | ||||
|             if(TTL.hasOwnProperty(i)){ | ||||
|                 if(TTL[i] <= curtime){ | ||||
|                     delete TTL[i]; | ||||
|                     delete CRC32[i]; | ||||
|                     delete _storage[i]; | ||||
|                     changed = true; | ||||
|                     deleted.push(i); | ||||
|                 }else if(TTL[i] < nextExpire){ | ||||
|                     nextExpire = TTL[i]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // set next check
 | ||||
|         if(nextExpire != Infinity){ | ||||
|             _ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime); | ||||
|         } | ||||
| 
 | ||||
|         // save changes
 | ||||
|         if(changed){ | ||||
|             _save(); | ||||
|             _publishChange(); | ||||
|             _fireObservers(deleted, "deleted"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * CRC32 calculation based on http://noteslog.com/post/crc32-for-javascript/
 | ||||
|      * | ||||
|      * @param {String} str String to be hashed | ||||
|      * @param {Number} [crc] Last crc value in case of streams | ||||
|      */ | ||||
|     function _crc32(str, crc){ | ||||
|         crc = crc || 0; | ||||
| 
 | ||||
|         var n = 0, //a number between 0 and 255
 | ||||
|             x = 0; //an hex number
 | ||||
|   | ||||
|         crc = crc ^ (-1); | ||||
|         for(var i = 0, len = str.length; i < len; i++){ | ||||
|             n = (crc ^ str.charCodeAt(i)) & 0xFF; | ||||
|             x = "0x" + _crc32Table.substr(n * 9, 8); | ||||
|             crc = (crc >>> 8)^x; | ||||
|         } | ||||
|         return crc^(-1); | ||||
|     } | ||||
| 
 | ||||
|     ////////////////////////// PUBLIC INTERFACE /////////////////////////
 | ||||
| 
 | ||||
|     $.jStorage = { | ||||
|         /* Version number */ | ||||
|         version: JSTORAGE_VERSION, | ||||
| 
 | ||||
|         /** | ||||
|          * Sets a key's value. | ||||
|          * | ||||
|          * @param {String} key Key to set. If this value is not set or not | ||||
|          *              a string an exception is raised. | ||||
|          * @param {Mixed} value Value to set. This can be any value that is JSON | ||||
|          *              compatible (Numbers, Strings, Objects etc.). | ||||
|          * @param {Object} [options] - possible options to use | ||||
|          * @param {Number} [options.TTL] - optional TTL value | ||||
|          * @return {Mixed} the used value | ||||
|          */ | ||||
|         set: function(key, value, options){ | ||||
|             _checkKey(key); | ||||
| 
 | ||||
|             options = options || {}; | ||||
| 
 | ||||
|             // undefined values are deleted automatically
 | ||||
|             if(typeof value == "undefined"){ | ||||
|                 this.deleteKey(key); | ||||
|                 return value; | ||||
|             } | ||||
| 
 | ||||
|             if(_XMLService.isXML(value)){ | ||||
|                 value = {_is_xml:true,xml:_XMLService.encode(value)}; | ||||
|             }else if(typeof value == "function"){ | ||||
|                 return undefined; // functions can't be saved!
 | ||||
|             }else if(value && typeof value == "object"){ | ||||
|                 // clone the object before saving to _storage tree
 | ||||
|                 value = JSON.parse(JSON.stringify(value)); | ||||
|             } | ||||
|             _storage[key] = value; | ||||
| 
 | ||||
|             _storage.__jstorage_meta.CRC32[key] = _crc32(JSON.stringify(value)); | ||||
| 
 | ||||
|             this.setTTL(key, options.TTL || 0); // also handles saving and _publishChange
 | ||||
| 
 | ||||
|             _fireObservers(key, "updated"); | ||||
|             return value; | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Looks up a key in cache | ||||
|          * | ||||
|          * @param {String} key - Key to look up. | ||||
|          * @param {mixed} def - Default value to return, if key didn't exist. | ||||
|          * @return {Mixed} the key value, default value or null | ||||
|          */ | ||||
|         get: function(key, def){ | ||||
|             _checkKey(key); | ||||
|             if(key in _storage){ | ||||
|                 if(_storage[key] && typeof _storage[key] == "object" && | ||||
|                         _storage[key]._is_xml && | ||||
|                             _storage[key]._is_xml){ | ||||
|                     return _XMLService.decode(_storage[key].xml); | ||||
|                 }else{ | ||||
|                     return _storage[key]; | ||||
|                 } | ||||
|             } | ||||
|             return typeof(def) == 'undefined' ? null : def; | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Deletes a key from cache. | ||||
|          * | ||||
|          * @param {String} key - Key to delete. | ||||
|          * @return {Boolean} true if key existed or false if it didn't | ||||
|          */ | ||||
|         deleteKey: function(key){ | ||||
|             _checkKey(key); | ||||
|             if(key in _storage){ | ||||
|                 delete _storage[key]; | ||||
|                 // remove from TTL list
 | ||||
|                 if(typeof _storage.__jstorage_meta.TTL == "object" && | ||||
|                   key in _storage.__jstorage_meta.TTL){ | ||||
|                     delete _storage.__jstorage_meta.TTL[key]; | ||||
|                 } | ||||
| 
 | ||||
|                 delete _storage.__jstorage_meta.CRC32[key]; | ||||
| 
 | ||||
|                 _save(); | ||||
|                 _publishChange(); | ||||
|                 _fireObservers(key, "deleted"); | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Sets a TTL for a key, or remove it if ttl value is 0 or below | ||||
|          * | ||||
|          * @param {String} key - key to set the TTL for | ||||
|          * @param {Number} ttl - TTL timeout in milliseconds | ||||
|          * @return {Boolean} true if key existed or false if it didn't | ||||
|          */ | ||||
|         setTTL: function(key, ttl){ | ||||
|             var curtime = +new Date(); | ||||
|             _checkKey(key); | ||||
|             ttl = Number(ttl) || 0; | ||||
|             if(key in _storage){ | ||||
| 
 | ||||
|                 if(!_storage.__jstorage_meta.TTL){ | ||||
|                     _storage.__jstorage_meta.TTL = {}; | ||||
|                 } | ||||
| 
 | ||||
|                 // Set TTL value for the key
 | ||||
|                 if(ttl>0){ | ||||
|                     _storage.__jstorage_meta.TTL[key] = curtime + ttl; | ||||
|                 }else{ | ||||
|                     delete _storage.__jstorage_meta.TTL[key]; | ||||
|                 } | ||||
| 
 | ||||
|                 _save(); | ||||
| 
 | ||||
|                 _handleTTL(); | ||||
| 
 | ||||
|                 _publishChange(); | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Gets remaining TTL (in milliseconds) for a key or 0 when no TTL has been set | ||||
|          * | ||||
|          * @param {String} key Key to check | ||||
|          * @return {Number} Remaining TTL in milliseconds | ||||
|          */ | ||||
|         getTTL: function(key){ | ||||
|             var curtime = +new Date(), ttl; | ||||
|             _checkKey(key); | ||||
|             if(key in _storage && _storage.__jstorage_meta.TTL && _storage.__jstorage_meta.TTL[key]){ | ||||
|                 ttl = _storage.__jstorage_meta.TTL[key] - curtime; | ||||
|                 return ttl || 0; | ||||
|             } | ||||
|             return 0; | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Deletes everything in cache. | ||||
|          * | ||||
|          * @return {Boolean} Always true | ||||
|          */ | ||||
|         flush: function(){ | ||||
|             _storage = {__jstorage_meta:{CRC32:{}}}; | ||||
|             _save(); | ||||
|             _publishChange(); | ||||
|             _fireObservers(null, "flushed"); | ||||
|             return true; | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Returns a read-only copy of _storage | ||||
|          * | ||||
|          * @return {Object} Read-only copy of _storage | ||||
|         */ | ||||
|         storageObj: function(){ | ||||
|             function F() {} | ||||
|             F.prototype = _storage; | ||||
|             return new F(); | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Returns an index of all used keys as an array | ||||
|          * ['key1', 'key2',..'keyN'] | ||||
|          * | ||||
|          * @return {Array} Used keys | ||||
|         */ | ||||
|         index: function(){ | ||||
|             var index = [], i; | ||||
|             for(i in _storage){ | ||||
|                 if(_storage.hasOwnProperty(i) && i != "__jstorage_meta"){ | ||||
|                     index.push(i); | ||||
|                 } | ||||
|             } | ||||
|             return index; | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * How much space in bytes does the storage take? | ||||
|          * | ||||
|          * @return {Number} Storage size in chars (not the same as in bytes, | ||||
|          *                  since some chars may take several bytes) | ||||
|          */ | ||||
|         storageSize: function(){ | ||||
|             return _storage_size; | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Which backend is currently in use? | ||||
|          * | ||||
|          * @return {String} Backend name | ||||
|          */ | ||||
|         currentBackend: function(){ | ||||
|             return _backend; | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Test if storage is available | ||||
|          * | ||||
|          * @return {Boolean} True if storage can be used | ||||
|          */ | ||||
|         storageAvailable: function(){ | ||||
|             return !!_backend; | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Register change listeners | ||||
|          * | ||||
|          * @param {String} key Key name | ||||
|          * @param {Function} callback Function to run when the key changes | ||||
|          */ | ||||
|         listenKeyChange: function(key, callback){ | ||||
|             _checkKey(key); | ||||
|             if(!_observers[key]){ | ||||
|                 _observers[key] = []; | ||||
|             } | ||||
|             _observers[key].push(callback); | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Remove change listeners | ||||
|          * | ||||
|          * @param {String} key Key name to unregister listeners against | ||||
|          * @param {Function} [callback] If set, unregister the callback, if not - unregister all | ||||
|          */ | ||||
|         stopListening: function(key, callback){ | ||||
|             _checkKey(key); | ||||
| 
 | ||||
|             if(!_observers[key]){ | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             if(!callback){ | ||||
|                 delete _observers[key]; | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             for(var i = _observers[key].length - 1; i>=0; i--){ | ||||
|                 if(_observers[key][i] == callback){ | ||||
|                     _observers[key].splice(i,1); | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
| 
 | ||||
|         /** | ||||
|          * Reloads the data from browser storage | ||||
|          */ | ||||
|         reInit: function(){ | ||||
|             _reloadData(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     // Initialize jStorage
 | ||||
|     _init(); | ||||
| 
 | ||||
| })(); | ||||
| @ -1,10 +1,12 @@ | ||||
| /*********************************************************************/ | ||||
| // NOTE: use String.fromCharCode(code)...
 | ||||
| // list of keys to be ignored by handler...
 | ||||
| // list of keys to be ignored by handler but still handled by the browser...
 | ||||
| var ignorekeys = [ | ||||
| 	116,													//	F5
 | ||||
| 	123,													//	F12
 | ||||
| ] | ||||
| 
 | ||||
| 
 | ||||
| var keybindings = { | ||||
| 	// togglable modes and options...
 | ||||
| 	191: { | ||||
| @ -88,6 +90,9 @@ var keybindings = { | ||||
| 		'ctrl+shift': ImageGrid.shiftImageUpNewRibbon		//	ctrl-shift-Up
 | ||||
| 	}, | ||||
| 
 | ||||
| 	// misc actions...
 | ||||
| 	82:		ImageGrid.reverseImageOrder,					//	r
 | ||||
| 
 | ||||
| 
 | ||||
| 	// ignore the modifiers (shift, alt, ctrl, caps)...
 | ||||
| 	16:		function(){}, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user