diff options
Diffstat (limited to 'includes/js/dojox/off/files.js')
-rw-r--r-- | includes/js/dojox/off/files.js | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/includes/js/dojox/off/files.js b/includes/js/dojox/off/files.js new file mode 100644 index 0000000..6c19ea0 --- /dev/null +++ b/includes/js/dojox/off/files.js @@ -0,0 +1,454 @@ +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 + // loading + this._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 offline + console.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 remove + for(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 check + for(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(dojo.config.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="+dojo.config.isDebug); + if(dojo.config.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 fast + dojox.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 imports + dojo.forEach(document.styleSheets, function(sheet){ + try{ + if(sheet.cssRules){ // Firefox + dojo.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){ // IE + var 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 it + var 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 anchors + if(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 right + url = new dojo._Url(url); + + // totally relative -- ../../someFile.html + if(!url.scheme && !url.port && !url.host){ + return true; + } + + // scheme relative with port specified -- brad.com:8080 + if(!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.com + if(!url.scheme && url.host && !url.port + && window.location.hostname == url.host + && window.location.port == 80){ + return true; + } + + // else we have everything + return 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 server + var 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, "_"); + + // clip at 64 characters, the max length of a resource store name + if(storeName.length >= 64){ + storeName = storeName.substring(0, 63); + } + + // refresh everything by simply removing + // any older stores + localServer.removeStore(storeName); + + // open/create the resource store + localServer.openStore(storeName); + var store = localServer.createStore(storeName); + this._store = store; + + // add our list of files to capture + var 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", dojo.config.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 page + if(data){ + newVersion = data; + } + + callback(oldVersion, newVersion, justDebugged); + } + }); + } +} + +} |