diff options
Diffstat (limited to 'includes/js/dojox/off/_common.js')
-rw-r--r-- | includes/js/dojox/off/_common.js | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/includes/js/dojox/off/_common.js b/includes/js/dojox/off/_common.js new file mode 100644 index 0000000..005cd31 --- /dev/null +++ b/includes/js/dojox/off/_common.js @@ -0,0 +1,559 @@ +if(!dojo._hasResource["dojox.off._common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojox.off._common"] = true; +dojo.provide("dojox.off._common"); + +dojo.require("dojox.storage"); +dojo.require("dojox.sql"); +dojo.require("dojox.off.sync"); + +// Author: Brad Neuberg, bkn3@columbia.edu, http://codinginparadise.org + +// summary: +// dojox.off is the main object for offline applications. +dojo.mixin(dojox.off, { + // isOnline: boolean + // true if we are online, false if not + isOnline: false, + + // NET_CHECK: int + // For advanced usage; most developers can ignore this. + // Time in seconds on how often we should check the status of the + // network with an automatic background timer. The current default + // is 5 seconds. + NET_CHECK: 5, + + // STORAGE_NAMESPACE: String + // For advanced usage; most developers can ignore this. + // The namespace we use to save core data into Dojo Storage. + STORAGE_NAMESPACE: "_dot", + + // enabled: boolean + // For advanced usage; most developers can ignore this. + // Whether offline ability is enabled or not. Defaults to true. + enabled: true, + + // availabilityURL: String + // For advanced usage; most developers can ignore this. + // The URL to check for site availability. We do a GET request on + // this URL to check for site availability. By default we check for a + // simple text file in src/off/network_check.txt that has one value + // it, the value '1'. + availabilityURL: dojo.moduleUrl("dojox", "off/network_check.txt"), + + // goingOnline: boolean + // For advanced usage; most developers can ignore this. + // True if we are attempting to go online, false otherwise + goingOnline: false, + + // coreOpFailed: boolean + // For advanced usage; most developers can ignore this. + // A flag set by the Dojo Offline framework that indicates that the + // user denied some operation that required the offline cache or an + // operation failed in some critical way that was unrecoverable. For + // example, if the offline cache is Google Gears and we try to get a + // Gears database, a popup window appears asking the user whether they + // will approve or deny this request. If the user denies the request, + // and we are doing some operation that is core to Dojo Offline, then + // we set this flag to 'true'. This flag causes a 'fail fast' + // condition, turning off offline ability. + coreOpFailed: false, + + // doNetChecking: boolean + // For advanced usage; most developers can ignore this. + // Whether to have a timing interval in the background doing automatic + // network checks at regular intervals; the length of time between + // checks is controlled by dojox.off.NET_CHECK. Defaults to true. + doNetChecking: true, + + // hasOfflineCache: boolean + // For advanced usage; most developers can ignore this. + // Determines if an offline cache is available or installed; an + // offline cache is a facility that can truely cache offline + // resources, such as JavaScript, HTML, etc. in such a way that they + // won't be removed from the cache inappropriately like a browser + // cache would. If this is false then an offline cache will be + // installed. Only Google Gears is currently supported as an offline + // cache. Future possible offline caches include Firefox 3. + hasOfflineCache: null, + + // browserRestart: boolean + // For advanced usage; most developers can ignore this. + // If true, the browser must be restarted to register the existence of + // a new host added offline (from a call to addHostOffline); if false, + // then nothing is needed. + browserRestart: false, + + _STORAGE_APP_NAME: window.location.href.replace(/[^0-9A-Za-z_]/g, "_"), + + _initializeCalled: false, + _storageLoaded: false, + _pageLoaded: false, + + onLoad: function(){ + // summary: + // Called when Dojo Offline can be used. + // description: + // Do a dojo.connect to this to know when you can + // start using Dojo Offline: + // dojo.connect(dojox.off, "onLoad", myFunc); + }, + + onNetwork: function(type){ + // summary: + // Called when our on- or offline- status changes. + // description: + // If we move online, then this method is called with the + // value "online". If we move offline, then this method is + // called with the value "offline". You can connect to this + // method to do add your own behavior: + // + // dojo.connect(dojox.off, "onNetwork", someFunc) + // + // Note that if you are using the default Dojo Offline UI + // widget that most of the on- and off-line notification + // and syncing is automatically handled and provided to the + // user. + // type: String + // Either "online" or "offline". + }, + + initialize: function(){ /* void */ + // summary: + // Called when a Dojo Offline-enabled application is finished + // configuring Dojo Offline, and is ready for Dojo Offline to + // initialize itself. + // description: + // When an application has finished filling out the variables Dojo + // Offline needs to work, such as dojox.off.ui.appName, it must + // this method to tell Dojo Offline to initialize itself. + + // Note: + // This method is needed for a rare edge case. In some conditions, + // especially if we are dealing with a compressed Dojo build, the + // entire Dojo Offline subsystem might initialize itself and be + // running even before the JavaScript for an application has had a + // chance to run and configure Dojo Offline, causing Dojo Offline + // to have incorrect initialization parameters for a given app, + // such as no value for dojox.off.ui.appName. This method is + // provided to prevent this scenario, to slightly 'slow down' Dojo + // Offline so it can be configured before running off and doing + // its thing. + + //console.debug("dojox.off.initialize"); + this._initializeCalled = true; + + if(this._storageLoaded && this._pageLoaded){ + this._onLoad(); + } + }, + + goOffline: function(){ /* void */ + // summary: + // For advanced usage; most developers can ignore this. + // Manually goes offline, away from the network. + if((dojox.off.sync.isSyncing)||(this.goingOnline)){ return; } + + this.goingOnline = false; + this.isOnline = false; + }, + + goOnline: function(callback){ /* void */ + // summary: + // For advanced usage; most developers can ignore this. + // Attempts to go online. + // description: + // Attempts to go online, making sure this web application's web + // site is available. 'callback' is called asychronously with the + // result of whether we were able to go online or not. + // callback: Function + // An optional callback function that will receive one argument: + // whether the site is available or not and is boolean. If this + // function is not present we call dojo.xoff.onOnline instead if + // we are able to go online. + + //console.debug("goOnline"); + + if(dojox.off.sync.isSyncing || dojox.off.goingOnline){ + return; + } + + this.goingOnline = true; + this.isOnline = false; + + // see if can reach our web application's web site + this._isSiteAvailable(callback); + }, + + onFrameworkEvent: function(type /* String */, saveData /* Object? */){ + // summary: + // For advanced usage; most developers can ignore this. + // A standard event handler that can be attached to to find out + // about low-level framework events. Most developers will not need to + // attach to this method; it is meant for low-level information + // that can be useful for updating offline user-interfaces in + // exceptional circumstances. The default Dojo Offline UI + // widget takes care of most of these situations. + // type: String + // The type of the event: + // + // * "offlineCacheInstalled" + // An event that is fired when a user + // has installed an offline cache after the page has been loaded. + // If a user didn't have an offline cache when the page loaded, a + // UI of some kind might have prompted them to download one. This + // method is called if they have downloaded and installed an + // offline cache so a UI can reinitialize itself to begin using + // this offline cache. + // * "coreOperationFailed" + // Fired when a core operation during interaction with the + // offline cache is denied by the user. Some offline caches, such + // as Google Gears, prompts the user to approve or deny caching + // files, using the database, and more. If the user denies a + // request that is core to Dojo Offline's operation, we set + // dojox.off.coreOpFailed to true and call this method for + // listeners that would like to respond some how to Dojo Offline + // 'failing fast'. + // * "save" + // Called whenever the framework saves data into persistent + // storage. This could be useful for providing save feedback + // or providing appropriate error feedback if saving fails + // due to a user not allowing the save to occur + // saveData: Object? + // If the type was 'save', then a saveData object is provided with + // further save information. This object has the following properties: + // + // * status - dojox.storage.SUCCESS, dojox.storage.PENDING, dojox.storage.FAILED + // Whether the save succeeded, whether it is pending based on a UI + // dialog asking the user for permission, or whether it failed. + // + // * isCoreSave - boolean + // If true, then this save was for a core piece of data necessary + // for the functioning of Dojo Offline. If false, then it is a + // piece of normal data being saved for offline access. Dojo + // Offline will 'fail fast' if some core piece of data could not + // be saved, automatically setting dojox.off.coreOpFailed to + // 'true' and dojox.off.enabled to 'false'. + // + // * key - String + // The key that we are attempting to persist + // + // * value - Object + // The object we are trying to persist + // + // * namespace - String + // The Dojo Storage namespace we are saving this key/value pair + // into, such as "default", "Documents", "Contacts", etc. + // Optional. + if(type == "save"){ + if(saveData.isCoreSave && (saveData.status == dojox.storage.FAILED)){ + dojox.off.coreOpFailed = true; + dojox.off.enabled = false; + + // FIXME: Stop the background network thread + dojox.off.onFrameworkEvent("coreOperationFailed"); + } + }else if(type == "coreOperationFailed"){ + dojox.off.coreOpFailed = true; + dojox.off.enabled = false; + // FIXME: Stop the background network thread + } + }, + + _checkOfflineCacheAvailable: function(callback){ + // is a true, offline cache running on this machine? + this.hasOfflineCache = dojo.isGears; + + callback(); + }, + + _onLoad: function(){ + //console.debug("dojox.off._onLoad"); + + // both local storage and the page are finished loading + + // cache the Dojo JavaScript -- just use the default dojo.js + // name for the most common scenario + // FIXME: TEST: Make sure syncing doesn't break if dojo.js + // can't be found, or report an error to developer + dojox.off.files.cache(dojo.moduleUrl("dojo", "dojo.js")); + + // pull in the files needed by Dojo + this._cacheDojoResources(); + + // FIXME: need to pull in the firebug lite files here! + // workaround or else we will get an error on page load + // from Dojo that it can't find 'console.debug' for optimized builds + // dojox.off.files.cache(dojo.config.baseRelativePath + "src/debug.js"); + + // make sure that resources needed by all of our underlying + // Dojo Storage storage providers will be available + // offline + dojox.off.files.cache(dojox.storage.manager.getResourceList()); + + // slurp the page if the end-developer wants that + dojox.off.files._slurp(); + + // see if we have an offline cache; when done, move + // on to the rest of our startup tasks + this._checkOfflineCacheAvailable(dojo.hitch(this, "_onOfflineCacheChecked")); + }, + + _onOfflineCacheChecked: function(){ + // this method is part of our _onLoad series of startup tasks + + // if we have an offline cache, see if we have been added to the + // list of available offline web apps yet + if(this.hasOfflineCache && this.enabled){ + // load framework data; when we are finished, continue + // initializing ourselves + this._load(dojo.hitch(this, "_finishStartingUp")); + }else if(this.hasOfflineCache && !this.enabled){ + // we have an offline cache, but it is disabled for some reason + // perhaps due to the user denying a core operation + this._finishStartingUp(); + }else{ + this._keepCheckingUntilInstalled(); + } + }, + + _keepCheckingUntilInstalled: function(){ + // this method is part of our _onLoad series of startup tasks + + // kick off a background interval that keeps + // checking to see if an offline cache has been + // installed since this page loaded + + // FIXME: Gears: See if we are installed somehow after the + // page has been loaded + + // now continue starting up + this._finishStartingUp(); + }, + + _finishStartingUp: function(){ + //console.debug("dojox.off._finishStartingUp"); + + // this method is part of our _onLoad series of startup tasks + + if(!this.hasOfflineCache){ + this.onLoad(); + }else if(this.enabled){ + // kick off a thread to check network status on + // a regular basis + this._startNetworkThread(); + + // try to go online + this.goOnline(dojo.hitch(this, function(){ + //console.debug("Finished trying to go online"); + // indicate we are ready to be used + dojox.off.onLoad(); + })); + }else{ // we are disabled or a core operation failed + if(this.coreOpFailed){ + this.onFrameworkEvent("coreOperationFailed"); + }else{ + this.onLoad(); + } + } + }, + + _onPageLoad: function(){ + //console.debug("dojox.off._onPageLoad"); + this._pageLoaded = true; + + if(this._storageLoaded && this._initializeCalled){ + this._onLoad(); + } + }, + + _onStorageLoad: function(){ + //console.debug("dojox.off._onStorageLoad"); + this._storageLoaded = true; + + // were we able to initialize storage? if + // not, then this is a core operation, and + // let's indicate we will need to fail fast + if(!dojox.storage.manager.isAvailable() + && dojox.storage.manager.isInitialized()){ + this.coreOpFailed = true; + this.enabled = false; + } + + if(this._pageLoaded && this._initializeCalled){ + this._onLoad(); + } + }, + + _isSiteAvailable: function(callback){ + // summary: + // Determines if our web application's website is available. + // description: + // This method will asychronously determine if our web + // application's web site is available, which is a good proxy for + // network availability. The URL dojox.off.availabilityURL is + // used, which defaults to this site's domain name (ex: + // foobar.com). We check for dojox.off.AVAILABILITY_TIMEOUT (in + // seconds) and abort after that + // callback: Function + // An optional callback function that will receive one argument: + // whether the site is available or not and is boolean. If this + // function is not present we call dojox.off.onNetwork instead if we + // are able to go online. + dojo.xhrGet({ + url: this._getAvailabilityURL(), + handleAs: "text", + timeout: this.NET_CHECK * 1000, + error: dojo.hitch(this, function(err){ + //console.debug("dojox.off._isSiteAvailable.error: " + err); + this.goingOnline = false; + this.isOnline = false; + if(callback){ callback(false); } + }), + load: dojo.hitch(this, function(data){ + //console.debug("dojox.off._isSiteAvailable.load, data="+data); + this.goingOnline = false; + this.isOnline = true; + + if(callback){ callback(true); + }else{ this.onNetwork("online"); } + }) + }); + }, + + _startNetworkThread: function(){ + //console.debug("startNetworkThread"); + + // kick off a thread that does periodic + // checks on the status of the network + if(!this.doNetChecking){ + return; + } + + window.setInterval(dojo.hitch(this, function(){ + var d = dojo.xhrGet({ + url: this._getAvailabilityURL(), + handleAs: "text", + timeout: this.NET_CHECK * 1000, + error: dojo.hitch(this, + function(err){ + if(this.isOnline){ + this.isOnline = false; + + // FIXME: xhrGet() is not + // correctly calling abort + // on the XHR object when + // it times out; fix inside + // there instead of externally + // here + try{ + if(typeof d.ioArgs.xhr.abort == "function"){ + d.ioArgs.xhr.abort(); + } + }catch(e){} + + // if things fell in the middle of syncing, + // stop syncing + dojox.off.sync.isSyncing = false; + + this.onNetwork("offline"); + } + } + ), + load: dojo.hitch(this, + function(data){ + if(!this.isOnline){ + this.isOnline = true; + this.onNetwork("online"); + } + } + ) + }); + + }), this.NET_CHECK * 1000); + }, + + _getAvailabilityURL: function(){ + var url = this.availabilityURL.toString(); + + // bust the browser's cache to make sure we are really talking to + // the server + if(url.indexOf("?") == -1){ + url += "?"; + }else{ + url += "&"; + } + url += "browserbust=" + new Date().getTime(); + + return url; + }, + + _onOfflineCacheInstalled: function(){ + this.onFrameworkEvent("offlineCacheInstalled"); + }, + + _cacheDojoResources: function(){ + // if we are a non-optimized build, then the core Dojo bootstrap + // system was loaded as separate JavaScript files; + // add these to our offline cache list. these are + // loaded before the dojo.require() system exists + + // FIXME: create a better mechanism in the Dojo core to + // expose whether you are dealing with an optimized build; + // right now we just scan the SCRIPT tags attached to this + // page and see if there is one for _base/_loader/bootstrap.js + var isOptimizedBuild = true; + dojo.forEach(dojo.query("script"), function(i){ + var src = i.getAttribute("src"); + if(!src){ return; } + + if(src.indexOf("_base/_loader/bootstrap.js") != -1){ + isOptimizedBuild = false; + } + }); + + if(!isOptimizedBuild){ + dojox.off.files.cache(dojo.moduleUrl("dojo", "_base.js").uri); + dojox.off.files.cache(dojo.moduleUrl("dojo", "_base/_loader/loader.js").uri); + dojox.off.files.cache(dojo.moduleUrl("dojo", "_base/_loader/bootstrap.js").uri); + + // FIXME: pull in the host environment file in a more generic way + // for other host environments + dojox.off.files.cache(dojo.moduleUrl("dojo", "_base/_loader/hostenv_browser.js").uri); + } + + // add anything that was brought in with a + // dojo.require() that resulted in a JavaScript + // URL being fetched + + // FIXME: modify dojo/_base/_loader/loader.js to + // expose a public API to get this information + + for(var i = 0; i < dojo._loadedUrls.length; i++){ + dojox.off.files.cache(dojo._loadedUrls[i]); + } + + // FIXME: add the standard Dojo CSS file + }, + + _save: function(){ + // summary: + // Causes the Dojo Offline framework to save its configuration + // data into local storage. + }, + + _load: function(callback){ + // summary: + // Causes the Dojo Offline framework to load its configuration + // data from local storage + dojox.off.sync._load(callback); + } +}); + + +// wait until the storage system is finished loading +dojox.storage.manager.addOnLoad(dojo.hitch(dojox.off, "_onStorageLoad")); + +// wait until the page is finished loading +dojo.addOnLoad(dojox.off, "_onPageLoad"); + +} |