                 opts.cb?(err)
 
     publish_ui: () =>
+        if @state != 'ready'
+            return
         url = document.URL
         url = url.slice(0,url.length-5) + 'html'
         dialog = templates.find(".smc-jupyter-publish-dialog").clone()

            @save_button.addClass('disabled')

    save: (cb) =>
        if not @save_button?
            return
        @save_button.icon_spin(start:true, delay:5000)
        async.parallel [@dom.save, @syncstring.save], (err) =>
            if not @save_button?
                return
            @save_button.icon_spin(false)
            @update_save_state()

# trying a lot of ideas, though in hindsite it's exactly the same as what React.js does (though
# I didn't know about React.js at the time).
###############################################################################

###
NOTE: There's a lot of this in the main code below:

        if @state != 'ready'
            return

Obviously, a better solution would involve two different classes, where this
guarding of methods so they are no-ops when the relevant attributes aren't
yet initialized would no longer be necessary.
###

# How long to try to download Jupyter notebook before giving up with an error.  Load times in excess of
# a minute can happen; this may be the SMC proxy being slow - not sure yet... but at least
