Subversion Repositories Applications.papyrus

Rev

Blame | Last modification | View Log | RSS feed

if(!dojo._hasResource["dojox.off.ui"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.off.ui"] = true;
dojo.provide("dojox.off.ui");

dojo.require("dojox.storage.Provider");
dojo.require("dojox.storage.manager");
dojo.require("dojox.storage.GearsStorageProvider");

// Author: Brad Neuberg, bkn3@columbia.edu, http://codinginparadise.org

// summary:
//      dojox.off.ui provides a standard,
//      default user-interface for a 
//      Dojo Offline Widget that can easily
//      be dropped into applications that would
//      like to work offline.
dojo.mixin(dojox.off.ui, {
        // appName: String
        //      This application's name, such as "Foobar". Note that
        //      this is a string, not HTML, so embedded markup will
        //      not work, including entities. Only the following
        //      characters are allowed: numbers, letters, and spaces.
        //      You must set this property.
        appName: "setme",
        
        // autoEmbed: boolean
        //      For advanced usage; most developers can ignore this.
        //      Whether to automatically auto-embed the default Dojo Offline
        //      widget into this page; default is true. 
        autoEmbed: true,
        
        // autoEmbedID: String
        //      For advanced usage; most developers can ignore this.
        //      The ID of the DOM element that will contain our
        //      Dojo Offline widget; defaults to the ID 'dot-widget'.
        autoEmbedID: "dot-widget",
        
        // runLink: String
        //      For advanced usage; most developers can ignore this.
        //      The URL that should be navigated to to run this 
        //      application offline; this will be placed inside of a
        //      link that the user can drag to their desktop and double
        //      click. Note that this URL must exactly match the URL
        //      of the main page of our resource that is offline for
        //      it to be retrieved from the offline cache correctly.
        //      For example, if you have cached your main page as
        //      http://foobar.com/index.html, and you set this to
        //      http://www.foobar.com/index.html, the run link will
        //      not work. By default this value is automatically set to 
        //      the URL of this page, so it does not need to be set
        //      manually unless you have unusual needs.
        runLink: window.location.href,
        
        // runLinkTitle: String
        //      For advanced usage; most developers can ignore this.
        //      The text that will be inside of the link that a user
        //      can drag to their desktop to run this application offline.
        //      By default this is automatically set to "Run " plus your
        //      application's name.
        runLinkTitle: "Run Application",
        
        // learnHowPath: String
        //      For advanced usage; most developers can ignore this.
        //      The path to a web page that has information on 
        //      how to use this web app offline; defaults to
        //      src/off/ui-template/learnhow.html, relative to
        //      your Dojo installation. Make sure to set
        //      dojo.to.ui.customLearnHowPath to true if you want
        //      a custom Learn How page.
        learnHowPath: dojo.moduleUrl("dojox", "off/resources/learnhow.html"),
        
        // customLearnHowPath: boolean
        //      For advanced usage; most developers can ignore this.
        //      Whether the developer is using their own custom page
        //      for the Learn How instructional page; defaults to false.
        //      Use in conjunction with dojox.off.ui.learnHowPath.
        customLearnHowPath: false,
        
        htmlTemplatePath: dojo.moduleUrl("dojox", "off/resources/offline-widget.html").uri,
        cssTemplatePath: dojo.moduleUrl("dojox", "off/resources/offline-widget.css").uri,
        onlineImagePath: dojo.moduleUrl("dojox", "off/resources/greenball.png").uri,
        offlineImagePath: dojo.moduleUrl("dojox", "off/resources/redball.png").uri,
        rollerImagePath: dojo.moduleUrl("dojox", "off/resources/roller.gif").uri,
        checkmarkImagePath: dojo.moduleUrl("dojox", "off/resources/checkmark.png").uri,
        learnHowJSPath: dojo.moduleUrl("dojox", "off/resources/learnhow.js").uri,
        
        _initialized: false,
        
        onLoad: function(){
                // summary:
                //      A function that should be connected to allow your
                //      application to know when Dojo Offline, the page, and
                //      the Offline Widget are all initialized and ready to be
                //      used:
                //
                //              dojo.connect(dojox.off.ui, "onLoad", someFunc)
        },

        _initialize: function(){
                //console.debug("dojox.off.ui._initialize");
                
                // make sure our app name is correct
                if(this._validateAppName(this.appName) == false){
                        alert("You must set dojox.off.ui.appName; it can only contain "
                                        + "letters, numbers, and spaces; right now it "
                                        + "is incorrectly set to '" + dojox.off.ui.appName + "'");
                        dojox.off.enabled = false;
                        return;
                }
                
                // set our run link text to its default
                this.runLinkText = "Run " + this.appName;
                
                // setup our event listeners for Dojo Offline events
                // to update our UI
                dojo.connect(dojox.off, "onNetwork", this, "_onNetwork");
                dojo.connect(dojox.off.sync, "onSync", this, "_onSync");
                
                // cache our default UI resources
                dojox.off.files.cache([
                                                        this.htmlTemplatePath,
                                                        this.cssTemplatePath,
                                                        this.onlineImagePath,
                                                        this.offlineImagePath,
                                                        this.rollerImagePath,
                                                        this.checkmarkImagePath
                                                        ]);
                
                // embed the offline widget UI
                if(this.autoEmbed){
                        this._doAutoEmbed();
                }
        },
        
        _doAutoEmbed: function(){
                // fetch our HTML for the offline widget

                // dispatch the request
                dojo.xhrGet({
                        url:     this.htmlTemplatePath,
                        handleAs:       "text",
                        error:          function(err){
                                dojox.off.enabled = false;
                                err = err.message||err;
                                alert("Error loading the Dojo Offline Widget from "
                                                + this.htmlTemplatePath + ": " + err);
                        },
                        load:           dojo.hitch(this, this._templateLoaded)   
                });
        },
        
        _templateLoaded: function(data){
                //console.debug("dojox.off.ui._templateLoaded");
                // inline our HTML
                var container = dojo.byId(this.autoEmbedID);
                if(container){ container.innerHTML = data; }
                
                // fill out our image paths
                this._initImages();
                
                // update our network indicator status ball
                this._updateNetIndicator();
                
                // update our 'Learn How' text
                this._initLearnHow();
                
                this._initialized = true;
                
                // check offline cache settings
                if(!dojox.off.hasOfflineCache){
                        this._showNeedsOfflineCache();
                        return;
                }
                
                // check to see if we need a browser restart
                // to be able to use this web app offline
                if(dojox.off.hasOfflineCache && dojox.off.browserRestart){
                        this._needsBrowserRestart();
                        return;
                }else{
                        var browserRestart = dojo.byId("dot-widget-browser-restart");
                        if(browserRestart){ browserRestart.style.display = "none"; }
                }
                
                // update our sync UI
                this._updateSyncUI();
                
                // register our event listeners for our main buttons
                this._initMainEvtHandlers();
                
                // if offline functionality is disabled, disable everything
                this._setOfflineEnabled(dojox.off.enabled);
                
                // update our UI based on the state of the network
                this._onNetwork(dojox.off.isOnline ? "online" : "offline");
                
                // try to go online
                this._testNet();
        },
        
        _testNet: function(){
                dojox.off.goOnline(dojo.hitch(this, function(isOnline){
                        //console.debug("testNet callback, isOnline="+isOnline);
                        
                        // display our online/offline results
                        this._onNetwork(isOnline ? "online" : "offline");
                        
                        // indicate that our default UI 
                        // and Dojo Offline are now ready to
                        // be used
                        this.onLoad();
                }));
        },
        
        _updateNetIndicator: function(){
                var onlineImg = dojo.byId("dot-widget-network-indicator-online");
                var offlineImg = dojo.byId("dot-widget-network-indicator-offline");
                var titleText = dojo.byId("dot-widget-title-text");
                
                if(onlineImg && offlineImg){
                        if(dojox.off.isOnline == true){
                                onlineImg.style.display = "inline";
                                offlineImg.style.display = "none";
                        }else{
                                onlineImg.style.display = "none";
                                offlineImg.style.display = "inline";
                        }
                }
                
                if(titleText){
                        if(dojox.off.isOnline){
                                titleText.innerHTML = "Online";
                        }else{
                                titleText.innerHTML = "Offline";
                        }
                }
        },
        
        _initLearnHow: function(){
                var learnHow = dojo.byId("dot-widget-learn-how-link");
                
                if(!learnHow){ return; }
                
                if(!this.customLearnHowPath){
                        // add parameters to URL so the Learn How page
                        // can customize itself and display itself
                        // correctly based on framework settings
                        var dojoPath = djConfig.baseRelativePath;
                        this.learnHowPath += "?appName=" + encodeURIComponent(this.appName)
                                                                        + "&hasOfflineCache=" + dojox.off.hasOfflineCache
                                                                        + "&runLink=" + encodeURIComponent(this.runLink)
                                                                        + "&runLinkText=" + encodeURIComponent(this.runLinkText)
                                                                        + "&baseRelativePath=" + encodeURIComponent(dojoPath);
                        
                        // cache our Learn How JavaScript page and
                        // the HTML version with full query parameters
                        // so it is available offline without a cache miss                                      
                        dojox.off.files.cache(this.learnHowJSPath);
                        dojox.off.files.cache(this.learnHowPath);
                }
                
                learnHow.setAttribute("href", this.learnHowPath);
                
                var appName = dojo.byId("dot-widget-learn-how-app-name");
                
                if(!appName){ return; }
                
                appName.innerHTML = "";
                appName.appendChild(document.createTextNode(this.appName));
        },
        
        _validateAppName: function(appName){
                if(!appName){ return false; }
                
                return (/^[a-z0-9 ]*$/i.test(appName));
        },
        
        _updateSyncUI: function(){
                var roller = dojo.byId("dot-roller");
                var checkmark = dojo.byId("dot-success-checkmark");
                var syncMessages = dojo.byId("dot-sync-messages");
                var details = dojo.byId("dot-sync-details");
                var cancel = dojo.byId("dot-sync-cancel");
                
                if(dojox.off.sync.isSyncing){
                        this._clearSyncMessage();
                        
                        if(roller){ roller.style.display = "inline"; }
                        
                        if(checkmark){ checkmark.style.display = "none"; }
                        
                        if(syncMessages){
                                dojo.removeClass(syncMessages, "dot-sync-error");
                        }
                        
                        if(details){ details.style.display = "none"; }
                        
                        if(cancel){ cancel.style.display = "inline"; }
                }else{  
                        if(roller){ roller.style.display = "none"; }
                        
                        if(cancel){ cancel.style.display = "none"; }
                        
                        if(syncMessages){
                                dojo.removeClass(syncMessages, "dot-sync-error");
                        }
                }
        },
        
        _setSyncMessage: function(message){
                var syncMessage = dojo.byId("dot-sync-messages");
                if(syncMessage){
                        // when used with Google Gears pre-release in Firefox/Mac OS X,
                        // the browser would crash when testing in Moxie
                        // if we set the message this way for some reason.
                        // Brad Neuberg, bkn3@columbia.edu
                        //syncMessage.innerHTML = message;
                        
                        while(syncMessage.firstChild){
                                syncMessage.removeChild(syncMessage.firstChild);
                        }
                        syncMessage.appendChild(document.createTextNode(message));
                }
        },
        
        _clearSyncMessage: function(){
                this._setSyncMessage("");
        },
        
        _initImages: function(){        
                var onlineImg = dojo.byId("dot-widget-network-indicator-online");
                if(onlineImg){
                        onlineImg.setAttribute("src", this.onlineImagePath);
                }
                
                var offlineImg = dojo.byId("dot-widget-network-indicator-offline");
                if(offlineImg){
                        offlineImg.setAttribute("src", this.offlineImagePath);
                }
                
                var roller = dojo.byId("dot-roller");
                if(roller){
                        roller.setAttribute("src", this.rollerImagePath);
                }
                
                var checkmark = dojo.byId("dot-success-checkmark");
                if(checkmark){
                        checkmark.setAttribute("src", this.checkmarkImagePath);
                }
        },
        
        _showDetails: function(evt){
                // cancel the button's default behavior
                evt.preventDefault();
                evt.stopPropagation();
                
                if(!dojox.off.sync.details.length){
                        return;
                }
                
                // determine our HTML message to display
                var html = "";
                html += "<html><head><title>Sync Details</title><head><body>";
                html += "<h1>Sync Details</h1>\n";
                html += "<ul>\n";
                for(var i = 0; i < dojox.off.sync.details.length; i++){
                        html += "<li>";
                        html += dojox.off.sync.details[i];
                        html += "</li>";        
                }
                html += "</ul>\n";
                html += "<a href='javascript:window.close()' "
                                 + "style='text-align: right; padding-right: 2em;'>"
                                 + "Close Window"
                                 + "</a>\n";
                html += "</body></html>";
                
                // open a popup window with this message
                var windowParams = "height=400,width=600,resizable=true,"
                                                        + "scrollbars=true,toolbar=no,menubar=no,"
                                                        + "location=no,directories=no,dependent=yes";

                var popup = window.open("", "SyncDetails", windowParams);
                
                if(!popup){ // aggressive popup blocker
                        alert("Please allow popup windows for this domain; can't display sync details window");
                        return;
                }
                
                popup.document.open();
                popup.document.write(html);
                popup.document.close();
                
                // put the focus on the popup window
                if(popup.focus){
                        popup.focus();
                }
        },
        
        _cancel: function(evt){
                // cancel the button's default behavior
                evt.preventDefault();
                evt.stopPropagation();
                
                dojox.off.sync.cancel();
        },
        
        _needsBrowserRestart: function(){
                var browserRestart = dojo.byId("dot-widget-browser-restart");
                if(browserRestart){
                        dojo.addClass(browserRestart, "dot-needs-browser-restart");
                }
                
                var appName = dojo.byId("dot-widget-browser-restart-app-name");
                if(appName){
                        appName.innerHTML = "";
                        appName.appendChild(document.createTextNode(this.appName));
                }
                
                var status = dojo.byId("dot-sync-status");
                if(status){
                        status.style.display = "none";
                }
        },
        
        _showNeedsOfflineCache: function(){
                var widgetContainer = dojo.byId("dot-widget-container");
                if(widgetContainer){
                        dojo.addClass(widgetContainer, "dot-needs-offline-cache");
                }
        },
        
        _hideNeedsOfflineCache: function(){
                var widgetContainer = dojo.byId("dot-widget-container");
                if(widgetContainer){
                        dojo.removeClass(widgetContainer, "dot-needs-offline-cache");
                }
        },
        
        _initMainEvtHandlers: function(){
                var detailsButton = dojo.byId("dot-sync-details-button");
                if(detailsButton){
                        dojo.connect(detailsButton, "onclick", this, this._showDetails);
                }
                var cancelButton = dojo.byId("dot-sync-cancel-button");
                if(cancelButton){
                        dojo.connect(cancelButton, "onclick", this, this._cancel);
                }
        },
        
        _setOfflineEnabled: function(enabled){
                var elems = [];
                elems.push(dojo.byId("dot-sync-status"));
                
                for(var i = 0; i < elems.length; i++){
                        if(elems[i]){
                                elems[i].style.visibility = 
                                                        (enabled ? "visible" : "hidden");
                        }
                }
        },
        
        _syncFinished: function(){
                this._updateSyncUI();
                
                var checkmark = dojo.byId("dot-success-checkmark");
                var details = dojo.byId("dot-sync-details");
                
                if(dojox.off.sync.successful == true){
                        this._setSyncMessage("Sync Successful");
                        if(checkmark){ checkmark.style.display = "inline"; }
                }else if(dojox.off.sync.cancelled == true){
                        this._setSyncMessage("Sync Cancelled");
                        
                        if(checkmark){ checkmark.style.display = "none"; }
                }else{
                        this._setSyncMessage("Sync Error");
                        
                        var messages = dojo.byId("dot-sync-messages");
                        if(messages){
                                dojo.addClass(messages, "dot-sync-error");
                        }
                        
                        if(checkmark){ checkmark.style.display = "none"; }
                }
                
                if(dojox.off.sync.details.length && details){
                        details.style.display = "inline";
                }
        },
        
        _onFrameworkEvent: function(type, saveData){
                if(type == "save"){
                        if(saveData.status == dojox.storage.FAILED && !saveData.isCoreSave){
                                alert("Please increase the amount of local storage available "
                                                + "to this application");
                                if(dojox.storage.hasSettingsUI()){
                                        dojox.storage.showSettingsUI();
                                }               
                        
                                // FIXME: Be able to know if storage size has changed
                                // due to user configuration
                        }
                }else if(type == "coreOperationFailed"){
                        console.log("Application does not have permission to use Dojo Offline");
                
                        if(!this._userInformed){
                                alert("This application will not work if Google Gears is not allowed to run");
                                this._userInformed = true;
                        }
                }else if(type == "offlineCacheInstalled"){
                        // clear out the 'needs offline cache' info
                        this._hideNeedsOfflineCache();
                
                        // check to see if we need a browser restart
                        // to be able to use this web app offline
                        if(dojox.off.hasOfflineCache == true
                                && dojox.off.browserRestart == true){
                                this._needsBrowserRestart();
                                return;
                        }else{
                                var browserRestart = dojo.byId("dot-widget-browser-restart");
                                if(browserRestart){
                                        browserRestart.style.display = "none";
                                }
                        }
                
                        // update our sync UI
                        this._updateSyncUI();
                
                        // register our event listeners for our main buttons
                        this._initMainEvtHandlers();
                
                        // if offline is disabled, disable everything
                        this._setOfflineEnabled(dojox.off.enabled);
                
                        // try to go online
                        this._testNet();
                }
        },
        
        _onSync: function(type){
                //console.debug("ui, onSync="+type);
                switch(type){
                        case "start": 
                                this._updateSyncUI();
                                break;
                                
                        case "refreshFiles":
                                this._setSyncMessage("Downloading UI...");
                                break;
                                
                        case "upload":
                                this._setSyncMessage("Uploading new data...");
                                break;
                                
                        case "download":
                                this._setSyncMessage("Downloading new data...");
                                break;
                                
                        case "finished":
                                this._syncFinished();
                                break;
                                
                        case "cancel":
                                this._setSyncMessage("Canceling Sync...");
                                break;
                                
                        default:
                                dojo.warn("Programming error: "
                                                        + "Unknown sync type in dojox.off.ui: " + type);
                                break;
                }
        },
        
        _onNetwork: function(type){
                // summary:
                //      Called when we go on- or off-line
                // description:
                //      When we go online or offline, this method is called to update
                //      our UI. Default behavior is to update the Offline
                //      Widget UI and to attempt a synchronization.
                // type: String
                //      "online" if we just moved online, and "offline" if we just
                //      moved offline.
                
                if(!this._initialized){ return; }
                
                // update UI
                this._updateNetIndicator();
                
                if(type == "offline"){
                        this._setSyncMessage("You are working offline");
                
                        // clear old details
                        var details = dojo.byId("dot-sync-details");
                        if(details){ details.style.display = "none"; }
                        
                        // if we fell offline during a sync, hide
                        // the sync info
                        this._updateSyncUI();
                }else{ // online
                        // synchronize, but pause for a few seconds
                        // so that the user can orient themselves
                        if(dojox.off.sync.autoSync){
                                window.setTimeout("dojox.off.sync.synchronize()", 1000);
                        }
                }
        }
});

// register ourselves for low-level framework events
dojo.connect(dojox.off, "onFrameworkEvent", dojox.off.ui, "_onFrameworkEvent");

// start our magic when the Dojo Offline framework is ready to go
dojo.connect(dojox.off, "onLoad", dojox.off.ui, dojox.off.ui._initialize);

}