Blame | Last modification | View Log | RSS feed
if(!dojo._hasResource["dojox.off.files"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.dojo._hasResource["dojox.off.files"] = true;dojo.provide("dojox.off.files");// Author: Brad Neuberg, bkn3@columbia.edu, http://codinginparadise.org// summary:// Helps maintain resources that should be// available offline, such as CSS files.// description:// dojox.off.files makes it easy to indicate// what resources should be available offline,// such as CSS files, JavaScript, HTML, etc.dojox.off.files = {// versionURL: String// An optional file, that if present, records the version// of our bundle of files to make available offline. If this// file is present, and we are not currently debugging,// then we only refresh our offline files if the version has// changed.versionURL: "version.js",// listOfURLs: Array// For advanced usage; most developers can ignore this.// Our list of URLs that will be cached and made available// offline.listOfURLs: [],// refreshing: boolean// For advanced usage; most developers can ignore this.// Whether we are currently in the middle// of refreshing our list of offline files.refreshing: false,_cancelID: null,_error: false,_errorMessages: [],_currentFileIndex: 0,_store: null,_doSlurp: false,slurp: function(){// summary:// Autoscans the page to find all resources to// cache. This includes scripts, images, CSS, and hyperlinks// to pages that are in the same scheme/port/host as this// page. We also scan the embedded CSS of any stylesheets// to find @import statements and url()'s.// You should call this method from the top-level, outside of// any functions and before the page loads://// <script>// dojo.require("dojox.sql");// dojo.require("dojox.off");// dojo.require("dojox.off.ui");// dojo.require("dojox.off.sync");//// // configure how we should work offline//// // set our application name// dojox.off.ui.appName = "Moxie";//// // automatically "slurp" the page and// // capture the resources we need offline// dojox.off.files.slurp();//// // tell Dojo Offline we are ready for it to initialize itself now// // that we have finished configuring it for our application// dojox.off.initialize();// </script>//// Note that inline styles on elements are not handled (i.e.// if you somehow have an inline style that uses a URL);// object and embed tags are not scanned since their format// differs based on type; and elements created by JavaScript// after page load are not found. For these you must manually// add them with a dojox.off.files.cache() method call.// just schedule the slurp once the page is loaded and// Dojo Offline is ready to slurp; dojox.off will call// our _slurp() method before indicating it is finished// loadingthis._doSlurp = true;},cache: function(urlOrList){ /* void */// summary:// Caches a file or list of files to be available offline. This// can either be a full URL, such as http://foobar.com/index.html,// or a relative URL, such as ../index.html. This URL is not// actually cached until dojox.off.sync.synchronize() is called.// urlOrList: String or Array[]// A URL of a file to cache or an Array of Strings of files to// cache//console.debug("dojox.off.files.cache, urlOrList="+urlOrList);if(dojo.isString(urlOrList)){var url = this._trimAnchor(urlOrList+"");if(!this.isAvailable(url)){this.listOfURLs.push(url);}}else if(urlOrList instanceof dojo._Url){var url = this._trimAnchor(urlOrList.uri);if(!this.isAvailable(url)){this.listOfURLs.push(url);}}else{dojo.forEach(urlOrList, function(url){url = this._trimAnchor(url);if(!this.isAvailable(url)){this.listOfURLs.push(url);}}, this);}},printURLs: function(){// summary:// A helper function that will dump and print out// all of the URLs that are cached for offline// availability. This can help with debugging if you// are trying to make sure that all of your URLs are// available offlineconsole.debug("The following URLs are cached for offline use:");dojo.forEach(this.listOfURLs, function(i){console.debug(i);});},remove: function(url){ /* void */// summary:// Removes a URL from the list of files to cache.// description:// Removes a URL from the list of URLs to cache. Note that this// does not actually remove the file from the offline cache;// instead, it just prevents us from refreshing this file at a// later time, so that it will naturally time out and be removed// from the offline cache// url: String// The URL to removefor(var i = 0; i < this.listOfURLs.length; i++){if(this.listOfURLs[i] == url){this.listOfURLs = this.listOfURLs.splice(i, 1);break;}}},isAvailable: function(url){ /* boolean */// summary:// Determines whether the given resource is available offline.// url: String// The URL to checkfor(var i = 0; i < this.listOfURLs.length; i++){if(this.listOfURLs[i] == url){return true;}}return false;},refresh: function(callback){ /* void *///console.debug("dojox.off.files.refresh");// summary:// For advanced usage; most developers can ignore this.// Refreshes our list of offline resources,// making them available offline.// callback: Function// A callback that receives two arguments: whether an error// occurred, which is a boolean; and an array of error message strings// with details on errors encountered. If no error occured then message is// empty array with length 0.try{if(djConfig.isDebug){this.printURLs();}this.refreshing = true;if(this.versionURL){this._getVersionInfo(function(oldVersion, newVersion, justDebugged){//console.warn("getVersionInfo, oldVersion="+oldVersion+", newVersion="+newVersion// + ", justDebugged="+justDebugged+", isDebug="+djConfig.isDebug);if(djConfig.isDebug || !newVersion || justDebugged|| !oldVersion || oldVersion != newVersion){console.warn("Refreshing offline file list");this._doRefresh(callback, newVersion);}else{console.warn("No need to refresh offline file list");callback(false, []);}});}else{console.warn("Refreshing offline file list");this._doRefresh(callback);}}catch(e){this.refreshing = false;// can't refresh files -- core operation --// fail fastdojox.off.coreOpFailed = true;dojox.off.enabled = false;dojox.off.onFrameworkEvent("coreOperationFailed");}},abortRefresh: function(){// summary:// For advanced usage; most developers can ignore this.// Aborts and cancels a refresh.if(!this.refreshing){return;}this._store.abortCapture(this._cancelID);this.refreshing = false;},_slurp: function(){if(!this._doSlurp){return;}var handleUrl = dojo.hitch(this, function(url){if(this._sameLocation(url)){this.cache(url);}});handleUrl(window.location.href);dojo.query("script").forEach(function(i){try{handleUrl(i.getAttribute("src"));}catch(exp){//console.debug("dojox.off.files.slurp 'script' error: "// + exp.message||exp);}});dojo.query("link").forEach(function(i){try{if(!i.getAttribute("rel")|| i.getAttribute("rel").toLowerCase() != "stylesheet"){return;}handleUrl(i.getAttribute("href"));}catch(exp){//console.debug("dojox.off.files.slurp 'link' error: "// + exp.message||exp);}});dojo.query("img").forEach(function(i){try{handleUrl(i.getAttribute("src"));}catch(exp){//console.debug("dojox.off.files.slurp 'img' error: "// + exp.message||exp);}});dojo.query("a").forEach(function(i){try{handleUrl(i.getAttribute("href"));}catch(exp){//console.debug("dojox.off.files.slurp 'a' error: "// + exp.message||exp);}});// FIXME: handle 'object' and 'embed' tag// parse our style sheets for inline URLs and importsdojo.forEach(document.styleSheets, function(sheet){try{if(sheet.cssRules){ // Firefoxdojo.forEach(sheet.cssRules, function(rule){var text = rule.cssText;if(text){var matches = text.match(/url\(\s*([^\) ]*)\s*\)/i);if(!matches){return;}for(var i = 1; i < matches.length; i++){handleUrl(matches[i])}}});}else if(sheet.cssText){ // IEvar matches;var text = sheet.cssText.toString();// unfortunately, using RegExp.exec seems to be flakey// for looping across multiple lines on IE using the// global flag, so we have to simulate itvar lines = text.split(/\f|\r|\n/);for(var i = 0; i < lines.length; i++){matches = lines[i].match(/url\(\s*([^\) ]*)\s*\)/i);if(matches && matches.length){handleUrl(matches[1]);}}}}catch(exp){//console.debug("dojox.off.files.slurp stylesheet parse error: "// + exp.message||exp);}});//this.printURLs();},_sameLocation: function(url){if(!url){ return false; }// filter out anchorsif(url.length && url.charAt(0) == "#"){return false;}// FIXME: dojo._Url should be made public;// it's functionality is very useful for// parsing URLs correctly, which is hard to// do righturl = new dojo._Url(url);// totally relative -- ../../someFile.htmlif(!url.scheme && !url.port && !url.host){return true;}// scheme relative with port specified -- brad.com:8080if(!url.scheme && url.host && url.port&& window.location.hostname == url.host&& window.location.port == url.port){return true;}// scheme relative with no-port specified -- brad.comif(!url.scheme && url.host && !url.port&& window.location.hostname == url.host&& window.location.port == 80){return true;}// else we have everythingreturn window.location.protocol == (url.scheme + ":")&& window.location.hostname == url.host&& (window.location.port == url.port || !window.location.port && !url.port);},_trimAnchor: function(url){return url.replace(/\#.*$/, "");},_doRefresh: function(callback, newVersion){// get our local servervar localServer;try{localServer = google.gears.factory.create("beta.localserver", "1.0");}catch(exp){dojo.setObject("google.gears.denied", true);dojox.off.onFrameworkEvent("coreOperationFailed");throw "Google Gears must be allowed to run";}var storeName = "dot_store_"+ window.location.href.replace(/[^0-9A-Za-z_]/g, "_");// refresh everything by simply removing// any older storeslocalServer.removeStore(storeName);// open/create the resource storelocalServer.openStore(storeName);var store = localServer.createStore(storeName);this._store = store;// add our list of files to capturevar self = this;this._currentFileIndex = 0;this._cancelID = store.capture(this.listOfURLs, function(url, success, captureId){//console.debug("store.capture, url="+url+", success="+success);if(!success && self.refreshing){self._cancelID = null;self.refreshing = false;var errorMsgs = [];errorMsgs.push("Unable to capture: " + url);callback(true, errorMsgs);return;}else if(success){self._currentFileIndex++;}if(success && self._currentFileIndex >= self.listOfURLs.length){self._cancelID = null;self.refreshing = false;if(newVersion){dojox.storage.put("oldVersion", newVersion, null,dojox.off.STORAGE_NAMESPACE);}dojox.storage.put("justDebugged", djConfig.isDebug, null,dojox.off.STORAGE_NAMESPACE);callback(false, []);}});},_getVersionInfo: function(callback){var justDebugged = dojox.storage.get("justDebugged",dojox.off.STORAGE_NAMESPACE);var oldVersion = dojox.storage.get("oldVersion",dojox.off.STORAGE_NAMESPACE);var newVersion = null;callback = dojo.hitch(this, callback);dojo.xhrGet({url: this.versionURL + "?browserbust=" + new Date().getTime(),timeout: 5 * 1000,handleAs: "javascript",error: function(err){//console.warn("dojox.off.files._getVersionInfo, err=",err);dojox.storage.remove("oldVersion", dojox.off.STORAGE_NAMESPACE);dojox.storage.remove("justDebugged", dojox.off.STORAGE_NAMESPACE);callback(oldVersion, newVersion, justDebugged);},load: function(data){//console.warn("dojox.off.files._getVersionInfo, load=",data);// some servers incorrectly return 404's// as a real pageif(data){newVersion = data;}callback(oldVersion, newVersion, justDebugged);}});}}}