Application cache loading status reporter for HTML5 / Javascript


/ Published in: JavaScript
Save to your folder(s)



Copy this code and paste it in your HTML
  1. /**
  2.  * HTML5 offline manifest preloader.
  3.  *
  4.  * Load all manifest cached entries, so that they are immediately available during the web app execution.
  5.  * Display some nice JQuery progress while loading.
  6.  *
  7.  * @copyright 2010 mFabrik Research
  8.  *
  9.  * @author Mikko Ohtamaa <[email protected]>
  10.  */
  11.  
  12. /**
  13.  * Preloader class constructor.
  14.  *
  15.  * Manifest is retrieved via HTTP GET and parsed.
  16.  * All cache entries are loaded using HTTP GET.
  17.  *
  18.  * Local storage attribute "preloaded" is used to check whether loading needs to be performed,
  19.  * as it is quite taxing operation.
  20.  *
  21.  * To debug this code and force retrieving of all manifest URLs, add reloaded=true HTTP GET query parameter:
  22.  *
  23.  *
  24.  *
  25.  * @param {Function} endCallback will be called when all offline entries are loaded
  26.  *
  27.  * @param {Object} progressMonitor ProgressMonitor object for which the status of the loading is reported.
  28.  */
  29. function ApplicationCachePreloader(endCallback, progressMonitor, debug) {
  30.  
  31. if(!progressMonitor) {
  32. throw "progressMonitor must be defined";
  33. }
  34.  
  35. this.endCallback = endCallback;
  36. this.progressMonitor = progressMonitor;
  37. this.logging = debug; // Flag to control console.log() output
  38.  
  39. this.eventCount = 0;
  40. }
  41.  
  42. ApplicationCachePreloader.prototype = {
  43.  
  44. translateEventCode : function(eventCode) {
  45. var cacheStatusValues = [];
  46. cacheStatusValues[0] = 'uncached';
  47. cacheStatusValues[1] = 'idle';
  48. cacheStatusValues[2] = 'checking';
  49. cacheStatusValues[3] = 'downloading';
  50. cacheStatusValues[4] = 'updateready';
  51. cacheStatusValues[5] = 'obsolete';
  52. return cacheStatusValues[eventCode];
  53. },
  54.  
  55. trapEvents : function(cache, handler) {
  56.  
  57. cache.addEventListener('progress', handler, false);
  58.  
  59. try {
  60. cache.addEventListener('cached', handler, false);
  61. } catch(e) {
  62. // FF 3.5 won't allow
  63. }
  64.  
  65. cache.addEventListener('checking', handler, false);
  66. cache.addEventListener('downloading', handler, false);
  67. cache.addEventListener('error', handler, false);
  68. cache.addEventListener('noupdate', handler, false);
  69. cache.addEventListener('obsolete', handler, false);
  70. cache.addEventListener('updateready', handler, false);
  71. },
  72.  
  73.  
  74. waitUntilAllowed : function() {
  75. try {
  76. cache.addEventListener('cached', handler, false);
  77. } catch(e) {
  78. // FF 3.5 needs user permission "Allow" in pop-up dialog
  79. this.debug("Not allowed yet");
  80. setTimeout(jQuery.proxy(this.waitUntilAllowed, this), 1000);
  81. }
  82. this.start();
  83. },
  84.  
  85. deferEndCallback : function(success) {
  86.  
  87. function timemout() {
  88. this.endCallback(success);
  89. }
  90.  
  91. setTimeout(jQuery.proxy(timemout, this), 100);
  92. },
  93.  
  94. /**
  95. * Do everything necessary to set-up offline application
  96. *
  97. * @return true if applicationCache loading performed succesfully
  98. */
  99. load : function() {
  100.  
  101. //alert("load");
  102.  
  103. if (window.applicationCache) {
  104. this.debug("ApplicationCache status " + window.applicationCache.status);
  105. this.debug("Please see http://www.w3.org/TR/html5/offline.html#applicationcache");
  106. } else {
  107. // This could be probably worked around on Google Chrome using Gears extension somehow
  108. this.silentError("applicationCache object not supported");
  109. this.deferEndCallback(true);
  110. return true;
  111. }
  112.  
  113. // Can't update
  114. if(!navigator.onLine) {
  115. this.debug("Not online");
  116. this.deferEndCallback(true);
  117. return true;
  118. }
  119.  
  120. if(jQuery.browser.mozilla) {
  121. // Mozilla won't work until user answers "Allow" to pop-up dialog,
  122. // put dialog seems to be broken
  123. this.deferEndCallback(true);
  124. return false;
  125. //this.waitUntilAllowed();
  126. } else {
  127. this.start();
  128. }
  129.  
  130. return true;
  131. },
  132.  
  133. start : function() {
  134.  
  135. //alert("Started");
  136.  
  137. this.debug("Preloading starting - xxxx");
  138.  
  139. this.progressMonitor.start();
  140.  
  141. this.trapEvents(window.applicationCache, jQuery.proxy(this.handleEvent, this));
  142. var cache = window.applicationCache;
  143.  
  144. if(cache.status == 4) {
  145. this.debug("Update complete");
  146. this.end(true);
  147. }
  148.  
  149. if (cache.status == 1) {
  150. this.debug("Idle - manifest not changed");
  151. this.end(true);
  152. }
  153.  
  154. if (cache.status == 0) {
  155. this.debug("Uncached - offline mode not enabled (prolly not saved on desktop/mainscreen and running straight from the browser)");
  156. this.end(true);
  157. }
  158.  
  159. //alert("Trapping");
  160. this.debug("Trapping and waiting events, status:"+ cache.status);
  161. },
  162.  
  163. end : function(success) {
  164. //alert("Ended:" + success);
  165.  
  166. // Trigger activation of new cache if available
  167. if(status) {
  168. if(!window.applicationCache) {
  169. // blaa... not supported
  170. } else {
  171. window.applicationCache.swapCache();
  172. }
  173.  
  174. }
  175.  
  176. this.debug("Preloading ending to " + success);
  177. this.progressMonitor.end(success);
  178. this.endCallback(success);
  179. },
  180.  
  181.  
  182. handleEvent : function(e) {
  183.  
  184. var online, status, type, message;
  185.  
  186. var cache = window.applicationCache;
  187.  
  188. online = (navigator.onLine) ? 'yes' : 'no';
  189. status = this.translateEventCode(cache.status);
  190. type = e.type;
  191. message = 'online: ' + online;
  192. message+= ', event: ' + type;
  193. message+= ', status: ' + status;
  194. if (type == 'error' && navigator.onLine) {
  195. message+= ' (prolly a syntax error in manifest)';
  196. }
  197.  
  198. // this.debug(message);
  199. // $("body").append("<p>" + message + "</p>");
  200.  
  201. this.eventCount++;
  202.  
  203. if(status == "downloading" || status == "checking") {
  204. this.progressMonitor.update(type, this.eventCount, this.eventCount);
  205. } else {
  206. console.log("Got event " + this.eventCount + " " + status);
  207. //alert("event:" + status);
  208. }
  209.  
  210. if(status == "error") {
  211. this.end(false);
  212. }
  213.  
  214. // Google Chrome reports "uncached" when the application offline
  215. // mode is not enabled - allow still to run in the browser
  216. // without doing resource caching
  217.  
  218. if(status == "cached" || status == "updateready" || status == "idle" || status == "uncached") {
  219. this.end(true);
  220. }
  221. },
  222.  
  223. /**
  224. * Write to debug console
  225. *
  226. * @param {String} msg
  227. */
  228. debug : function(msg) {
  229. if(this.logging) {
  230. console.log(msg);
  231. }
  232. },
  233.  
  234. /**
  235. * Non-end user visible error message
  236. *
  237. * @param {Object} msg
  238. */
  239. silentError : function(msg) {
  240. console.log(msg);
  241. }
  242. };
  243.  
  244. function ProgressMonitor() {
  245. };
  246.  
  247. ProgressMonitor.prototype = {
  248.  
  249. /**
  250. * Start progress bar... initialize as 0 / 0
  251. */
  252. start : function(status, coldVirgin) {
  253. $("#web-app-updating").show();
  254.  
  255. if(window.navigator.standlone) {
  256. $("#web-app-updating .standalone").show();
  257. }
  258. },
  259.  
  260. end: function(status) {
  261. $("#web-app-updating").hide();
  262. },
  263.  
  264. update : function(status, currentEntry, maxEntries) {
  265. $("#web-app-updating .event-count").text(currentEntry);
  266. },
  267.  
  268. /**
  269. * Called when application has not been saved on the desktop.
  270. *
  271. * ...problems with Mobile Safari cache size.
  272. */
  273. needWebAppMode : function() {
  274. $("#web-app-no-browser").show();
  275. }
  276. };

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.