mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-28 16:01:13 +00:00
302 lines
8.3 KiB
CoffeeScript
302 lines
8.3 KiB
CoffeeScript
define [
|
|
"utils/Modal"
|
|
"pdf/CompiledView"
|
|
"pdf/SyncButtonsView"
|
|
"libs/latex-log-parser"
|
|
"libs/jquery.storage"
|
|
"libs/underscore"
|
|
"libs/backbone"
|
|
], (Modal, CompiledView, SyncButtonsView, LogParser) ->
|
|
class PdfManager
|
|
templates:
|
|
pdfLink: $("#pdfSideBarLinkTemplate").html()
|
|
|
|
constructor: (@ide) ->
|
|
_.extend @, Backbone.Events
|
|
@createSyncButtons()
|
|
@createPdfPanel()
|
|
@ide.editor.aceEditor.commands.addCommand
|
|
name: "compile",
|
|
bindKey: win: "Ctrl-Enter", mac: "Command-Enter"
|
|
exec: (editor) =>
|
|
@refreshPdf()
|
|
readOnly: true
|
|
|
|
@ide.editor.aceEditor.commands.removeCommand "replace"
|
|
|
|
createPdfPanel: () ->
|
|
@view = new CompiledView manager: @, ide: @ide
|
|
@view.on "dblclick", (e) => @syncToCode(e)
|
|
@view.render()
|
|
if $.localStorage("layout.pdf") == "flat"
|
|
@switchToFlatView()
|
|
else if $.localStorage("layout.pdf") == "split"
|
|
@switchToSplitView()
|
|
else if $(window).width() < 1024
|
|
@switchToFlatView()
|
|
else
|
|
@switchToSplitView()
|
|
|
|
createSyncButtons: () ->
|
|
unless @ide.userSettings.pdfViewer == "native"
|
|
@syncButtonsView = new SyncButtonsView(ide: @ide)
|
|
@syncButtonsView.on "click:sync-code-to-pdf", () =>
|
|
@syncToPdf()
|
|
@syncButtonsView.on "click:sync-pdf-to-code", () =>
|
|
@syncToCode()
|
|
@syncButtonsView.hide()
|
|
|
|
switchToFlatView: (options = {showPdf: false}) ->
|
|
@teardownSplitView()
|
|
@setupFlatView()
|
|
@view.toggleFlatViewButton()
|
|
if options.showPdf
|
|
@ide.sideBarView.selectLink "pdf"
|
|
@ide.mainAreaManager.change "pdf"
|
|
@view.resize()
|
|
|
|
switchToSplitView: () ->
|
|
@teardownFlatView()
|
|
@setupSplitView()
|
|
@view.toggleSplitViewButton()
|
|
@view.resize()
|
|
@ide.editor.setIdeToEditorPanel()
|
|
|
|
setupFlatView: () ->
|
|
@teardownFlatView()
|
|
@ide.editor.switchToFlatView()
|
|
pdfLink = $(@templates.pdfLink)
|
|
@ide.sideBarView.addLink
|
|
identifier : "pdf"
|
|
before : "history"
|
|
element : pdfLink
|
|
pdfLink.on "click", (e) => @showPdfPanel()
|
|
|
|
@ide.mainAreaManager.addArea
|
|
identifier: "pdf"
|
|
element: @view.$el
|
|
@view.resize()
|
|
|
|
@view.undelegateEvents()
|
|
@view.delegateEvents()
|
|
|
|
|
|
teardownFlatView: () ->
|
|
@ide.sideBarView.removeLink("pdf")
|
|
@ide.mainAreaManager.removeArea("pdf")
|
|
@view.afterSwitchView()
|
|
|
|
setupSplitView: () ->
|
|
@ide.editor.switchToSplitView()
|
|
@ide.editor.rightPanel.append(
|
|
@view.$el
|
|
)
|
|
@view.$el.show()
|
|
@view.resize()
|
|
|
|
@view.undelegateEvents()
|
|
@view.delegateEvents()
|
|
|
|
@ide.editor.$splitter.append(
|
|
@syncButtonsView?.$el
|
|
)
|
|
|
|
setTimeout(@ide.layoutManager.resizeAllSplitters, 100)
|
|
|
|
teardownSplitView: () ->
|
|
@view.afterSwitchView()
|
|
|
|
showPdfPanel: () ->
|
|
@ide.sideBarView.selectLink 'pdf'
|
|
@ide.mainAreaManager.change 'pdf'
|
|
@view.resize()
|
|
|
|
if !@view.hasPdf()
|
|
@refreshPdf()
|
|
|
|
showRawLogPanel: () ->
|
|
@ide.mainAreaManager.change 'rawLog'
|
|
|
|
refreshPdf: (opts) ->
|
|
if @ide.project?
|
|
@_refreshPdfWhenProjectIsLoaded(opts)
|
|
else
|
|
@ide.on "afterJoinProject", () =>
|
|
@_refreshPdfWhenProjectIsLoaded(opts)
|
|
|
|
_refreshPdfWhenProjectIsLoaded: (opts = {}) ->
|
|
|
|
if !@ide.project.get("rootDoc_id")?
|
|
new Modal
|
|
title: "No root document selected"
|
|
message: "First you need to choose a root document via the settings menu. This tells ShareLaTeX which file to run LaTeX on."
|
|
buttons: [{
|
|
text: "OK",
|
|
class: "btn-primary"
|
|
}]
|
|
else if !@compiling
|
|
@view.onCompiling()
|
|
@syncButtonsView?.hide()
|
|
@compiling = true
|
|
@_doCompile opts, (error, status, outputFiles) =>
|
|
@compiling = false
|
|
@view.doneCompiling()
|
|
@syncButtonsView?.show()
|
|
|
|
if error?
|
|
@view.updateLog(systemError: true)
|
|
@view.unsetPdf()
|
|
@view.showLog()
|
|
else if status == "timedout"
|
|
@view.updateLog(timedOut: true)
|
|
@view.unsetPdf()
|
|
@view.showLog()
|
|
else if status == "autocompile-backoff"
|
|
@view.showBeforeCompile()
|
|
else
|
|
pdfExists = (status == "success")
|
|
@fetchLogAndUpdateView(pdfExists)
|
|
|
|
if pdfExists
|
|
@view.setPdf("/project/#{@ide.project_id}/output/output.pdf?cache_bust=#{Date.now()}")
|
|
@view.showPdf()
|
|
else
|
|
@view.unsetPdf()
|
|
@view.showLog()
|
|
@syncButtonsView?.hide()
|
|
|
|
if outputFiles?
|
|
@view.showOutputFileDownloadLinks(outputFiles)
|
|
|
|
_doCompile: (opts, callback = (error, status, outputFiles) ->) ->
|
|
url = "/project/#{@ide.project_id}/compile"
|
|
if opts.isAutoCompile
|
|
url += "?auto_compile=true"
|
|
$.ajax(
|
|
url: url
|
|
type: "POST"
|
|
headers:
|
|
"X-CSRF-Token": window.csrfToken
|
|
contentType: "application/json; charset=utf-8"
|
|
dataType: 'json'
|
|
data: JSON.stringify settingsOverride:
|
|
rootDoc_id: opts.rootDocOverride_id ? null
|
|
success: (body, status, response) ->
|
|
callback null, body.status, body.outputFiles
|
|
error: (error) ->
|
|
callback error
|
|
)
|
|
|
|
fetchLogAndUpdateView: (pdfExists) ->
|
|
$.ajax(
|
|
url: "/project/#{@ide.project_id}/output/output.log"
|
|
success: (body, status, response) =>
|
|
@parseLogAndUpdateView(pdfExists, body)
|
|
error: () =>
|
|
@view.updateLog(pdfExists: pdfExists, logExists: false)
|
|
)
|
|
|
|
parseLogAndUpdateView: (pdfExists, log) ->
|
|
errors = LogParser.parse(log, ignoreDuplicates: true)
|
|
lastCompileErrors = {}
|
|
for error in errors.all
|
|
error.file = @_normalizeFilePath(error.file)
|
|
|
|
doc_id = @ide.fileTreeManager.getDocIdOfPath(error.file)
|
|
if doc_id?
|
|
lastCompileErrors[doc_id] ||= []
|
|
lastCompileErrors[doc_id].push
|
|
row: error.line - 1
|
|
type: if error.level == "error" then "error" else "warning"
|
|
text: error.message
|
|
@ide.editor.compilationErrors = lastCompileErrors
|
|
@ide.editor.refreshCompilationErrors()
|
|
|
|
@view.updateLog(pdfExists: pdfExists, logExists: true, compileErrors: errors, rawLog: log)
|
|
|
|
_normalizeFilePath: (path) ->
|
|
path = path.replace(/^compiles\/[0-9a-f]{32}\/(\.\/)?/, "")
|
|
path = path.replace(/^\/compile\//, "")
|
|
|
|
rootDoc_id = @ide.project.get("rootDoc_id")
|
|
if rootDoc_id?
|
|
rootDocPath = @ide.fileTreeManager.getPathOfEntityId(rootDoc_id)
|
|
if rootDocPath?
|
|
rootDocDir = rootDocPath.split("/").slice(0,-1).map( (part) -> part + "/" ).join("")
|
|
path = path.replace(/^\.\//, rootDocDir)
|
|
|
|
return path
|
|
|
|
downloadPdf: () ->
|
|
@ide.mainAreaManager.setIframeSrc "/project/#{@ide.project_id}/output/output.pdf?popupDownload=true"
|
|
|
|
deleteCachedFiles: () ->
|
|
modal = new Modal
|
|
title: "Clear cache?"
|
|
message: "This will clear all hidden LaTeX files like .aux, .bbl, etc, from our compile server. You generally don't need to do this unless you're having trouble with references. Your project files will not be deleted or changed."
|
|
buttons: [{
|
|
text: "Cancel"
|
|
}, {
|
|
text: "Clear from cache",
|
|
class: "btn-primary",
|
|
close: false
|
|
callback: ($button) =>
|
|
$button.text("Clearing...")
|
|
$button.prop("disabled", true)
|
|
$.ajax({
|
|
url: "/project/#{@ide.project_id}/output"
|
|
type: "DELETE"
|
|
headers:
|
|
"X-CSRF-Token": window.csrfToken
|
|
complete: () -> modal.remove()
|
|
})
|
|
|
|
}]
|
|
|
|
syncToCode: (e) ->
|
|
if !e?
|
|
e = @view.getPdfPosition()
|
|
return if !e?
|
|
# It's not clear exactly where we should sync to if it was directly
|
|
# clicked on, but a little bit down from the very top seems best.
|
|
e.y = e.y + 80
|
|
|
|
$.ajax {
|
|
url: "/project/#{@ide.project_id}/sync/pdf"
|
|
data:
|
|
page: e.page + 1
|
|
h: e.x.toFixed(2)
|
|
v: e.y.toFixed(2)
|
|
type: "GET"
|
|
success: (response) =>
|
|
data = JSON.parse(response)
|
|
if data.code and data.code.length > 0
|
|
file = data.code[0].file
|
|
line = data.code[0].line
|
|
@ide.fileTreeManager.openDocByPath(file, line)
|
|
}
|
|
|
|
syncToPdf: () ->
|
|
entity_id = @ide.editor.getCurrentDocId()
|
|
file = @ide.fileTreeManager.getPathOfEntityId(entity_id)
|
|
|
|
# If the root file is folder/main.tex, then synctex sees the
|
|
# path as folder/./main.tex
|
|
rootFolderPath = @ide.fileTreeManager.getRootFolderPath()
|
|
if rootFolderPath != ""
|
|
file = file.replace(RegExp("^#{rootFolderPath}"), "#{rootFolderPath}/.")
|
|
|
|
line = @ide.editor.getCurrentLine()
|
|
column = @ide.editor.getCurrentColumn()
|
|
|
|
$.ajax {
|
|
url: "/project/#{@ide.project_id}/sync/code"
|
|
data:
|
|
file: file
|
|
line: line + 1
|
|
column: column
|
|
type: "GET"
|
|
success: (response) =>
|
|
data = JSON.parse(response)
|
|
@view.highlightInPdf(data.pdf or [])
|
|
}
|