Przeglądaj źródła

Merge branch 'master' of github.com:sharelatex/web-sharelatex

Henry Oswald 12 lat temu
rodzic
commit
811859a4a1

+ 3 - 0
services/web/app/coffee/Features/Compile/CompileController.coffee

@@ -35,6 +35,9 @@ module.exports = CompileController =
 	getFileFromClsi: (req, res, next = (error) ->) ->
 		CompileController.proxyToClsi("/project/#{req.params.Project_id}/output/#{req.params.file}", req, res, next)
 
+	proxySync: (req, res, next = (error) ->) ->
+		CompileController.proxyToClsi(req.url, req, res, next)
+
 	proxyToClsi: (url, req, res, next = (error) ->) ->
 		logger.log url: url, "proxying to CLSI"
 		url = "#{Settings.apis.clsi.url}#{url}"

+ 3 - 0
services/web/app/coffee/router.coffee

@@ -109,6 +109,9 @@ module.exports = class Router
 				next()
 			), SecurityManager.requestCanAccessProject, CompileController.getFileFromClsi
 		app.del "/project/:Project_id/output", SecurityManager.requestCanAccessProject, CompileController.deleteAuxFiles
+		app.get "/project/:Project_id/sync/code", SecurityManager.requestCanAccessProject, CompileController.proxySync
+		app.get "/project/:Project_id/sync/pdf", SecurityManager.requestCanAccessProject, CompileController.proxySync
+
 
 		app.del  '/Project/:Project_id', SecurityManager.requestIsOwner, ProjectController.deleteProject
 		app.post  '/Project/:Project_id/clone', SecurityManager.requestCanAccessProject, ProjectController.cloneProject

+ 6 - 0
services/web/public/coffee/editor/Editor.coffee

@@ -304,6 +304,12 @@ define [
 		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)

+ 4 - 0
services/web/public/coffee/pdf/CompiledView.coffee

@@ -64,6 +64,7 @@ define [
 				PdfView = PdfjsView
 				@pdfjs = true
 			@pdfView = new PdfView(manager: @)
+			@pdfView.on "dblclick", (e) => @trigger "dblclick", e
 
 		render: () ->
 			@setElement(@templates.pdfPanel)
@@ -214,4 +215,7 @@ define [
 			Backbone.View::undelegateEvents.apply(this, arguments)
 			@pdfView.undelegateEvents()
 
+		highlightInPdf: (args...) ->
+			@pdfView.highlightInPdf?(args...)
+
 			

+ 39 - 2
services/web/public/coffee/pdf/PDFjsView.coffee

@@ -2,16 +2,18 @@ define [
 	"libs/pdfListView/PdfListView"
 	"libs/pdfListView/TextLayerBuilder"
 	"libs/pdfListView/AnnotationsLayerBuilder"
+	"libs/pdfListView/HighlightsLayerBuilder"
 	"text!libs/pdfListView/TextLayer.css"
 	"text!libs/pdfListView/AnnotationsLayer.css"
+	"text!libs/pdfListView/HighlightsLayer.css"
 	"libs/backbone"
 	"libs/jquery.storage"
-], (PDFListView, TextLayerBuilder, AnnotationsLayerBuilder, textLayerCss, annotationsLayerCss) ->
+], (PDFListView, TextLayerBuilder, AnnotationsLayerBuilder, HighlightsLayerBuilder, textLayerCss, annotationsLayerCss, highlightsLayerCss) ->
 	if PDFJS?
 		PDFJS.workerSrc = "#{window.sharelatex.pdfJsWorkerPath}"
 
 	style = $("<style/>")
-	style.text(textLayerCss + "\n" + annotationsLayerCss)
+	style.text(textLayerCss + "\n" + annotationsLayerCss + "\n" + highlightsLayerCss)
 	$("body").append(style)
 
 	PDFjsView = Backbone.View.extend
@@ -33,6 +35,9 @@ define [
 			@pdfListView = new PDFListView @$(".pdfjs-list-view")[0],
 				textLayerBuilder: TextLayerBuilder
 				annotationsLayerBuilder: AnnotationsLayerBuilder
+				highlightsLayerBuilder: HighlightsLayerBuilder
+				ondblclick: (e) =>
+					@trigger "dblclick", e
 				#logLevel: PDFListView.Logger.DEBUG
 			@pdfListView.listView.pageWidthOffset = 20
 			@pdfListView.listView.pageHeightOffset = 20
@@ -121,3 +126,35 @@ define [
 		onResize: () ->
 			@pdfListView.onResize()
 
+		highlightInPdf: (areas) ->
+			highlights = for area in (areas or [])
+				{
+					page: area.page - 1
+					highlight:
+						left: area.h
+						top: area.v
+						height: area.height
+						width: area.width
+				}
+
+			if highlights.length > 0
+				first = highlights[0]
+				@pdfListView.setPdfPosition({
+					page: first.page
+					offset:
+						left: first.highlight.left
+						top: first.highlight.top - 100
+				}, true)
+
+			@pdfListView.clearHighlights()
+			@pdfListView.setHighlights(highlights, true)
+			
+			setTimeout () =>
+				@$(".pdfjs-list-view .plv-highlights-layer > div").fadeOut "slow", () =>
+					@pdfListView.clearHighlights()
+			, 1000
+
+
+
+			
+

+ 35 - 0
services/web/public/coffee/pdf/PdfManager.coffee

@@ -24,6 +24,7 @@ define [
 
 		createPdfPanel: () ->
 			@view = new CompiledView manager: @, ide: @ide
+			@view.on "dblclick", (e) => @syncToCode(e)
 			@view.render()
 			if $.localStorage("layout.pdf") == "flat"
 				@switchToFlatView()
@@ -210,3 +211,37 @@ define [
 						})
 
 				}]
+
+		syncToCode: (e) ->
+			$.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)
+			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 [])
+			}

+ 16 - 0
services/web/public/js/libs/pdfListView/HighlightsLayer.css

@@ -0,0 +1,16 @@
+.plv-highlights-layer {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  overflow: hidden;
+  pointer-events: none;
+}
+.plv-highlights-layer > div {
+  display: block;
+  position: absolute;
+  pointer-events: auto;
+  background-color: yellow;
+  opacity: 0.4;
+}

+ 39 - 0
services/web/public/js/libs/pdfListView/HighlightsLayerBuilder.js

@@ -0,0 +1,39 @@
+(function (name, context, definition) {
+  if (typeof module != 'undefined' && module.exports) module.exports = definition()
+  else if (typeof define == 'function' && define.amd) define(definition)
+  else context[name] = definition()
+})('HighlightsLayerBuilder', this, function (name, context) {
+
+function HighlightsLayerBuilder(pageView, highlightsLayerDiv) {
+    this.highlightsLayerDiv = highlightsLayerDiv;
+    this.pageView = pageView;
+    this.highlightElements = [];
+};
+
+HighlightsLayerBuilder.EXTERNAL_LINK_TARGET = "_blank";
+
+HighlightsLayerBuilder.prototype = {
+    addHighlight: function(left, top, width, height) {
+        rect = this.pageView.viewport.convertToViewportRectangle([left, top, left + width, top + height]);
+        rect = PDFJS.Util.normalizeRect(rect);
+        var element = document.createElement("div");
+        element.style.left = Math.floor(rect[0]) + 'px';
+        element.style.top = Math.floor(rect[1]) + 'px';
+        element.style.width = Math.ceil(rect[2] - rect[0]) + 'px';
+        element.style.height = Math.ceil(rect[3] - rect[1]) + 'px';
+        this.highlightElements.push(element);
+        this.highlightsLayerDiv.appendChild(element);
+        return element;
+    },
+
+    clearHighlights: function() {
+        for (var i = 0; i < this.highlightElements.length; i++) {
+            this.highlightElements[i].remove();
+        }
+        this.highlightElements = [];
+    }
+}
+
+return HighlightsLayerBuilder;
+
+});

+ 94 - 4
services/web/public/js/libs/pdfListView/PdfListView.js

@@ -215,6 +215,16 @@ ListView.prototype = {
         // per pageContainer.
         this.pdfDoc.pages.map(function(page) {
             var pageView = new PageView(page, this);
+
+            // TODO: Switch over to a proper event handler
+            var that = this;
+            var index = that.pageViews.length;
+            pageView.ondblclick = function(e) {
+                e.page = index;
+                if (that.ondblclick) {
+                    that.ondblclick.call(that, e);
+                }
+            }
             this.pageViews.push(pageView);
 
             var container = new PageContainerView(this);
@@ -378,14 +388,31 @@ ListView.prototype = {
         return pdfPosition;
     },
 
-    setPdfPosition: function(pdfPosition) {
+    setPdfPosition: function(pdfPosition, fromTop) {
         if (typeof pdfPosition !== "undefined" && pdfPosition != null) {
             var offset = pdfPosition.offset;
             var page_index = pdfPosition.page;
             var pageView = this.pageViews[page_index];
+            if (fromTop) {
+                offset.top = pageView.normalHeight - offset.top;
+            }
             var position = pageView.getPdfPositionInViewer(offset.left, offset.top);
             this.dom.scrollTop = position.top;
         }
+    },
+
+    setHighlights: function(highlights, fromTop) {
+        for (i = 0; i < highlights.length; i++) {
+            var pageIndex = highlights[i].page;
+            var pageView = this.pageViews[pageIndex];
+            pageView.addHighlight(highlights[i].highlight, fromTop);
+        }
+    },
+
+    clearHighlights: function() {
+        for (var i = 0; i < this.pageViews.length; i++) {
+            var pageView = this.pageViews[i].clearHighlights();
+        }
     }
 };
 
@@ -462,6 +489,27 @@ function PageView(page, listView) {
 
     var dom = this.dom = document.createElement('div');
     dom.className = "plv-page-view page-view";
+    var that = this;
+    dom.ondblclick = function(e) {
+        var layerX = e.layerX;
+        var layerY = e.layerY;
+        var element = e.target;
+        while (element.offsetParent && element.offsetParent !== dom) {
+            layerX = layerX + element.offsetLeft;
+            layerY = layerY + element.offsetTop;
+            element = element.offsetParent;
+        }
+
+        var pdfPoint = that.viewport.convertToPdfPoint(layerX, layerY);
+        var event = {
+            x: pdfPoint[0],
+            y: that.normalHeight - pdfPoint[1]
+        };
+
+        if (that.ondblclick) {
+            that.ondblclick.call(that.listView, event)
+        }
+    }
     this.createNewCanvas();
 }
 
@@ -560,6 +608,7 @@ PageView.prototype = {
             scaled: pixelRatio != 1
         };
     },
+
     createNewCanvas: function() {
         if (this.canvas) {
             this.dom.removeChild(this.canvas);
@@ -628,6 +677,25 @@ PageView.prototype = {
         }
         var pdfOffset = this.viewport.convertToPdfPoint(0, canvasOffset);
         return pdfOffset[1];
+    },
+
+    clearHighlights: function() {
+        if (this.highlightsLayer) {
+            this.highlightsLayer.clearHighlights();
+        }
+    },
+
+    addHighlight: function(highlight, fromTop) {
+        if (this.highlightsLayer) {
+            var top = highlight.top;
+            var left = highlight.left;
+            var width = highlight.width;
+            var height = highlight.height;
+            if (fromTop) {
+                top = this.normalHeight - top;
+            }
+            this.highlightsLayer.addHighlight(left, top, width, height);
+        }
     }
 };
 
@@ -716,6 +784,14 @@ Page.prototype = {
                 );
             }
 
+            var highlightsLayerBuilder = pageView.listView.options.highlightsLayerBuilder;
+            if (highlightsLayerBuilder) {
+                var highlightsLayerDiv = pageView.highlightsLayerDiv = document.createElement("div");
+                highlightsLayerDiv.className = 'plv-highlights-layer highlights-layer';
+                pageView.dom.appendChild(highlightsLayerDiv);
+                pageView.highlightsLayer = new highlightsLayerBuilder(pageView, highlightsLayerDiv);
+            }
+
             renderContext = {
               canvasContext: pageView.getCanvasContext(),
               viewport: viewport,
@@ -766,13 +842,19 @@ function PDFListView(mainDiv, options) {
     }
     logger.logLevel = options.logLevel;
 
+    var self = this;
+
     this.listView = new ListView(mainDiv, options);
+    this.listView.ondblclick = function(e) {
+        if (options.ondblclick) {
+            options.ondblclick.call(self, e);
+        }
+    }
 
     this.renderController = new RenderController();
     this.renderController.addListView(this.listView);
     this.renderController.updateRenderList();
 
-    var self = this;
 
     mainDiv.addEventListener('scroll', function() {
         // This will update the list AND start rendering if needed.
@@ -844,8 +926,16 @@ PDFListView.prototype = {
         return this.listView.getPdfPosition();
     },
 
-    setPdfPosition: function(pdfPosition) {
-        this.listView.setPdfPosition(pdfPosition)
+    setPdfPosition: function(pdfPosition, fromTop) {
+        this.listView.setPdfPosition(pdfPosition, fromTop);
+    },
+
+    setHighlights: function(highlights, fromTop) {
+        this.listView.setHighlights(highlights, fromTop);
+    },
+
+    clearHighlights: function() {
+        this.listView.clearHighlights();
     }
 };
 PDFListView.Logger = Logger;

+ 7 - 0
services/web/public/js/libs/pdfListView/TextLayerBuilder.js

@@ -84,6 +84,13 @@ TextLayerBuilder.prototype = {
         textDiv.style.left = geom.x + 'px';
         textDiv.style.top = (geom.y - fontHeight) + 'px';
 
+        textDiv.ondblclick = function(e) {
+            if (window.getSelection)
+                window.getSelection().removeAllRanges();
+            else if (document.selection)
+                document.selection.empty();
+        }
+
         // The content of the div is set in the `setTextContent` function.
 
         this.textDivs.push(textDiv);