diff --git a/services/web/public/coffee/base.coffee b/services/web/public/coffee/base.coffee index c79f8e6d45..6859e31d34 100644 --- a/services/web/public/coffee/base.coffee +++ b/services/web/public/coffee/base.coffee @@ -11,6 +11,7 @@ define [ "underscore" "ngSanitize" "ipCookie" + "pdfViewerApp" ]) - return App \ No newline at end of file + return App diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index 17142eb51d..75bdf68cae 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -6,7 +6,7 @@ define [ "ide/online-users/OnlineUsersManager" "ide/track-changes/TrackChangesManager" "ide/permissions/PermissionsManager" - "ide/pdf/PdfManager" + "ide/pdfng/PdfManager" "ide/binary-files/BinaryFilesManager" "ide/settings/index" "ide/share/index" diff --git a/services/web/public/coffee/ide/pdfng/PdfManager.coffee b/services/web/public/coffee/ide/pdfng/PdfManager.coffee new file mode 100644 index 0000000000..0c3fd7f19b --- /dev/null +++ b/services/web/public/coffee/ide/pdfng/PdfManager.coffee @@ -0,0 +1,21 @@ +define [ + "ide/pdfng/controllers/PdfController" + "ide/pdfng/controllers/PdfViewToggleController" + "ide/pdfng/directives/pdfJs" +], () -> + class PdfManager + constructor: (@ide, @$scope) -> + @$scope.pdf = + url: null # Pdf Url + error: false # Server error + timeout: false # Server timed out + failure: false # PDF failed to compile + compiling: false + uncompiled: true + logEntries: [] + logEntryAnnotations: {} + rawLog: "" + view: null # 'pdf' 'logs' + showRawLog: false + highlights: [] + position: null diff --git a/services/web/public/coffee/ide/pdfng/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdfng/controllers/PdfController.coffee new file mode 100644 index 0000000000..e5eaf6f352 --- /dev/null +++ b/services/web/public/coffee/ide/pdfng/controllers/PdfController.coffee @@ -0,0 +1,293 @@ +define [ + "base" + "libs/latex-log-parser" +], (App, LogParser) -> + App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking) -> + autoCompile = true + $scope.$on "project:joined", () -> + return if !autoCompile + autoCompile = false + $scope.recompile(isAutoCompile: true) + $scope.hasPremiumCompile = $scope.project.features.compileGroup == "priority" + + sendCompileRequest = (options = {}) -> + url = "/project/#{$scope.project_id}/compile" + if options.isAutoCompile + url += "?auto_compile=true" + return $http.post url, { + settingsOverride: + rootDoc_id: options.rootDocOverride_id or null + _csrf: window.csrfToken + } + + parseCompileResponse = (response) -> + # Reset everything + $scope.pdf.error = false + $scope.pdf.timedout = false + $scope.pdf.failure = false + $scope.pdf.uncompiled = false + $scope.pdf.url = null + + if response.status == "timedout" + $scope.pdf.timedout = true + else if response.status == "autocompile-backoff" + $scope.pdf.uncompiled = true + else if response.status == "failure" + $scope.pdf.failure = true + fetchLogs() + else if response.status == "success" + $scope.pdf.url = "/project/#{$scope.project_id}/output/output.pdf?cache_bust=#{Date.now()}" + fetchLogs() + + IGNORE_FILES = ["output.fls", "output.fdb_latexmk"] + $scope.pdf.outputFiles = [] + for file in response.outputFiles + if IGNORE_FILES.indexOf(file.path) == -1 + # Turn 'output.blg' into 'blg file'. + if file.path.match(/^output\./) + file.name = "#{file.path.replace(/^output\./, "")} file" + else + file.name = file.path + $scope.pdf.outputFiles.push file + + fetchLogs = () -> + $http.get "/project/#{$scope.project_id}/output/output.log" + .success (log) -> + $scope.pdf.rawLog = log + logEntries = LogParser.parse(log, ignoreDuplicates: true) + $scope.pdf.logEntries = logEntries + $scope.pdf.logEntries.all = logEntries.errors.concat(logEntries.warnings).concat(logEntries.typesetting) + + $scope.pdf.logEntryAnnotations = {} + for entry in logEntries.all + entry.file = normalizeFilePath(entry.file) + + entity = ide.fileTreeManager.findEntityByPath(entry.file) + if entity? + $scope.pdf.logEntryAnnotations[entity.id] ||= [] + $scope.pdf.logEntryAnnotations[entity.id].push { + row: entry.line - 1 + type: if entry.level == "error" then "error" else "warning" + text: entry.message + } + + .error () -> + $scope.pdf.logEntries = [] + $scope.pdf.rawLog = "" + + getRootDocOverride_id = () -> + doc = ide.editorManager.getCurrentDocValue() + return null if !doc? + for line in doc.split("\n") + match = line.match /(.*)\\documentclass/ + if match and !match[1].match /%/ + return ide.editorManager.getCurrentDocId() + return null + + normalizeFilePath = (path) -> + path = path.replace(/^(.*)\/compiles\/[0-9a-f]{24}\/(\.\/)?/, "") + path = path.replace(/^\/compile\//, "") + + rootDocDirname = ide.fileTreeManager.getRootDocDirname() + if rootDocDirname? + path = path.replace(/^\.\//, rootDocDirname + "/") + + return path + + compileCount = 0 + $scope.recompile = (options = {}) -> + return if $scope.pdf.compiling + $scope.pdf.compiling = true + + if !options.isAutoCompile + compileCount++ + if compileCount == 1 + event_tracking.send('editor-interaction', 'single-compile') + else if compileCount == 3 + event_tracking.send('editor-interaction', 'multi-compile') + + options.rootDocOverride_id = getRootDocOverride_id() + + sendCompileRequest(options) + .success (data) -> + $scope.pdf.view = "pdf" + $scope.pdf.compiling = false + parseCompileResponse(data) + .error () -> + $scope.pdf.compiling = false + $scope.pdf.error = true + + # This needs to be public. + ide.$scope.recompile = $scope.recompile + + $scope.clearCache = () -> + $http { + url: "/project/#{$scope.project_id}/output" + method: "DELETE" + headers: + "X-Csrf-Token": window.csrfToken + } + + $scope.toggleLogs = () -> + if !$scope.pdf.view? or $scope.pdf.view == "pdf" + $scope.pdf.view = "logs" + else + $scope.pdf.view = "pdf" + + $scope.showPdf = () -> + $scope.pdf.view = "pdf" + + $scope.toggleRawLog = () -> + $scope.pdf.showRawLog = !$scope.pdf.showRawLog + + $scope.openOutputFile = (file) -> + window.open("/project/#{$scope.project_id}/output/#{file.path}") + + $scope.openClearCacheModal = () -> + modalInstance = $modal.open( + templateUrl: "clearCacheModalTemplate" + controller: "ClearCacheModalController" + scope: $scope + ) + + $scope.syncToCode = (position) -> + synctex + .syncToCode(position) + .then (data) -> + {doc, line} = data + ide.editorManager.openDoc(doc, gotoLine: line) + + $scope.switchToFlatLayout = () -> + $scope.ui.pdfLayout = 'flat' + $scope.ui.view = 'pdf' + $.localStorage "pdf.layout", "flat" + + $scope.switchToSideBySideLayout = () -> + $scope.ui.pdfLayout = 'sideBySide' + $scope.ui.view = 'editor' + $.localStorage "pdf.layout", "split" + + if pdfLayout = $.localStorage("pdf.layout") + $scope.switchToSideBySideLayout() if pdfLayout == "split" + $scope.switchToFlatLayout() if pdfLayout == "flat" + else + $scope.switchToSideBySideLayout() + + App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) -> + synctex = + syncToPdf: (cursorPosition) -> + deferred = $q.defer() + + doc_id = ide.editorManager.getCurrentDocId() + if !doc_id? + deferred.reject() + return deferred.promise + doc = ide.fileTreeManager.findEntityById(doc_id) + if !doc? + deferred.reject() + return deferred.promise + path = ide.fileTreeManager.getEntityPath(doc) + if !path? + deferred.reject() + return deferred.promise + + # If the root file is folder/main.tex, then synctex sees the + # path as folder/./main.tex + rootDocDirname = ide.fileTreeManager.getRootDocDirname() + if rootDocDirname? and rootDocDirname != "" + path = path.replace(RegExp("^#{rootDocDirname}"), "#{rootDocDirname}/.") + + {row, column} = cursorPosition + + $http({ + url: "/project/#{ide.project_id}/sync/code", + method: "GET", + params: { + file: path + line: row + 1 + column: column + } + }) + .success (data) -> + deferred.resolve(data.pdf or []) + .error (error) -> + deferred.reject(error) + + return deferred.promise + + syncToCode: (position, options = {}) -> + deferred = $q.defer() + if !position? + deferred.reject() + return deferred.promise + + # It's not clear exactly where we should sync to if it wasn't directly + # clicked on, but a little bit down from the very top seems best. + if options.includeVisualOffset + position.offset.top = position.offset.top + 80 + + $http({ + url: "/project/#{ide.project_id}/sync/pdf", + method: "GET", + params: { + page: position.page + 1 + h: position.offset.left.toFixed(2) + v: position.offset.top.toFixed(2) + } + }) + .success (data) -> + if data.code? and data.code.length > 0 + doc = ide.fileTreeManager.findEntityByPath(data.code[0].file) + return if !doc? + deferred.resolve({doc: doc, line: data.code[0].line}) + .error (error) -> + deferred.reject(error) + + return deferred.promise + + return synctex + ] + + App.controller "PdfSynctexController", ["$scope", "synctex", "ide", ($scope, synctex, ide) -> + @cursorPosition = null + ide.$scope.$on "cursor:editor:update", (event, @cursorPosition) => + + $scope.syncToPdf = () => + return if !@cursorPosition? + synctex + .syncToPdf(@cursorPosition) + .then (highlights) -> + $scope.pdf.highlights = highlights + + $scope.syncToCode = () -> + synctex + .syncToCode($scope.pdf.position, includeVisualOffset: true) + .then (data) -> + {doc, line} = data + ide.editorManager.openDoc(doc, gotoLine: line) + ] + + App.controller "PdfLogEntryController", ["$scope", "ide", ($scope, ide) -> + $scope.openInEditor = (entry) -> + entity = ide.fileTreeManager.findEntityByPath(entry.file) + return if !entity? or entity.type != "doc" + if entry.line? + line = entry.line + ide.editorManager.openDoc(entity, gotoLine: line) + ] + + App.controller 'ClearCacheModalController', ["$scope", "$modalInstance", ($scope, $modalInstance) -> + $scope.state = + inflight: false + + $scope.clear = () -> + $scope.state.inflight = true + $scope + .clearCache() + .then () -> + $scope.state.inflight = false + $modalInstance.close() + + $scope.cancel = () -> + $modalInstance.dismiss('cancel') + ] \ No newline at end of file diff --git a/services/web/public/coffee/ide/pdfng/controllers/PdfViewToggleController.coffee b/services/web/public/coffee/ide/pdfng/controllers/PdfViewToggleController.coffee new file mode 100644 index 0000000000..156d46dd3e --- /dev/null +++ b/services/web/public/coffee/ide/pdfng/controllers/PdfViewToggleController.coffee @@ -0,0 +1,17 @@ +define [ + "base" +], (App) -> + App.controller "PdfViewToggleController", ($scope) -> + $scope.togglePdfView = () -> + if $scope.ui.view == "pdf" + $scope.ui.view = "editor" + else + $scope.ui.view = "pdf" + + $scope.fileTreeClosed = false + $scope.$on "layout:main:resize", (e, state) -> + if state.west.initClosed + $scope.fileTreeClosed = true + else + $scope.fileTreeClosed = false + $scope.$apply() \ No newline at end of file diff --git a/services/web/public/coffee/ide/pdfng/directives/pdfAnnotations.coffee b/services/web/public/coffee/ide/pdfng/directives/pdfAnnotations.coffee new file mode 100644 index 0000000000..a2d9d1751a --- /dev/null +++ b/services/web/public/coffee/ide/pdfng/directives/pdfAnnotations.coffee @@ -0,0 +1,42 @@ +app = angular.module 'pdfAnnotations', [] + +app.factory 'pdfAnnotations', [ () -> + class pdfAnnotations + + @EXTERNAL_LINK_TARGET = "_blank"; + + constructor: (options) -> + @annotationsLayerDiv = options.annotations; + @viewport = options.viewport + @navigateFn = options.navigateFn + + setAnnotations: (annotations) -> + for annotation in annotations + switch annotation.subtype + when 'Link' then @addLink(annotation); + when 'Text' then continue + + addLink: (link) -> + element = @buildLinkElementFromRect(link.rect); + @setLinkTarget(element, link); + @annotationsLayerDiv.appendChild(element); + + buildLinkElementFromRect: (rect) -> + rect = @viewport.convertToViewportRectangle(rect); + rect = PDFJS.Util.normalizeRect(rect); + element = document.createElement("a"); + 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'; + element + + setLinkTarget: (element, link) -> + if link.url + element.href = link.url; + element.target = @EXTERNAL_LINK_TARGET; + else if (link.dest) + element.href = "#" + link.dest; + element.onclick = (e) => + @navigateFn link +] diff --git a/services/web/public/coffee/ide/pdfng/directives/pdfHighlights.coffee b/services/web/public/coffee/ide/pdfng/directives/pdfHighlights.coffee new file mode 100644 index 0000000000..e3fd2f3ac2 --- /dev/null +++ b/services/web/public/coffee/ide/pdfng/directives/pdfHighlights.coffee @@ -0,0 +1,27 @@ +app = angular.module 'pdfHighlights', [] + +app.factory 'pdfHighlights', [ () -> + class pdfHighlights + + constructor: (options) -> + @highlightsLayerDiv = options.highlights + @viewport = options.viewport + @highlightElements = [] + + addHighlight: (left, top, width, height) -> + rect = @viewport.convertToViewportRectangle([left, top, left + width, top + height]) + rect = PDFJS.Util.normalizeRect(rect) + 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' + @highlightElements.push(element) + @highlightsLayerDiv.appendChild(element) + element + + clearHighlights: () -> + for h in @highlightElements + h.remove() + @highlightElements = [] +] diff --git a/services/web/public/coffee/ide/pdfng/directives/pdfJs.coffee b/services/web/public/coffee/ide/pdfng/directives/pdfJs.coffee new file mode 100644 index 0000000000..32b09dc293 --- /dev/null +++ b/services/web/public/coffee/ide/pdfng/directives/pdfJs.coffee @@ -0,0 +1,198 @@ +define [ + "base" + "ide/pdfng/directives/pdfViewer" + "ide/pdfng/directives/pdfPage" + "ide/pdfng/directives/pdfRenderer" + "ide/pdfng/directives/pdfTextLayer" + "ide/pdfng/directives/pdfAnnotations" + "ide/pdfng/directives/pdfHighlights" + "libs/pdf" + "text!libs/pdfListView/TextLayer.css" + "text!libs/pdfListView/AnnotationsLayer.css" + "text!libs/pdfListView/HighlightsLayer.css" +], ( + App + pdfViewerApp + pdfPage + pdfRenderer + pdfTextLayer + pdfAnnotations + pdfHighlights + pdf + textLayerCss + annotationsLayerCss + highlightsLayerCss +) -> + if PDFJS? + PDFJS.workerSrc = window.pdfJsWorkerPath + PDFJS.disableAutoFetch = true + + style = $("") + style.text(textLayerCss + "\n" + annotationsLayerCss + "\n" + highlightsLayerCss) + $("body").append(style) + + App.directive "pdfjs", ["$timeout", ($timeout) -> + return { + scope: { + "pdfSrc": "=" + "highlights": "=" + "position": "=" + "dblClickCallback": "=" + } + link: (scope, element, attrs) -> + # pdfListView = new PDFListView element.find(".pdfjs-viewer")[0], + # textLayerBuilder: TextLayerBuilder + # annotationsLayerBuilder: AnnotationsLayerBuilder + # highlightsLayerBuilder: HighlightsLayerBuilder + # ondblclick: (e) -> onDoubleClick(e) + # # logLevel: PDFListView.Logger.DEBUG + # pdfListView.listView.pageWidthOffset = 20 + # pdfListView.listView.pageHeightOffset = 20 + + scope.loading = false + scope.scale = {} + initializedPosition = false + initializePosition = () -> + return if initializedPosition + initializedPosition = true + + if (scale = $.localStorage("pdf.scale"))? + #pdfListView.setScaleMode(scale.scaleMode, scale.scale) + else + scope.scale = { scaleMode: 'scale_mode_fit_width' } + + if (position = $.localStorage("pdf.position.#{attrs.key}")) + 1 + #pdfListView.setPdfPosition(position) + + #scope.position = pdfListView.getPdfPosition(true) + + $(window).unload () => + $.localStorage "pdf.scale", { +# scaleMode: pdfListView.getScaleMode() +# scale: pdfListView.getScale() + } +# $.localStorage "pdf.position.#{attrs.key}", pdfListView.getPdfPosition() + + flashControls = () -> + scope.flashControls = true + $timeout () -> + scope.flashControls = false + , 1000 + + element.find(".pdfjs-viewer").scroll () -> +# scope.position = pdfListView.getPdfPosition(true) + + onDoubleClick = (e) -> + scope.dblClickCallback?(page: e.page, offset: { top: e.y, left: e.x }) + + scope.$watch "pdfSrc", (url) -> + if url + scope.loading = true + scope.progress = 0 + console.log 'pdfSrc =', url + initializePosition() + flashControls() + scope.$broadcast 'layout-ready' + # pdfListView + # .loadPdf(url, onProgress) + # .then () -> + # scope.$apply () -> + # scope.loading = false + # delete scope.progress + # initializePosition() + # flashControls() + + scope.$watch "highlights", (areas) -> + return if !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 - 80 + # }, true) + + # pdfListView.clearHighlights() + # pdfListView.setHighlights(highlights, true) + + # setTimeout () => + # pdfListView.clearHighlights() + # , 1000 + + scope.fitToHeight = () -> +# pdfListView.setToFitHeight() + + scope.fitToWidth = () -> +# pdfListView.setToFitWidth() + + scope.zoomIn = () -> +# scale = pdfListView.getScale() +# pdfListView.setScale(scale * 1.2) + + scope.zoomOut = () -> +# scale = pdfListView.getScale() +# pdfListView.setScale(scale / 1.2) + + if attrs.resizeOn? + for event in attrs.resizeOn.split(",") + scope.$on event, (e) -> + console.log 'got a resize event', event, e +# pdfListView.onResize() + + template: """ +
+ + """ + } + ] diff --git a/services/web/public/coffee/ide/pdfng/directives/pdfPage.coffee b/services/web/public/coffee/ide/pdfng/directives/pdfPage.coffee new file mode 100644 index 0000000000..3f0293d81f --- /dev/null +++ b/services/web/public/coffee/ide/pdfng/directives/pdfPage.coffee @@ -0,0 +1,80 @@ +app = angular.module 'pdfPage', [] + +app.directive 'pdfPage', ['$timeout', ($timeout) -> + { + require: '^pdfViewer', + template: ''' + + + + + ''' + link: (scope, element, attrs, ctrl) -> + canvasElement = $(element).find('.pdf-canvas') + textElement = $(element).find('.text-layer') + annotationsElement = $(element).find('.annotations-layer') + highlightsElement = $(element).find('.highlights-layer') + + updatePageSize = (size) -> + element.height(Math.floor(size[0])) + element.width(Math.floor(size[1])) + scope.page.sized = true + + isVisible = (containerSize) -> + elemTop = element.offset().top - containerSize[2] + elemBottom = elemTop + element.innerHeight() + visible = (elemTop < containerSize[1] and elemBottom > 0) + scope.page.visible = visible + scope.page.elemTop = elemTop + scope.page.elemBottom = elemBottom + return visible + + renderPage = () -> + scope.document.renderPage { + canvas: canvasElement, + text: textElement + annotations: annotationsElement + highlights: highlightsElement + }, scope.page.pageNum + + pausePage = () -> + scope.document.pause { + canvas: canvasElement, + text: textElement + }, scope.page.pageNum + + # keep track of our page element, so we can access it in the + # parent with scope.pages[i].element + scope.page.element = element + + if (!scope.page.sized && scope.defaultPageSize) + updatePageSize scope.defaultPageSize + + if scope.page.current + console.log 'we must scroll to this page', scope.page.pageNum, + 'at position', scope.page.position + renderPage() + # this is the current page, we want to scroll it into view + scope.document.getPdfViewport(scope.page.pageNum).then (viewport) -> + scope.page.viewport = viewport + ctrl.setPdfPosition(scope.page, scope.page.position) + + scope.$watch 'defaultPageSize', (defaultPageSize) -> + return unless defaultPageSize? + updatePageSize defaultPageSize + + watchHandle = scope.$watch 'containerSize', (containerSize, oldVal) -> + return unless containerSize? + return unless scope.page.sized + oldVisible = scope.page.visible + newVisible = isVisible containerSize + scope.page.visible = newVisible + if newVisible && !oldVisible + renderPage() + # TODO deregister this listener after the page is rendered + #watchHandle() + else if !newVisible && oldVisible + pausePage() + + } +] diff --git a/services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee b/services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee new file mode 100644 index 0000000000..8957fd83c9 --- /dev/null +++ b/services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee @@ -0,0 +1,185 @@ +app = angular.module 'PDFRenderer', ['pdfAnnotations', 'pdfTextLayer'] + +console.log 'hello from Renderer' + +app.factory 'PDFRenderer', ['$q', '$timeout', 'pdfAnnotations', 'pdfTextLayer', ($q, $timeout, pdfAnnotations, pdfTextLayer) -> + + class PDFRenderer + @JOB_QUEUE_INTERVAL: 100 + + constructor: (@url, @options) -> + @scale = @options.scale || 1 + @document = $q.when(PDFJS.getDocument @url) + @navigateFn = @options.navigateFn + @resetState() + + resetState: () -> + console.log 'reseting renderer state' + @page = [] + @complete = [] + @timeout = [] + @renderTask = [] + @renderQueue = [] + @jobs = 0 + + getNumPages: () -> + @document.then (pdfDocument) -> + pdfDocument.numPages + + getPage: (pageNum) -> + # with promise caching + return @page[pageNum] if @page[pageNum]? + @page[pageNum] = @document.then (pdfDocument) -> + pdfDocument.getPage(pageNum) + + getPdfViewport: (pageNum, scale) -> + scale ?= @scale + @document.then (pdfDocument) -> + pdfDocument.getPage(pageNum).then (page) -> + viewport = page.getViewport scale + + getDestinations: () -> + @document.then (pdfDocument) -> + pdfDocument.getDestinations() + + getPageIndex: (ref) -> + @document.then (pdfDocument) -> + pdfDocument.getPageIndex(ref).then (idx) -> + idx + + getScale: () -> + @scale + + setScale: (@scale) -> + @resetState() + + pause: (element, pagenum) -> + return if @complete[pagenum] + @renderQueue = @renderQueue.filter (q) -> + q.pagenum != pagenum + @stopSpinner (element.canvas) + + triggerRenderQueue: () -> + $timeout () => + @processRenderQueue() + , @JOB_QUEUE_INTERVAL + + removeCompletedJob: (pagenum) -> + # may need to clean up deferred object here + delete @renderTask[pagenum] + @jobs = @jobs - 1 + @triggerRenderQueue() + + renderPage: (element, pagenum) -> + viewport = $q.defer() + current = { + 'element': element + 'pagenum': pagenum + } + @renderQueue.push(current) + @triggerRenderQueue() + + processRenderQueue: () -> + return if @jobs > 0 + current = @renderQueue.pop() + return unless current? + [element, pagenum] = [current.element, current.pagenum] + return if @complete[pagenum] + return if @renderTask[pagenum] + @jobs = @jobs + 1 + + @addSpinner(element.canvas) + + pageLoad = @getPage(pagenum) + + @renderTask[pagenum] = pageLoad.then (pageObject) => + @doRender element, pagenum, pageObject + + @renderTask[pagenum].then () => + # complete + @complete[pagenum] = true + @removeCompletedJob pagenum + , () => + # rejected + @removeCompletedJob pagenum + + doRender: (element, pagenum, page) -> + self = this + scale = @scale + + if (not scale?) + console.log 'scale is undefined, returning' + return + + canvas = $('') + + viewport = page.getViewport (scale) + + devicePixelRatio = window.devicePixelRatio || 1 + + ctx = canvas[0].getContext '2d' + backingStoreRatio = ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1 + pixelRatio = devicePixelRatio / backingStoreRatio + + scaledWidth = (Math.floor(viewport.width) * pixelRatio) | 0 + scaledHeight = (Math.floor(viewport.height) * pixelRatio) | 0 + + newWidth = Math.floor(viewport.width) + newHeight = Math.floor(viewport.height) + + canvas[0].height = scaledHeight + canvas[0].width = scaledWidth + + canvas.height(newHeight + 'px') + canvas.width(newWidth + 'px') + + element.canvas[0].height = newHeight + element.canvas[0].width = newWidth + + if pixelRatio != 1 + ctx.scale(pixelRatio, pixelRatio) + + textLayer = new pdfTextLayer({ + textLayerDiv: element.text[0] + viewport: viewport + }) + page.getTextContent().then (textContent) -> + console.log 'text content is', textContent + window.RENDER_DELAY = 0 + textLayer.setTextContent textContent + + annotationsLayer = new pdfAnnotations({ + annotations: element.annotations[0] + viewport: viewport + navigateFn: @navigateFn + }) + page.getAnnotations().then (annotations) -> + console.log 'annotations are', annotations + window.RENDER_DELAY = 0 + annotationsLayer.setAnnotations annotations + + return @renderTask = page.render { + canvasContext: ctx + viewport: viewport + } + .then () -> + element.canvas.replaceWith(canvas) + canvas.removeClass('pdf-canvas-new') + + addSpinner: (element) -> + element.css({position: 'relative'}) + h = element.parent().height() + w = element.parent().width() + size = Math.floor(0.5 * Math.min(h, w)) + spinner = $('