         opts = @opts = defaults opts,
             sync_interval   : 1000
             cursor_interval : 2000
+            read_only : false
         window.s = @
         @element = templates.find(".smc-jupyter-notebook").clone()
+
+        if @opts.read_only
+            @readonly = true
+            @element.find(".smc-jupyter-notebook-buttons").remove()
+
         @element.data("jupyter_notebook", @)
 
         # Jupyter is proxied via the following canonical URL (don't have to guess at the port):

                    cb?()

    _config_doc: () =>
        @dbg("_config_doc")
        # todo -- should check if .ipynb file is newer... ?
        @status("Displaying Jupyter Notebook")

there is a single upstream copy of @doc in the local_hub daemon.

The user edits @nb.  Periodically we check to see if any changes were made (@nb.dirty) and
if so, we copy the state of @nb to @doc's live.

When @doc changes do to some other user changing something, we compute a diff that tranforms
the live notebook from its current state to the state that matches the new version of @doc.
See the function set_nb below.  Incidentally, I came up with this approach from scratch after
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).
###

async                = require('async')
