Storage.as 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. import DojoExternalInterface;
  2. class Storage {
  3. public static var SUCCESS = "success";
  4. public static var FAILED = "failed";
  5. public static var PENDING = "pending";
  6. // Wait the following number of milliseconds before flushing
  7. public static var FLUSH_DELAY_DEFAULT = 500;
  8. public var flush_delay;
  9. public var so;
  10. public var timer;
  11. private var _NAMESPACE_KEY = "allNamespaces";
  12. public function Storage(){
  13. flush_delay = Storage.FLUSH_DELAY_DEFAULT;
  14. //getURL("javascript:console.debug('FLASH:Storage constructor')");
  15. DojoExternalInterface.initialize();
  16. DojoExternalInterface.addCallback("put", this, put);
  17. DojoExternalInterface.addCallback("putMultiple", this, putMultiple);
  18. DojoExternalInterface.addCallback("get", this, get);
  19. DojoExternalInterface.addCallback("getMultiple", this, getMultiple);
  20. DojoExternalInterface.addCallback("showSettings", this, showSettings);
  21. DojoExternalInterface.addCallback("clear", this, clear);
  22. DojoExternalInterface.addCallback("getKeys", this, getKeys);
  23. DojoExternalInterface.addCallback("getNamespaces", this, getNamespaces);
  24. DojoExternalInterface.addCallback("remove", this, remove);
  25. DojoExternalInterface.addCallback("removeMultiple", this, removeMultiple);
  26. DojoExternalInterface.addCallback("flush", this, flush);
  27. DojoExternalInterface.addCallback("setFlushDelay", this, setFlushDelay);
  28. DojoExternalInterface.addCallback("getFlushDelay", this, getFlushDelay);
  29. DojoExternalInterface.loaded();
  30. // preload the System Settings finished button movie for offline
  31. // access so it is in the cache
  32. _root.createEmptyMovieClip("_settingsBackground", 1);
  33. // getURL("javascript:alert('"+DojoExternalInterface.dojoPath+"');");
  34. _root._settingsBackground.loadMovie(DojoExternalInterface.dojoPath + "storage_dialog.swf");
  35. }
  36. // Set a new value for the flush delay timer.
  37. // Possible values:
  38. // 0 : Perform the flush synchronously after each "put" request
  39. // > 0 : Wait until 'newDelay' ms have passed without any "put" request to flush
  40. // -1 : Do not automatically flush
  41. public function setFlushDelay(newDelay){
  42. flush_delay = Number(newDelay);
  43. }
  44. public function getFlushDelay(){
  45. return String(flush_delay);
  46. }
  47. public function flush(namespace){
  48. if(timer){
  49. _global.clearTimeout(timer);
  50. delete timer;
  51. }
  52. var so = SharedObject.getLocal(namespace);
  53. //var st = (new Date()).getTime();
  54. var flushResults = so.flush();
  55. //var end = (new Date()).getTime();
  56. //getURL("javascript:dojo.debug('FLASH: flush - not a word game - took " + (end - st) + "ms')");
  57. // return results of this command to JavaScript
  58. var statusResults;
  59. if(flushResults == true){
  60. statusResults = Storage.SUCCESS;
  61. }else if(flushResults == "pending"){
  62. statusResults = Storage.PENDING;
  63. }else{
  64. statusResults = Storage.FAILED;
  65. }
  66. DojoExternalInterface.call("dojox.storage._onStatus", null, statusResults, null);
  67. }
  68. // FIXME: This code has gotten ugly -- refactor
  69. public function put(keyName, keyValue, namespace){
  70. // Get the SharedObject for these values and save it
  71. so = SharedObject.getLocal(namespace);
  72. // Save the key and value
  73. so.data[keyName] = keyValue;
  74. // Do all the flush/no-flush stuff
  75. var keyNames = new Array(); keyNames[0] = keyName;
  76. postWrite( so, keyNames, namespace);
  77. }
  78. public function putMultiple(metaKey, metaValue, metaLengths, namespace){
  79. // Get the SharedObject for these values and save it
  80. so = SharedObject.getLocal(namespace);
  81. // Create array of keys and value lengths
  82. var keys = metaKey.split(",");
  83. var lengths = metaLengths.split(",");
  84. // Loop through the array and write the values
  85. for(var i=0;i<keys.length;i++){
  86. so.data[keys[i]] = metaValue.slice(0,lengths[i]);
  87. metaValue = metaValue.slice(lengths[i]);
  88. }
  89. // Do all the flush/no-flush stuff
  90. postWrite( so, keys, namespace);
  91. }
  92. public function postWrite( so, keyNames, namespace){
  93. // TODO: Review all this 'handler' stuff. In particular, the flush could now be with keys pending
  94. // from several different requests, not only the ones passed in this method call
  95. // prepare a storage status handler
  96. var self = this;
  97. so.onStatus = function(infoObject:Object){
  98. //getURL("javascript:console.debug('FLASH: onStatus, infoObject="+infoObject.code+"')");
  99. // delete the data value if the request was denied
  100. if(infoObject.code == "SharedObject.Flush.Failed"){
  101. for(var i=0;i<keyNames.length;i++){
  102. delete self.so.data[keyNames[i]];
  103. }
  104. }
  105. var statusResults;
  106. if(infoObject.code == "SharedObject.Flush.Failed"){
  107. statusResults = Storage.FAILED;
  108. }else if(infoObject.code == "SharedObject.Flush.Pending"){
  109. statusResults = Storage.PENDING;
  110. }else if(infoObject.code == "SharedObject.Flush.Success"){
  111. // if we have succeeded saving our value, see if we
  112. // need to update our list of namespaces
  113. if(self.hasNamespace(namespace) == true){
  114. statusResults = Storage.SUCCESS;
  115. }else{
  116. // we have a new namespace we must store
  117. self.addNamespace(namespace, keyNames[0]);
  118. return;
  119. }
  120. }
  121. //getURL("javascript:console.debug('FLASH: onStatus, statusResults="+statusResults+"')");
  122. // give the status results to JavaScript
  123. DojoExternalInterface.call("dojox.storage._onStatus", null, statusResults, keyNames[0]);
  124. }
  125. // Clear any pending flush timers
  126. if(timer){
  127. //getURL("javascript:dojo.debug('FLASH: clearing timer')");
  128. _global.clearTimeout( timer);
  129. }
  130. // If we have a flush delay set, set a timer for its execution
  131. if(flush_delay > 0){
  132. timer = _global.setTimeout( flush, flush_delay, namespace);
  133. // With a flush_delay value of 0, execute the flush request synchronously
  134. }else if(flush_delay == 0){
  135. //getURL("javascript:dojo.debug('FLASH: calling flush now')");
  136. flush(namespace);
  137. }
  138. // Otherwise just don't flush - will be probably be flushed manually
  139. }
  140. public function get(keyName, namespace){
  141. // Get the SharedObject for these values and save it
  142. so = SharedObject.getLocal(namespace);
  143. var results = so.data[keyName];
  144. return results;
  145. }
  146. // Returns an array with the contents of each key value on the metaKeys array
  147. public function getMultiple(metaKeys, namespace){
  148. // get the storage object
  149. so = SharedObject.getLocal(namespace);
  150. // Create array of keys to read
  151. var keys = metaKeys.split(",");
  152. var results = new Array();
  153. // Read from storage into results array
  154. for(var i=0;i<keys.length;i++){
  155. var val = so.data[keys[i]];
  156. val = val.split("\\").join("\\\\");
  157. val = val.split('"').join('\\"');
  158. results.push( val);
  159. }
  160. // Make the results array into a string
  161. var metaResults = '["' + results.join('","') + '"]';
  162. return metaResults;
  163. }
  164. public function showSettings(){
  165. // Show the configuration options for the Flash player, opened to the
  166. // section for local storage controls (pane 1)
  167. System.showSettings(1);
  168. // there is no way we can intercept when the Close button is pressed, allowing us
  169. // to hide the Flash dialog. Instead, we need to load a movie in the
  170. // background that we can show a close button on.
  171. _root.createEmptyMovieClip("_settingsBackground", 1);
  172. _root._settingsBackground.loadMovie(DojoExternalInterface.dojoPath + "storage_dialog.swf");
  173. }
  174. public function clear(namespace){
  175. so = SharedObject.getLocal(namespace);
  176. so.clear();
  177. so.flush();
  178. // remove this namespace entry now
  179. removeNamespace(namespace);
  180. }
  181. public function getKeys(namespace){
  182. // Returns a list of the available keys in this namespace
  183. // get the storage object
  184. so = SharedObject.getLocal(namespace);
  185. // get all of the keys
  186. var results = new Array();
  187. for(var i in so.data){
  188. results.push(i);
  189. }
  190. // remove our key that records our list of namespaces
  191. for(var i = 0; i < results.length; i++){
  192. if(results[i] == _NAMESPACE_KEY){
  193. results.splice(i, 1);
  194. break;
  195. }
  196. }
  197. // join the keys together in a comma seperated string
  198. results = results.join(",");
  199. return results;
  200. }
  201. public function getNamespaces(){
  202. var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY);
  203. var results = new Array();
  204. for(var i in allNamespaces.data){
  205. results.push(i);
  206. }
  207. return results.join(",");
  208. }
  209. public function remove(keyName, namespace){
  210. // Removes a key
  211. // get the storage object
  212. so = SharedObject.getLocal(namespace);
  213. // delete this value
  214. delete so.data[keyName];
  215. // save the changes
  216. so.flush();
  217. // see if we are the last entry for this namespace
  218. var availableKeys = getKeys(namespace);
  219. if(availableKeys == ""){
  220. // we are empty
  221. removeNamespace(namespace);
  222. }
  223. }
  224. // Removes all the values for each keys on the metaKeys array
  225. public function removeMultiple(metaKeys, namespace){
  226. // get the storage object
  227. so = SharedObject.getLocal(namespace);
  228. // Create array of keys to read
  229. var keys = metaKeys.split(",");
  230. var results = new Array();
  231. // Delete elements
  232. for(var i=0;i<keys.length;i++){
  233. delete so.data[keys[i]];
  234. }
  235. // see if there are no more entries for this namespace
  236. var availableKeys = getKeys(namespace);
  237. if(availableKeys == ""){
  238. // we are empty
  239. removeNamespace(namespace);
  240. }
  241. }
  242. private function hasNamespace(namespace):Boolean{
  243. // Get the SharedObject for the namespace list
  244. var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY);
  245. var results = false;
  246. for(var i in allNamespaces.data){
  247. if(i == namespace){
  248. results = true;
  249. break;
  250. }
  251. }
  252. return results;
  253. }
  254. // FIXME: This code has gotten ugly -- refactor
  255. private function addNamespace(namespace, keyName){
  256. if(hasNamespace(namespace) == true){
  257. return;
  258. }
  259. // Get the SharedObject for the namespace list
  260. var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY);
  261. // prepare a storage status handler if the keyName is
  262. // not null
  263. if(keyName != null && typeof keyName != "undefined"){
  264. var self = this;
  265. allNamespaces.onStatus = function(infoObject:Object){
  266. // delete the data value if the request was denied
  267. if(infoObject.code == "SharedObject.Flush.Failed"){
  268. delete self.so.data[keyName];
  269. }
  270. var statusResults;
  271. if(infoObject.code == "SharedObject.Flush.Failed"){
  272. statusResults = Storage.FAILED;
  273. }else if(infoObject.code == "SharedObject.Flush.Pending"){
  274. statusResults = Storage.PENDING;
  275. }else if(infoObject.code == "SharedObject.Flush.Success"){
  276. statusResults = Storage.SUCCESS;
  277. }
  278. // give the status results to JavaScript
  279. DojoExternalInterface.call("dojox.storage._onStatus", null, statusResults, keyName);
  280. }
  281. }
  282. // save the namespace list
  283. allNamespaces.data[namespace] = true;
  284. var flushResults = allNamespaces.flush();
  285. // return results of this command to JavaScript
  286. if(keyName != null && typeof keyName != "undefined"){
  287. var statusResults;
  288. if(flushResults == true){
  289. statusResults = Storage.SUCCESS;
  290. }else if(flushResults == "pending"){
  291. statusResults = Storage.PENDING;
  292. }else{
  293. statusResults = Storage.FAILED;
  294. }
  295. DojoExternalInterface.call("dojox.storage._onStatus", null, statusResults, keyName);
  296. }
  297. }
  298. // FIXME: This code has gotten ugly -- refactor
  299. private function removeNamespace(namespace){
  300. if(hasNamespace(namespace) == false){
  301. return;
  302. }
  303. // try to save the namespace list; don't have a return
  304. // callback; if we fail on this, the worst that will happen
  305. // is that we have a spurious namespace entry
  306. var allNamespaces = SharedObject.getLocal(_NAMESPACE_KEY);
  307. delete allNamespaces.data[namespace];
  308. allNamespaces.flush();
  309. }
  310. static function main(mc){
  311. //getURL("javascript:console.debug('FLASH: storage loaded')");
  312. _root.app = new Storage();
  313. }
  314. }