summaryrefslogtreecommitdiff
path: root/includes/js/dojox/off/files.js
diff options
context:
space:
mode:
Diffstat (limited to 'includes/js/dojox/off/files.js')
-rw-r--r--includes/js/dojox/off/files.js454
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);
+ }
+ });
+ }
+}
+
+}