
                        img.css(cursor:'crosshair')
                        output.append(img)
                        img.click (e) =>
                            offset = img.offset()
                            x = (e.pageX - offset.left) /img.width()
                            y = (e.pageY - offset.top) /img.height()
                            @execute_code(code:"click(#{x},#{y})", preparse:true)

                        @_block_mouse_move = undefined
                        img.mousemove (e) =>
                            if @_block_mouse_move?
                                return
                            @_block_mouse_move = true
                            setTimeout( ( () => delete @_block_mouse_move ), 100 )
                            offset = img.offset()
                            x = (e.pageX - offset.left) /img.width()
                            y = (e.pageY - offset.top) /img.height()
                            @execute_code(code:"mousemove(#{x},#{y})", preparse:true)



<a href="#stop" class='btn btn-warning btn-small'> <i class="icon-stop"></i> Shutdown</a>

        @element.find("a[href=#stop]").click () =>
            bootbox.confirm "Stop the IPython Notebook server and close this file?  Unsaved worked in any open notebooks anywhere will be lost, and opened notebooks using this server will mysteriously stop working.", (result) =>
                if result
                    @server.stop_server()
                    @editor.close(@filename)
                    setTimeout(f, 3000)  # should be long enough to get save message.
            return false


        @server_switch = @element.find(".salvus-ipython-notebook-server-switch").bootstrapSwitch()

        @server_switch.on 'switch-change', (e, data) =>
            @server_switch.icon_spin(start:true)
            if data.value
                @server.start_server (err, url) =>
                    @server_switch.icon_spin(false)
            else
                @server.start_server (err, url) =>
                    @server_switch.icon_spin(false)




@remove_cell_flags_from_text(changeObj.text)


        start = 0
        while true
            i = text.indexOf(MARKERS.cell, start)
            if i == -1
                console.log(" ==> '#{text}'")
                return text
            j = text.indexOf('\n', i)
            if j - i >= 38
                text  = text.slice(0, i+37) + text.slice(j-1,0)
                start = i + 38
            else
                start = j + 1



    xxx_patch_in_place: (p) =>
        if @opts.string
            console.log("patching string in place")  # should never need to happen
            @opts.string = diffsync.dmp.patch_apply(p, @string())[0]
        else
            cm = @opts.cm

            # We maintain our cursor position using the following trick:
            #    1. Insert a non-used unicode character where the cursor is.
            #    2. Apply the patches.
            #    3. Find the unicode character, remove it, and put the cursor there.
            #       If the unicode character vanished, just put the cursor at the coordinates
            #       where it used to be (better than nothing).
            # There is a more sophisticated approach described at http://neil.fraser.name/writing/cursor/
            # but it is harder to implement given that we'll have to dive into the details of his
            # patch_apply implementation.  This thing below took only a few minutes to implement.
            scroll = cm.getScrollInfo()

            cursor_anchor = cm.getCursor('anchor')
            cursor_head   = cm.getCursor('head')
            range = not (cursor_anchor.line == cursor_head.line and cursor_anchor.ch == cursor_head.ch)

            c_anchor = "\uFE10"   # chosen from http://billposer.org/Linguistics/Computation/UnicodeRanges.html
                                # since it is (1) undefined, and (2) looks like a cursor..
            if range
                c_head   = "\uFE11"

            cm.replaceRange(c_anchor, cursor_anchor)
            if range
                # Have to put the other symbol on the *outside* of the selection, which depends on
                # on whether anchor is before or after head.
                if cursor_head.line > cursor_anchor.line or (cursor_head.line == cursor_anchor.line and cursor_head.ch >= cursor_anchor.ch)
                    cm.replaceRange(c_head, {line:cursor_head.line, ch:cursor_head.ch+1})
                else
                    cm.replaceRange(c_head, {line:cursor_head.line, ch:cursor_head.ch})

            s = @string()

            x = diffsync.dmp.patch_apply(p, s)
            new_value = x[0]
            #console.log("patch app info = ", x[1])
            v = new_value.split('\n')
            find_cursor = (pos0, chr) ->
                line  = pos0.line
                B = 5
                # We first try an interval of radius B around the cursor, since
                # that is where the cursor is most likely to be.
                for k in [Math.max(0, line-B)...Math.max(0,Math.min(line-B, v.length))].concat([0...v.length])
                    ch = v[k]?.indexOf(chr)
                    if ch? and ch != -1
                        return pos:{line:k, ch:ch}, marker:true
                return pos:pos0, marker:false

            anchor_pos = find_cursor(cursor_anchor, c_anchor)
            if range
                head_pos   = find_cursor(cursor_head, c_head)
            else
                head_pos = anchor_pos

            #console.log(misc.to_json(diffsync.dmp.diff_main(s, new_value)))

            next_pos = (val, pos) ->
                # This functions answers the question:
                # If you were to insert the string val at the CodeMirror position pos
                # in a codemirror document, at what position (in codemirror) would
                # the inserted string end at?
                number_of_newlines = (val.match(/\n/g)||[]).length
                if number_of_newlines == 0
                    return {line:pos.line, ch:pos.ch+val.length}
                else
                    return {line:pos.line+number_of_newlines, ch:(val.length - val.lastIndexOf('\n')-1)}

            pos = {line:0, ch:0}  # start at the beginning
            for chunk in diffsync.dmp.diff_main(s, new_value)
                #console.log(chunk)
                op  = chunk[0]  # 0 = stay same; -1 = delete; +1 = add
                val = chunk[1] # the actual text to leave same, delete, or add
                pos1 = next_pos(val, pos)
                switch op
                    when 0 # stay the same
                        # Move our pos pointer to the next position
                        pos = pos1
                        #console.log("skipping to ", pos1)
                    when -1 # delete
                        # Delete until where val ends; don't change pos pointer.
                        cm.replaceRange("", pos, pos1)
                        #console.log("deleting from ", pos, " to ", pos1)
                    when +1 # insert
                        # Insert the new text right here.
                        cm.replaceRange(val, pos)
                        #console.log("inserted new text at ", pos)
                        # Move our pointer to just beyond the text we just inserted.
                        pos = pos1

            ###
            if cm.getValue() != new_value
                console.log("** BUG in new patch application code! ** ")
                #console.log("cm val = '#{cm.getValue()}'")
                #console.log("new_value = '#{new_value}'")
                cm.setValue(new_value)
            ###

            cm.setSelection(anchor_pos.pos, head_pos.pos)
            # Remove the markers: complicated since can't remove both simultaneously, and
            # removing one impacts position of the other, when in same line.
            pos = undefined
            if anchor_pos.marker
                pos = anchor_pos.pos
                cm.replaceRange("", pos, {line:pos.line, ch:pos.ch+1})
            if range and head_pos.marker
                pos1 = head_pos.pos
                if pos? and pos1.line == pos.line
                    if pos1.ch > pos.ch
                        pos1.ch -= 1
                cm.replaceRange("", pos1, {line:pos1.line, ch:pos1.ch+1})

            cm.scrollTo(scroll.left, scroll.top)
            cm.scrollIntoView(anchor_pos.pos)  # just in case





connect_to_server: (cb) =>
        console.log("connect to sage server")
        if @session?
            cb?('already connected or attempting to connect')
            return
        @session = "init"

        session_uuid = @codemirror.getLine(0)
        if session_uuid.length != 36 # probably not yet initialized
            session_uuid = undefined

        async.series([
            (cb) =>
                # If the worksheet specifies a specific session_uuid,
                # try to connect to that one, in case it is still running.
                if session_uuid?
                    console.log("connect to existing session")
                    salvus_client.connect_to_session
                        type         : 'sage'
                        timeout      : 60
                        project_id   : @editor.project_id
                        session_uuid : session_uuid
                        cb           : (err, _session) =>
                            if err or _session.event == 'error'
                                # NOPE -- try to make a new session (below)
                                console.log("connect to existing session -- fail: ", err, _session)
                                cb()
                            else
                                # Bingo -- got it!
                                console.log("connect to existing session -- got it!")
                                @session = _session
                                cb()
                else
                    # No session_uuid requested.
                    cb()
            (cb) =>
                if @session? and @session != "init"
                    # We successfully got a session above.
                    cb()
                else
                    # Create a completely new session on the given project.
                    console.log("connect to new session")
                    salvus_client.new_session
                        timeout    : 60
                        type       : "sage"
                        project_id : @editor.project_id
                        cb : (err, _session) =>
                            if err or _session.event == 'error'
                                console.log("connect to new session -- fail", err, _session)
                                alert_message(type:'error', message:err)
                                @session = undefined
                            else
                                console.log("connect to new session -- got it!")
                                @session = _session
                            cb(err)
        ], (err) =>
            if @session?.session_uuid?
                @codemirror.setLine(0, @session.session_uuid)
            cb?(err)
        )


<!--
<form method='POST' enctype='multipart/form-data' action='upload?uuid=123'>
File to upload: <input type=file name=upfile><br>
Notes about the file: <input type=text name=note><br>
<br>
<input type=submit value=Press> to upload the file!
</form>-->
                            <input type=submit value=Press> to upload the file!
-->
        label += "<a href='' style='margin:5px'><i class='icon-folder-open-o'> </i></a>"
        label += "<a href='' style='margin:5px'><i class='icon-search'> </i></a>"
        label += "<a href='' style='margin:5px'><i class='icon-cog'> </i></a>"



cm = @opts.cm
            pos = cm.getCursor()
            cursor = "\u5077"
            cm.replaceRange(cursor, pos)
            scroll = cm.getScrollInfo()
            new_value = diffsync.dmp.patch_apply(p, @string())[0]
            line = 0
            for v in new_value.split('\n')
                ch = v.indexOf(cursor)
                if ch != -1
                    break
                else
                    line += 1
            if ch != -1
                pos = {line:line, ch:ch}
            cm.setValue(new_value)
            cm.replaceRange("", pos, {line:line, ch:ch+1})
            cm.setCursor(pos) # TODO -- read and implement http://neil.fraser.name/writing/cursor/
            cm.scrollTo(scroll.left, scroll.top)




    _objs_to_propagate: (changeObj) =>
        # Return only the part of the input changeObj that we want to propagate to other editors.
        # In particular, we do not include ones for which origin is not set, since they are caused
        # by the replaceRange we call internally when applying a changeObj.  Returns undefined
        # if nothing to propagate
        cur = result = undefined
        while changeObj?
            if changeObj.origin? and changeObj.origin != 'setValue'
                console.log("including #{changeObj}")
                if result?
                    cur.next = changeObj
                    cur = changeObj
                else
                    cur = result = changeObj
            changeObj = changeObj.next
        if result? and result.next?
            delete result.next
        return result

    render_file_display: (path, cb) =>
        salvus_client.exec
            project_id : @project.project_id
            command    : "cat"
            args       : [path]
            timeout    : 3
            max_output : 100000
            cb         : (err, output) =>
                if err
                    cb($("<div>").html(err))
                else
                    cb($("<pre style='background-color:#fff; padding:2ex; margin-left:2ex;'>").text(output.stdout))

    xxx_render_file_display: (path, cb) =>
        salvus_client.read_text_file_from_project
            project_id : @project.project_id
            timeout : 3
            path : path
            cb : (err, mesg) ->
                if err
                    cb($("<div>").html("Unable to load file..."))
                else if mesg.event == 'error'
                    cb($("<div>").html(mesg.error))
                else
                    cb($("<pre style='background-color:#fff; padding:2ex; margin-left:2ex;'>").text(mesg.content))


# jQuery mod for detecting DOC removal
# See http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
# Usage:   $('.thing').bind('destroyed', () -> )
$.event.special.destroyed =
    remove: (o) ->
        if o.handler
            o.handler()



    _next_cell0 : (cell) ->
        next = cell.element.next()
        if next.length == 0
            elt = cell.element.parent().closest(".salvus-unit")
            n = elt.next()
            if n.length == 0
                elt = elt.parent().closest(".salvus-unit")
            else
                elt = n
        else
            elt = next
        while elt.length > 0
            if elt.hasClass("salvus-cell")
                return elt.data("cell")
            n = elt.find(".salvus-cell")
            if n.length > 0
                return $(n[0]).data("cell")
            n = elt.next()
            if n.length == 0
                elt = elt.parent().closest(".salvus-unit")
            else
                elt = n


        prev = cell.element.prev()
        if prev.length == 0
            elt = cell.element.parent().closest(".salvus-unit").prev()
        else
            elt = prev
        while elt.length > 0
            if elt.hasClass("salvus-cell")
                return elt.data("cell")
            n = elt.find(".salvus-cell")
            if n.length > 0
                return $(n[n.length-1]).data("cell")
            n = elt.prev()
            if n.length == 0
                elt = elt.parent().closest(".salvus-unit").prev()
        if IS_ANDROID
            submit = (ev) =>
                alert(ev.keyCode)
                if ev.keyCode == 13
                    @.session.write_data(@mobile_target.val())
                    @mobile_target.val('')
                else
                    return false
            $(document).on('keydown',submit).on('keypress',submit)
        else
            @terminal.focus()

        # 2. Mark special styles of the output text:
        #  THIS IS INSANELY SLOW/USELESS
        # y = start
        # m = 0
        # while y <= end
        #     row = y + terminal.ydisp
        #     ln = this.lines[row]
        #     for i in [0...width]
        #         data = ln[i][0]
        #         if data != terminal.defAttr
        #             m += 1
        #             if m > 30
        #                 break
        #             e.markText({line:row, ch:i}, {line:row, ch:i+1}, {className:'special'})
        #             console.log('marking some text')
        #     y++
        # console.log("DONE MARKING")



  <section class="slide">
    <h2>Extensions</h2>
    <p>Core gives you basic slide functionality...</p>
    <ul>
      <li class="slide">
        <h3  class="well btn btn-success btn-large">deck.goto</h3>
        <p>Adds a shortcut key to jump to any slide number...</p>
      </li>
      <li class="slide"><div class="slide-cell"></div></li>
      <li class="slide"><div class="slide-cell"></div></li>
    </ul>
  </section>

  <section class="slide">
    <div class="slide-cell"></div>
    <h2>Extensions</h2>
    <p>Core gives you basic slide functionality...</p>
    <h2>Extensions</h2>
    <p>Core gives you basic slide functionality...</p>
    <h2>Extensions</h2>
    <p>Core gives you basic slide functionality...</p>
    <h2>Extensions</h2>
    <p>Core gives you basic slide functionality...</p>
    <h2>Extensions</h2>
    <p>Core gives you basic slide functionality...</p>
    <h2>Extensions</h2>
    <p>Core gives you basic slide functionality...</p>
    <h2>Extensions</h2>
    <p>Core gives you basic slide functionality...</p>
    <h2>Extensions</h2>
    <p>Core gives you basic slide functionality...</p>
    <h2>Extensions</h2>
  </section>

  <section class="slide">
    <h1>Here</h1>
  </section>




    ###############################################################
    # jquery plugins for manipulating the contenteditable editor
    # in ways I need, using rangy mostly for cross-platform support
    # TODO: Move this to its own file.
    ###############################################################
    $.fn.extend
        # set_caret_position: move the cursor to given position in the given element
        salvusws_set_caret_position: (opts={}) ->
            opts = defaults opts,
                offset: 0
                type:   'character'   # 'range', 'character'
            @each () ->
                range = rangy.createRange()
                if opts.type == 'range'
                    range.setStart(this, opts.offset)
                    range.setEnd(this, opts.offset)
                else
                    range.selectCharacters(this, opts.offset, opts.offset)
                sel = rangy.getSelection()
                if sel.rangeCount > 0
                    sel.removeAllRanges()
                sel.addRange(range)

    class CaretPosition
        constructor: (@container, @offset) ->
        equals: (other) ->  # true if this and other are at the same position
            @container == other.container and @offset == other.offset
        set: (type='range') -> # moves the cursor to this position, if it exists
            range = rangy.createRange()
            range.setStart(@container, @offset)
            range.setEnd(@container, @offset)
            sel = rangy.getSelection()
            if sel.rangeCount > 0
                sel.removeAllRanges()
            sel.addRange(range)

    get_caret_position = () ->
        try
            sel   = rangy.getSelection()
            range = sel.getRangeAt(0)
            return new CaretPosition(range.startContainer, range.startOffset)
        catch error
            return undefined  # no caret position

--------------
        salvusws_text: (opts={}) ->   # returns text rather than a jquery wrapped object
            opts = defaults opts, {} # no options
            result = ''
            @each () ->
                that = this
                async.series([
                    (cb) -> client.html_to_text(html:$(that).html(), cb:((error, plain) -> result += plain; cb()))
                ])
                # This was the rangy implementation, but it was 10000 times slower than htmlparser, so screw that!
                #r = rangy.createRange()
                #r.selectNodeContents(this)
                #result += r.text()

            # &nbsp;'s are converted to character code 160, not 32 (which is a space).
            # We thus must replace all 32's by 160, or sage will be unhappy:
            return result.replace(/\xA0/g, " ")


                #cell.draggable().bind("click", () -> $(this).focus())
                #last_active_cell = active_cell = cell
                #.blur((e) -> active_cell=undefined; highlight(input:$(this)) )


                        #r2 = rangy.createRange()
                        #r2.selectNodeContents(newNode)
                        #r2.moveStart("character", 1)
                        #sel.setSingleRange(r2)
                        #sel.deleteFromDocument(r2)
                        #
                        sel.removeAllRanges()
                        r2 = rangy.createRange()
                        r2.setStart(newNode,1)
                        r2.setEnd(newNode,1)
                        if sel.rangeCount > 0
                            sel.removeAllRanges()
                        sel.addRange(r2)
                        return false

                        # r2 = rangy.createRange()
                        # r2.selectNodeContents(newNode)
                        # r2.moveStart("character", 1)
                        # sel.setSingleRange(r2)
                        # sel.deleteFromDocument(r2)

                        # sel.removeAllRanges()
                        # r2 = rangy.createRange()
                        # r2.setStart(newNode,1)
                        # r2.setEnd(newNode,1)
                        # if sel.rangeCount > 0
                        #     sel.removeAllRanges()
                        # sel.addRange(r2)

                        return false



        old_email_address = account_settings.settings.email_address
        account_settings.settings.email_address = $("#account-change_email_new_address").val()
        $("#account-change_email_new_address").val("")

        account_settings.save_to_server
            password   : password
            cb         : (error, mesg) ->
                console.log(error, mesg)
                account_settings.settings.email_address = old_email_address
                if error  # exceptional condition -- some sort of server or connection error
                    alert_message(type:"error", message:error)
                    return true   # kill modal (since this is a weird error condition)
                if mesg.event != "changed_email_address" # exceptional condition -- a *BUG*
                    alert_message(type:"error", message:"Internal or network error changing email address.")
                    return true   # kill modal
                if mesg.error  # there was an error
                    if mesg.reason?
                        $("#account-change_email_address-#{mesg.reason}").show()
                        return false
                    else
                        alert_message(type:"error", message:mesg.error)
                        return true
                else # no errors
                    $("#account-settings-email_address").html(mesg.new_email_address)
                    account_settings.settings.email_address = mesg.new_email_address
                    return true   # kill modal

        Mercury.config.behaviors.execute2 = (selection, options) ->
            e = selection.wrap('<div class="sage-cell">', true)
            console.log(e)
            e.execute_cell()

        Mercury.config.behaviors.execute = (selection, options) ->
            input = selection.textContent()
            output = ""
            salvus_exec input, (mesg) ->
                console.log(mesg)
                if mesg.stdout?
                    output += "<span class='sage-stdout'>#{mesg.stdout}</span>"
                if mesg.stderr?
                    output += "<span class='sage-stderr'>#{mesg.stderr}</span>"
                selection.insertNode("<span>" + input + "<br>" + output + "</span>")
                Mercury.trigger('reinitialize')

    $(window).on 'mercury:ready', () ->


