|
|
@@ -1,374 +0,0 @@
|
|
|
-define [
|
|
|
- "editor/Document"
|
|
|
- "undo/UndoManager"
|
|
|
- "utils/Modal"
|
|
|
- "ace/ace"
|
|
|
- "ace/edit_session"
|
|
|
- "ace/mode/latex"
|
|
|
- "ace/range"
|
|
|
- "ace/keyboard/vim"
|
|
|
- "ace/keyboard/emacs"
|
|
|
- "libs/backbone"
|
|
|
- "libs/jquery.storage"
|
|
|
-], (Document, UndoManager, Modal) ->
|
|
|
- AceEditor = require("ace/ace")
|
|
|
- EditSession = require('ace/edit_session').EditSession
|
|
|
- LatexMode = require("ace/mode/latex").Mode
|
|
|
- Range = require("ace/range").Range
|
|
|
- Vim = require("ace/keyboard/vim").handler
|
|
|
- Emacs = require("ace/keyboard/emacs").handler
|
|
|
- keybindings = ace: null, vim: Vim, emacs: Emacs
|
|
|
-
|
|
|
- class Editor
|
|
|
- templates:
|
|
|
- editorPanel: $("#editorPanelTemplate").html()
|
|
|
- loadingIndicator: $("#loadingIndicatorTemplate").html()
|
|
|
-
|
|
|
- viewOptions: {flatView:"flatView", splitView:"splitView"}
|
|
|
- currentViewState: undefined
|
|
|
- compilationErrors: {}
|
|
|
-
|
|
|
- constructor: (@ide) ->
|
|
|
- _.extend @, Backbone.Events
|
|
|
- @editorPanel = $(@templates.editorPanel)
|
|
|
- @ide.mainAreaManager.addArea
|
|
|
- identifier: "editor"
|
|
|
- element: @editorPanel
|
|
|
- @initializeEditor()
|
|
|
- @bindToFileTreeEvents()
|
|
|
- @enable()
|
|
|
- @loadingIndicator = $(@templates.loadingIndicator)
|
|
|
- @editorPanel.find("#editor").append(@loadingIndicator)
|
|
|
- @leftPanel = @editorPanel.find("#leftEditorPanel")
|
|
|
- @rightPanel = @editorPanel.find("#rightEditorPanel")
|
|
|
- @initSplitView()
|
|
|
- @switchToFlatView()
|
|
|
-
|
|
|
- bindToFileTreeEvents: () ->
|
|
|
- @ide.fileTreeManager.on "open:doc", (doc_id, options = {}) =>
|
|
|
- if @enabled
|
|
|
- @openDoc doc_id, options
|
|
|
-
|
|
|
- initSplitView: () ->
|
|
|
- @$splitter = splitter = @editorPanel.find("#editorSplitter")
|
|
|
- options =
|
|
|
- spacing_open: 8
|
|
|
- spacing_closed: 16
|
|
|
- east:
|
|
|
- size: "50%"
|
|
|
- maskIframesOnResize: true
|
|
|
- onresize: () =>
|
|
|
- @trigger("resize")
|
|
|
-
|
|
|
- if (state = $.localStorage("layout.editor"))?
|
|
|
- options.east = state.east
|
|
|
-
|
|
|
- splitter.layout options
|
|
|
-
|
|
|
- $(window).unload () =>
|
|
|
- @_saveSplitterState()
|
|
|
-
|
|
|
- _saveSplitterState: () ->
|
|
|
- if $("#editorSplitter").is(":visible")
|
|
|
- state = $("#editorSplitter").layout().readState()
|
|
|
- eastWidth = state.east.size + $("#editorSplitter .ui-layout-resizer-east").width()
|
|
|
- percentWidth = eastWidth / $("#editorSplitter").width() * 100 + "%"
|
|
|
- state.east.size = percentWidth
|
|
|
- $.localStorage("layout.editor", state)
|
|
|
-
|
|
|
- switchToSplitView: () ->
|
|
|
- if @currentViewState != @viewOptions.splitView
|
|
|
- @currentViewState = @viewOptions.splitView
|
|
|
- @leftPanel.prepend(
|
|
|
- @editorPanel.find("#editorWrapper")
|
|
|
- )
|
|
|
- splitter = @editorPanel.find("#editorSplitter")
|
|
|
- splitter.show()
|
|
|
- @ide.layoutManager.resizeAllSplitters()
|
|
|
-
|
|
|
- switchToFlatView: () ->
|
|
|
- if @currentViewState != @viewOptions.flatView
|
|
|
- @_saveSplitterState()
|
|
|
- @currentViewState = @viewOptions.flatView
|
|
|
- @editorPanel.prepend(
|
|
|
- @editorPanel.find("#editorWrapper")
|
|
|
- )
|
|
|
- @editorPanel.find("#editorSplitter").hide()
|
|
|
- @aceEditor.resize(true)
|
|
|
-
|
|
|
- showLoading: () ->
|
|
|
- delay = 600 # ms
|
|
|
- @loading = true
|
|
|
- setTimeout ( =>
|
|
|
- if @loading
|
|
|
- @loadingIndicator.show()
|
|
|
- ), delay
|
|
|
-
|
|
|
- hideLoading: () ->
|
|
|
- @loading = false
|
|
|
- @loadingIndicator.hide()
|
|
|
-
|
|
|
- showUndoConflictWarning: () ->
|
|
|
- $("#editor").prepend($("#undoConflictWarning"))
|
|
|
- $("#undoConflictWarning").show()
|
|
|
- hideBtn = $("#undoConflictWarning .js-hide")
|
|
|
- hideBtn.off("click")
|
|
|
- hideBtn.on "click", (e) ->
|
|
|
- e.preventDefault()
|
|
|
- $("#undoConflictWarning").hide()
|
|
|
- if @hideUndoWarningTimeout?
|
|
|
- clearTimeout @hideUndoWarningTimeout
|
|
|
- delete @hideUndoWarningTimeout
|
|
|
- @hideUndoWarningTimeout = setTimeout ->
|
|
|
- $("#undoConflictWarning").fadeOut("slow")
|
|
|
- , 4000
|
|
|
-
|
|
|
-
|
|
|
- initializeEditor: () ->
|
|
|
- @aceEditor = aceEditor = AceEditor.edit("editor")
|
|
|
-
|
|
|
- @on "resize", => @aceEditor.resize()
|
|
|
- @ide.layoutManager.on "resize", => @trigger "resize"
|
|
|
-
|
|
|
- mode = window.userSettings.mode
|
|
|
- theme = window.userSettings.theme
|
|
|
-
|
|
|
- chosenKeyBindings = keybindings[mode]
|
|
|
- aceEditor.setKeyboardHandler(chosenKeyBindings)
|
|
|
- aceEditor.setTheme("ace/theme/#{window.userSettings.theme}")
|
|
|
- aceEditor.setShowPrintMargin(false)
|
|
|
-
|
|
|
- # Prevert Ctrl|Cmd-S from triggering save dialog
|
|
|
- aceEditor.commands.addCommand
|
|
|
- name: "save",
|
|
|
- bindKey: win: "Ctrl-S", mac: "Command-S"
|
|
|
- exec: () ->
|
|
|
- readOnly: true
|
|
|
- aceEditor.commands.removeCommand "transposeletters"
|
|
|
- aceEditor.commands.removeCommand "showSettingsMenu"
|
|
|
- aceEditor.commands.removeCommand "foldall"
|
|
|
-
|
|
|
- aceEditor.showCommandLine = (args...) =>
|
|
|
- @trigger "showCommandLine", aceEditor, args...
|
|
|
-
|
|
|
- aceEditor.on "dblclick", (e) => @trigger "dblclick", e
|
|
|
- aceEditor.on "click", (e) => @trigger "click", e
|
|
|
- aceEditor.on "mousemove", (e) =>
|
|
|
- position = @aceEditor.renderer.screenToTextCoordinates(e.clientX, e.clientY)
|
|
|
- e.position = position
|
|
|
- @trigger "mousemove", e
|
|
|
-
|
|
|
- setIdeToEditorPanel: (options = {}) ->
|
|
|
- @aceEditor.focus()
|
|
|
- @aceEditor.resize()
|
|
|
- loadDocument = =>
|
|
|
- @refreshCompilationErrors()
|
|
|
-
|
|
|
- @ide.layoutManager.resizeAllSplitters()
|
|
|
-
|
|
|
- if options.line?
|
|
|
- @gotoLine(options.line)
|
|
|
- else
|
|
|
- pos = $.localStorage("doc.position.#{@current_doc_id}") || {}
|
|
|
- @ignoreCursorPositionChanges = true
|
|
|
- @setCursorPosition(pos.cursorPosition or {row: 0, column: 0})
|
|
|
- @setScrollTop(pos.scrollTop or 0)
|
|
|
- @ignoreCursorPositionChanges = false
|
|
|
- @ide.mainAreaManager.change 'editor', =>
|
|
|
- setTimeout loadDocument, 0
|
|
|
-
|
|
|
- refreshCompilationErrors: () ->
|
|
|
- @getSession().setAnnotations @compilationErrors[@current_doc_id]
|
|
|
-
|
|
|
- openDoc: (doc_id, options = {}) ->
|
|
|
- if @current_doc_id == doc_id && !options.forceReopen
|
|
|
- @setIdeToEditorPanel(line: options.line)
|
|
|
- else
|
|
|
- @showLoading()
|
|
|
- @current_doc_id = doc_id
|
|
|
- @_openNewDocument doc_id, (error, document) =>
|
|
|
- if error?
|
|
|
- @ide.showGenericServerErrorMessage()
|
|
|
- return
|
|
|
-
|
|
|
- @setIdeToEditorPanel(line: options.line)
|
|
|
- @hideLoading()
|
|
|
- @trigger "change:doc", @getSession()
|
|
|
-
|
|
|
- _openNewDocument: (doc_id, callback = (error, document) ->) ->
|
|
|
- if @document?
|
|
|
- @document.leaveAndCleanUp()
|
|
|
- @_unbindFromDocumentEvents(@document)
|
|
|
- @_detachDocumentFromEditor(@document)
|
|
|
-
|
|
|
- @document = Document.getDocument @ide, doc_id
|
|
|
-
|
|
|
- @document.join (error) =>
|
|
|
- return callback(error) if error?
|
|
|
- @_bindToDocumentEvents(@document)
|
|
|
- @_bindDocumentToEditor(@document)
|
|
|
- callback null, @document
|
|
|
-
|
|
|
- _bindToDocumentEvents: (document) ->
|
|
|
- document.on "remoteop", () =>
|
|
|
- @undoManager.nextUpdateIsRemote = true
|
|
|
-
|
|
|
- document.on "error", (error) =>
|
|
|
- @openDoc(document.doc_id, forceReopen: true)
|
|
|
-
|
|
|
- Modal.createModal
|
|
|
- title: "Out of sync"
|
|
|
- message: "Sorry, this file has gone out of sync and we need to do a full refresh. Please let us know if this happens frequently."
|
|
|
- buttons:[
|
|
|
- text: "Ok"
|
|
|
- ]
|
|
|
-
|
|
|
- document.on "externalUpdate", () =>
|
|
|
- Modal.createModal
|
|
|
- title: "Document Updated Externally"
|
|
|
- message: "This document was just updated externally. Any recent changes you have made may have been overwritten. To see previous versions please look in the history."
|
|
|
- buttons:[
|
|
|
- text: "Ok"
|
|
|
- ]
|
|
|
-
|
|
|
- _unbindFromDocumentEvents: (document) ->
|
|
|
- document.off()
|
|
|
-
|
|
|
- _bindDocumentToEditor: (document) ->
|
|
|
- $("#editor").show()
|
|
|
- @_bindNewDocToAce(document)
|
|
|
-
|
|
|
- _detachDocumentFromEditor: (document) ->
|
|
|
- document.detachFromAce()
|
|
|
-
|
|
|
- _bindNewDocToAce: (document) ->
|
|
|
- @_createNewSessionFromDocLines(document.getSnapshot().split("\n"))
|
|
|
- @_setReadWritePermission()
|
|
|
- @_bindToAceEvents()
|
|
|
-
|
|
|
- # Updating the doc can cause the cursor to jump around
|
|
|
- # but we shouldn't record that
|
|
|
- @ignoreCursorPositionChanges = true
|
|
|
- document.attachToAce(@aceEditor)
|
|
|
- @ignoreCursorPositionChanges = false
|
|
|
-
|
|
|
- _bindToAceEvents: () ->
|
|
|
- aceDoc = @getDocument()
|
|
|
- aceDoc.on 'change', (change) => @onDocChange(change)
|
|
|
-
|
|
|
- session = @getSession()
|
|
|
- session.on "changeScrollTop", (e) => @onScrollTopChange(e)
|
|
|
- session.selection.on 'changeCursor', (e) => @onCursorChange(e)
|
|
|
-
|
|
|
- _createNewSessionFromDocLines: (docLines) ->
|
|
|
- @aceEditor.setSession(new EditSession(docLines))
|
|
|
- session = @getSession()
|
|
|
- session.setUseWrapMode(true)
|
|
|
- session.setMode(new LatexMode())
|
|
|
- @undoManager = new UndoManager(@)
|
|
|
- session.setUndoManager @undoManager
|
|
|
-
|
|
|
- _setReadWritePermission: () ->
|
|
|
- if !@ide.isAllowedToDoIt 'readAndWrite'
|
|
|
- @makeReadOnly()
|
|
|
- else
|
|
|
- @makeWritable()
|
|
|
-
|
|
|
- onDocChange: (change) ->
|
|
|
- @lastUpdated = new Date()
|
|
|
- @trigger "update:doc", change
|
|
|
-
|
|
|
- onScrollTopChange: (event) ->
|
|
|
- @trigger "scroll", event
|
|
|
- if !@ignoreCursorPositionChanges
|
|
|
- docPosition = $.localStorage("doc.position.#{@current_doc_id}") || {}
|
|
|
- docPosition.scrollTop = @getScrollTop()
|
|
|
- $.localStorage("doc.position.#{@current_doc_id}", docPosition)
|
|
|
-
|
|
|
- onCursorChange: (event) ->
|
|
|
- @trigger "cursor:change", event
|
|
|
- if !@ignoreCursorPositionChanges
|
|
|
- docPosition = $.localStorage("doc.position.#{@current_doc_id}") || {}
|
|
|
- docPosition.cursorPosition = @getCursorPosition()
|
|
|
- $.localStorage("doc.position.#{@current_doc_id}", docPosition)
|
|
|
-
|
|
|
- makeReadOnly: () ->
|
|
|
- @aceEditor.setReadOnly true
|
|
|
-
|
|
|
- makeWritable: () ->
|
|
|
- @aceEditor.setReadOnly false
|
|
|
-
|
|
|
- getSession: () -> @aceEditor.getSession()
|
|
|
-
|
|
|
- getDocument: () -> @getSession().getDocument()
|
|
|
-
|
|
|
- gotoLine: (line) ->
|
|
|
- @aceEditor.gotoLine(line)
|
|
|
-
|
|
|
- getCurrentLine: () ->
|
|
|
- @aceEditor.selection?.getCursor()?.row
|
|
|
-
|
|
|
- getCurrentColumn: () ->
|
|
|
- @aceEditor.selection?.getCursor()?.column
|
|
|
-
|
|
|
- getLines: (from, to) ->
|
|
|
- if from? and to?
|
|
|
- @getSession().doc.getLines(from, to)
|
|
|
- else
|
|
|
- @getSession().doc.getAllLines()
|
|
|
-
|
|
|
- addMarker: (position, klass, type, inFront) ->
|
|
|
- range = new Range(
|
|
|
- position.row, position.column,
|
|
|
- position.row, position.column + position.length
|
|
|
- )
|
|
|
- @getSession().addMarker range, klass, type, inFront
|
|
|
-
|
|
|
- removeMarker: (markerId) ->
|
|
|
- @getSession().removeMarker markerId
|
|
|
-
|
|
|
- getCursorPosition: () -> @aceEditor.getCursorPosition()
|
|
|
- setCursorPosition: (pos) -> @aceEditor.moveCursorToPosition(pos)
|
|
|
-
|
|
|
- getScrollTop: () -> @getSession().getScrollTop()
|
|
|
- setScrollTop: (pos) -> @getSession().setScrollTop(pos)
|
|
|
-
|
|
|
- replaceText: (range, text) ->
|
|
|
- @getSession().replace(new Range(
|
|
|
- range.start.row, range.start.column,
|
|
|
- range.end.row, range.end.column
|
|
|
- ), text)
|
|
|
-
|
|
|
- getContainerElement: () ->
|
|
|
- $(@aceEditor.renderer.getContainerElement())
|
|
|
-
|
|
|
- getCursorElement: () ->
|
|
|
- @getContainerElement().find(".ace_cursor")
|
|
|
-
|
|
|
- textToEditorCoordinates: (x, y) ->
|
|
|
- editorAreaOffset = @getContainerElement().offset()
|
|
|
- {pageX, pageY} = @aceEditor.renderer.textToScreenCoordinates(x, y)
|
|
|
- return {
|
|
|
- x: pageX - editorAreaOffset.left
|
|
|
- y: pageY - editorAreaOffset.top
|
|
|
- }
|
|
|
-
|
|
|
- chaosMonkey: (line = 0, char = "a") ->
|
|
|
- @_cm = setInterval () =>
|
|
|
- @aceEditor.session.insert({row: line, column: 0}, char)
|
|
|
- , 100
|
|
|
-
|
|
|
- clearChaosMonkey: () ->
|
|
|
- clearInterval @_cm
|
|
|
-
|
|
|
- getCurrentDocId: () ->
|
|
|
- @current_doc_id
|
|
|
-
|
|
|
- enable: () ->
|
|
|
- @enabled = true
|
|
|
-
|
|
|
- disable: () ->
|
|
|
- @enabled = false
|
|
|
-
|
|
|
- hasUnsavedChanges: () ->
|
|
|
- Document.hasUnsavedChanges()
|