/* eslint indent: ["error", 2, { "outerIIFEBody": 0, "SwitchCase": 1 }] */ /** * @copyright Copyright (C) 2018, struktur AG * * @author Joachim Bauch * * @license AGPL-3.0 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ import $ from 'jquery'; import 'jquery-ui/ui/widgets/draggable'; import { each, throttle } from 'lodash'; import io from 'socket.io-client'; import iro from '@jaames/iro'; import paper from 'paper'; import { pdfjsLib, PDFViewerApplication, PDFViewerApplicationOptions } from 'pdfjs-dist-viewer-min'; (function() { "use strict"; // TODO(jojo): Should get this from a viewer.js property. var CSS_UNITS = 96.0 / 72.0; var INITAL_COLORS = [ "#ff0000", "#008080", "#00ffff", "#00ff00", "#008000", "#c0c0c0", "#f7347a", "#990000", "#ccff00", "#3399ff", "#f6546a", "#ffff00", "#ffa500", "#0000ff", "#800080", ]; var DEFAULT_STROKE_WIDTH = 5; var PERMISSION_CREATE = 4; var PERMISSION_READ = 1; var PERMISSION_UPDATE = 2; var PERMISSION_DELETE = 8; var PERMISSION_SHARE = 16; var PERMISSION_ALL = 31; // Taken from https://gist.github.com/jed/982883 function uuidv4() { return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) ); } function getRandomColor() { return INITAL_COLORS[Math.floor(Math.random() * INITAL_COLORS.length)]; } // Required for running in older versions of Qt (see #31). // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart if (!String.prototype.padStart) { String.prototype.padStart = function padStart(targetLength, padString) { targetLength = targetLength >> 0; //truncate if number, or convert non-number to 0; padString = String(padString !== undefined ? padString : ' '); if (this.length >= targetLength) { return String(this); } else { targetLength = targetLength - this.length; if (targetLength > padString.length) { padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed } return padString.slice(0, targetLength) + String(this); } }; } var uid = uuidv4(); var object_count = 0; function getObjectId() { return uid + ":" + (++object_count); } var Storage = function(prefix) { this.prefix = prefix; this.storage = window.localStorage; if (!this.storage) { console.warn('LocalStorage not available, saving of settings will be disabled.'); } }; Storage.prototype._getKey = function(key) { if (!this.prefix) { return key; } return this.prefix + '.' + key; }; Storage.prototype.get = function(key) { if (!this.storage) { return null; } return this.storage.getItem(this._getKey(key)); }; Storage.prototype.set = function(key, value) { if (!this.storage) { return; } else if (!value) { this.storage.removeItem(this._getKey(key)); return; } this.storage.setItem(this._getKey(key), value); }; var BaseDrawer = function(annotator) { this.annotator = annotator; }; BaseDrawer.prototype.destroy = function() {}; BaseDrawer.prototype.activate = function(page_annotator) {}; BaseDrawer.prototype.updateSettings = function(page_annotator) {}; BaseDrawer.prototype.onMouseDown = function(page_annotator, event) {}; BaseDrawer.prototype.onMouseUp = function(page_annotator, event) {}; BaseDrawer.prototype.onMouseDrag = function(page_annotator, event) {}; BaseDrawer.prototype.onMouseEnter = function(page_annotator, event) {}; BaseDrawer.prototype.onMouseLeave = function(page_annotator, event) {}; BaseDrawer.prototype.onMouseMove = function(page_annotator, event) {}; BaseDrawer.prototype.onClick = function(page_annotator, event) {}; BaseDrawer.prototype.onKeyUp = function(page_annotator, event) {}; BaseDrawer.prototype.onItemMoved = function(page_annotator, item, event) {}; var NullDrawer = function() { BaseDrawer.apply(this, arguments); }; NullDrawer.prototype = Object.create(BaseDrawer.prototype); var FreehandDrawer = function(annotator) { BaseDrawer.apply(this, arguments); this.path = null; this.intervalId = null; }; FreehandDrawer.prototype = Object.create(BaseDrawer.prototype); FreehandDrawer.prototype.sendPath = function() { if (this.path && this.path.segments.length) { this.annotator.sendItem(this.page_annotator, this.path); } }; FreehandDrawer.prototype.onMouseDown = function(page_annotator, event) { page_annotator.activate(); this.page_annotator = page_annotator; this.intervalId = setInterval(this.sendPath.bind(this), 250); this.path = page_annotator.createPath({ name: getObjectId(), strokeColor: this.annotator.color, strokeWidth: this.annotator.strokeWidth, }); }; FreehandDrawer.prototype.onMouseUp = function(page_annotator, event) { if (!this.path) { return; } if (this.path.segments.length) { this.path.smooth(); this.annotator.sendItem(page_annotator, this.path); } this.path = null; clearInterval(this.intervalId); }; FreehandDrawer.prototype.onMouseDrag = function(page_annotator, event) { if (!this.path) { return; } this.path.add(event.point); }; var LineDrawer = function(annotator) { FreehandDrawer.apply(this, arguments); }; LineDrawer.prototype = Object.create(FreehandDrawer.prototype); LineDrawer.prototype.onMouseDrag = function(page_annotator, event) { if (!this.path) { return; } this.path.removeSegment(1); this.path.add(event.point); }; var RectangleDrawer = function(annotator) { BaseDrawer.apply(this, arguments); this.options = {}; this.rect = null; }; RectangleDrawer.prototype = Object.create(BaseDrawer.prototype); RectangleDrawer.prototype.onMouseDown = function(page_annotator, event) { page_annotator.activate(); this.options = { name: getObjectId(), strokeColor: this.annotator.color, strokeWidth: this.annotator.strokeWidth, from: [event.point.x, event.point.y], to: [event.point.x, event.point.y], }; this.update(page_annotator); }; RectangleDrawer.prototype.update = function(page_annotator) { var create = page_annotator.createRectangle.bind(page_annotator); if (!this.rect) { this.rect = create(this.options); } else { this.rect = this.rect.replaceWith(create(this.options)); } }; RectangleDrawer.prototype.onMouseUp = function(page_annotator, event) { if (!this.rect) { return; } this.options.to = [event.point.x, event.point.y]; this.update(page_annotator); this.rect.ready = true; this.annotator.sendItem(page_annotator, this.rect); this.options = {}; this.rect = null; }; RectangleDrawer.prototype.onMouseDrag = function(page_annotator, event) { if (!this.rect) { return; } this.options.to = [event.point.x, event.point.y]; this.update(page_annotator); }; var EllipseDrawer = function(annotator) { BaseDrawer.apply(this, arguments); this.options = {}; this.ellipse = null; }; EllipseDrawer.prototype = Object.create(BaseDrawer.prototype); EllipseDrawer.prototype.onMouseDown = function(page_annotator, event) { page_annotator.activate(); this.options = { name: getObjectId(), strokeColor: this.annotator.color, strokeWidth: this.annotator.strokeWidth, from: [event.point.x, event.point.y], to: [event.point.x, event.point.y], }; this.update(page_annotator); }; EllipseDrawer.prototype.update = function(page_annotator) { var create = page_annotator.createEllipse.bind(page_annotator); if (!this.ellipse) { this.ellipse = create(this.options); } else { var temp_ellipse = create(this.options); this.ellipse = this.ellipse.replaceWith(temp_ellipse); } }; EllipseDrawer.prototype.onMouseUp = function(page_annotator, event) { if (!this.ellipse) { return; } this.options.to = [event.point.x, event.point.y]; this.update(page_annotator); this.ellipse.ready = true; this.annotator.sendItem(page_annotator, this.ellipse); this.options = {}; this.ellipse = null; }; EllipseDrawer.prototype.onMouseDrag = function(page_annotator, event) { if (!this.ellipse) { return; } this.options.to = [event.point.x, event.point.y]; this.update(page_annotator); }; var PointerDrawer = function(annotator) { BaseDrawer.apply(this, arguments); }; PointerDrawer.prototype = Object.create(BaseDrawer.prototype); PointerDrawer.prototype.destroy = function() { this.annotator.destroyCursors(); }; PointerDrawer.prototype.onMouseMove = function(page_annotator, event) { this.annotator.renderCursor(page_annotator, event.point.x, event.point.y); }; PointerDrawer.prototype.onMouseLeave = function(page_annotator, event) { this.annotator.hideCursor(); }; var SelectDrawer = function(annotator) { BaseDrawer.apply(this, arguments); this.selected_item = null; this.prev_settings = null; this.pending_items = {}; this.send_interval = setInterval(this._sendPending.bind(this), 100); }; SelectDrawer.prototype = Object.create(BaseDrawer.prototype); SelectDrawer.prototype._select = function(item) { if (this.selected_item === item) { return; } this._unselect(); this.selected_item = item; if (this.selected_item.onFocus) { var result = this.selected_item.onFocus(); if (result) { this.selected_item = result; } } else { this.selected_item.shadowColor = "black"; this.selected_item.shadowBlur = 10; } var color = this.selected_item.strokeColor; if (typeof(color) !== 'string') { color = color.toCSS(true); if (this.selected_item.strokeColor.hasAlpha() && color.length <= 7) { color += Math.round(this.selected_item.strokeColor.alpha * 255).toString(16); } } var settings = { 'color': color, 'strokeWidth': this.selected_item.strokeWidth, }; this.prev_settings = this.annotator.updateSettings(settings); }; SelectDrawer.prototype._unselect = function() { var reset; if (this.prev_settings) { if (this.selected_item) { reset = { 'strokeColor': this.selected_item.strokeColor, 'strokeWidth': this.selected_item.strokeWidth, }; } this.annotator.updateSettings(this.prev_settings); this.prev_settings = null; } if (!this.selected_item) { return; } if (this.selected_item.onBlur) { this.selected_item.onBlur(); } else { this.selected_item.shadowColor = null; this.selected_item.shadowBlur = 0; } if (reset) { this.selected_item.strokeColor = reset.strokeColor; this.selected_item.strokeWidth = reset.strokeWidth; } this.selected_item = null; }; SelectDrawer.prototype._sendItem = function(page_annotator, item) { // Need to remove "selected" layout while generating item JSON. var is_selected = (item === this.selected_item); if (is_selected) { this._unselect(); } this.annotator.sendItem(page_annotator, item); if (is_selected) { this._select(item); } }; SelectDrawer.prototype._sendPending = function() { var now = Date.now(); for (var name in this.pending_items) { if (!this.pending_items.hasOwnProperty(name)) { continue; } var info = this.pending_items[name]; var page_annotator = info[0]; var item = info[1]; var last_change = info[2]; if (now - last_change < 250) { continue; } this._sendItem(page_annotator, item); delete this.pending_items[name]; } }; SelectDrawer.prototype.destroy = function() { clearInterval(this.send_interval); this._sendPending(); this._unselect(); }; SelectDrawer.prototype._hitItem = function(page_annotator, point) { var hit = page_annotator.scope.project.hitTest(point); if (!hit) { // Check if user clicked on a text annotation. each(page_annotator.textAnnotations, function(ta) { var elem = ta.textareaContainer; if (!elem.is(':visible')) { return; } var rect = elem[0].getBoundingClientRect(); if (rect.left <= point.x && rect.right >= point.x && rect.top <= point.y && rect.bottom >= point.y) { hit = { "item": ta, }; } }); if (!hit) { this._unselect(); return; } } this._select(hit.item); }; SelectDrawer.prototype.onMouseDown = function(page_annotator, event) { this._hitItem(page_annotator, event.point); }; SelectDrawer.prototype.onClick = function(page_annotator, event) { this._hitItem(page_annotator, event.point); }; SelectDrawer.prototype.onKeyUp = function(page_annotator, event) { if (!this.selected_item) { return; } if (this.selected_item.ignoreKeyUp && this.selected_item.ignoreKeyUp()) { return; } switch (event.keyCode) { case 8: // Backspace // Fallthrough case 46: // Delete key var item = this.selected_item; this.annotator.deleteItem(page_annotator, item); this._unselect(); item.remove(); break; } }; SelectDrawer.prototype.onItemMoved = function(page_annotator, item, event) { event.stopPropagation(); item.position.x += event.delta.x; item.position.y += event.delta.y; this.addPendingItem(page_annotator, item); }; SelectDrawer.prototype.addPendingItem = function(page_annotator, item) { if (this.pending_items.hasOwnProperty(item.name)) { this.pending_items[item.name][0] = page_annotator; this.pending_items[item.name][1] = item; } else { this.pending_items[item.name] = [page_annotator, item, Date.now()]; } }; SelectDrawer.prototype.updateSettings = function(page_annotator) { if (!this.selected_item) { return; } if (this.selected_item.strokeColor === this.annotator.color && this.selected_item.strokeWidth === this.annotator.strokeWidth) { return; } this.selected_item.strokeColor = this.annotator.color; this.selected_item.strokeWidth = this.annotator.strokeWidth; this.addPendingItem(page_annotator, this.selected_item); }; var TextDrawer = function(annotator) { BaseDrawer.apply(this, arguments); }; TextDrawer.prototype = Object.create(BaseDrawer.prototype); TextDrawer.prototype.onClick = function(page_annotator, event) { var author = this.annotator.displayname; var textarea = page_annotator.createTextArea({ 'x': event.point.x, 'y': event.point.y, 'author': author, }); textarea.sendData(); textarea.onFocus(); textarea.textarea.trigger('focus'); this.annotator.setDrawMode("select"); }; var Cursor = function(annotator, userid, radius) { this.annotator = annotator; this.page_annotator = null; this.name = getObjectId(); this.userid = userid; this.radius = radius; this.circle = null; this.label = $("
"); this.label.css('position', 'absolute'); }; Cursor.prototype.destroy = function() { if (this.page_annotator) { this.page_annotator.unregisterPendingActivate(this); } if (this.circle) { this.circle.remove(); this.circle = null; } this.label.remove(); }; Cursor.prototype.draw = function(page_annotator, x, y, color, text) { this.x = x; this.y = y; var center = new paper.Point(x, y); if (this.page_annotator && page_annotator !== this.page_annotator) { this.page_annotator.unregisterPendingActivate(this); if (this.page_annotator.container) { this.page_annotator.container.remove(this.label); } this.page_annotator = null; this.destroy(); } if (!this.circle) { page_annotator.activate(); this.circle = new paper.Path.Circle(center, this.radius); this.circle.name = getObjectId(); } if (!this.page_annotator) { this.page_annotator = page_annotator; if (this.page_annotator.container) { this.page_annotator.container.append(this.label); } else { this.page_annotator.registerPendingActivate(this); } } this.circle.set({ fillColor: color, shadowColor: color, shadowBlur: 10, position: center }); this.label.html(text); this.update(); }; Cursor.prototype.activate = function(page_annotator) { if (page_annotator !== this.page_annotator) { return; } this.label.remove(); if (page_annotator.container) { page_annotator.container.append(this.label); } }; Cursor.prototype.update = function() { var radius = this.circle.bounds.width / 2; var newRadius = this.radius / this.page_annotator.scale; if (radius !== newRadius) { this.circle.scale(newRadius / radius); } var x = this.x * this.page_annotator.scale; var y = this.y * this.page_annotator.scale; this.label.css('left', x + (this.radius * 2)); this.label.css('top', y - (this.radius)); }; var TextArea = function(page_annotator, text, color, author) { this.page_annotator = page_annotator; this.content = text; this.color = color; this.author = author; this.modified = Math.round((new Date()).getTime() / 1000); this.name = getObjectId(); this.textareaContainer = $('
'); this.textareaContainer.draggable({ start: function(event, ui) { if ($('textarea', this.textareaContainer).prop("disabled")) { event.preventDefault(); } }.bind(this), drag: function(event, ui) { this.textareaContainerPos = [ ui.position.left / this.page_annotator.scale, ui.position.top / this.page_annotator.scale ]; this.update(); this.sendData(); }.bind(this), stop: function(event, ui) { this.textareaContainerPos = [ ui.position.left / this.page_annotator.scale, ui.position.top / this.page_annotator.scale ]; this.update(); this.sendData(); }.bind(this), }); this.textareaContainer.on('click', function(event) { this.page_annotator.annotator.drawer.onClick(this.page_annotator, { 'point': { 'x': event.originalEvent.clientX, 'y': event.originalEvent.clientY, }, }); }.bind(this)); this.textareaContainer.on('mouseup', function(event) { this.page_annotator.annotator.drawer.onMouseUp(this.page_annotator, { 'point': { 'x': event.originalEvent.clientX, 'y': event.originalEvent.clientY, }, }); }.bind(this)); this.page_annotator.container.append(this.textareaContainer); this.circle = null; this.line = null; this.textarea = $(''); this.authorLabel = $(''); this.closeButton = $(''); this.closeButton.on('click', function() { this.textareaContainer.hide(); this.update(); }.bind(this)); this.textarea.on('input', function(e) { if (this.content === this.textarea.val()) { return; } this.content = this.textarea.val(); this.author = this.page_annotator.annotator.displayname; this.modified = Math.round((new Date()).getTime() / 1000); this.update(); this.sendData(); }.bind(this)); this.textarea.on('focus', function(e) { this.onFocus(); }.bind(this)); this.textarea.on('dragstart', function(e) { e.preventDefault(); }.bind(this)); this.textareaContainer.append(this.authorLabel, this.closeButton, this.textarea); this.textareaContainer.css("background-color", this.color); this.textarea.css("font-size", this.page_annotator.annotator.fontSize+"px"); this._sendData = throttle(function() { this.page_annotator.annotator.sendItem(this.page_annotator, this); }.bind(this), 250); }; TextArea.prototype.exportJSON = function() { var data = { "type": "text-annotation", "anchor": [ this.circle.position.x, this.circle.position.y, ], "author": this.author, "color": this.color, "content": this.content, "modified": this.modified, "pos": [ this.textareaContainerPos[0] / this.page_annotator.scale, this.textareaContainerPos[1] / this.page_annotator.scale, ], }; return JSON.stringify(data); }; Object.defineProperty(TextArea.prototype, 'strokeColor', { "get": function() { return this.color; }, "set": function(value) { if (this.color === value) { return; } this.color = value; this.textareaContainer.css("background-color", this.color); if (!$('textarea', this.textareaContainer).prop("disabled")) { this.textareaContainer.css("box-shadow", "0px 0px 15px 5px"+this.color); } this.authorLabel.css('background-color', this.color); if (this.circle) { this.circle.fillColor = value; } if (this.line) { this.line.strokeColor = value; } }, }); Object.defineProperty(TextArea.prototype, 'strokeWidth', { "get": function() { return this.page_annotator.annotator.strokeWidth; }, "set": function(value) { // Not supported by text annotations. }, }); TextArea.prototype.sendData = function() { this._sendData(); }; TextArea.prototype.draw = function(x, y) { this.x = x; this.y = y; if (!this.circle) { this.drawCircle(); } if (!this.textareaContainerPos) { this.textareaContainerPos = [x, y]; } this.update(); }; TextArea.prototype.drawCircle = function() { var center = new paper.Point(this.x, this.y); var circle = new paper.Path.Circle(center, 10); circle.name = this.name+"pointer"; circle.set({ fillColor: this.color, shadowColor: this.color, shadowBlur: 2, position: center }); circle.onClick = function(e) { this.textareaContainer.show(); this.textarea.trigger('blur'); this.update(); }.bind(this); circle.onFocus = function() { this.onFocus(); return this; }.bind(this); circle.onBlur = function() { this.onBlur(); return this; }.bind(this); circle.onMouseDrag = function(event) { event.stopPropagation(); this.textarea.trigger('blur'); this.circle.position.x += event.delta.x; this.circle.position.y += event.delta.y; this.update(); this.sendData(); }.bind(this); circle.onMouseUp = function(event) { this.update(); this.sendData(); }.bind(this); this.circle = circle; }; TextArea.prototype.update = function(page_annotator) { this.authorLabel.text(this.author || 'Anonymous'); this.authorLabel.attr('title', this.author || 'Anonymous'); this.authorLabel.css('background-color', this.color); this.textareaContainer.css('left', this.textareaContainerPos[0]*this.page_annotator.scale); this.textareaContainer.css('top', this.textareaContainerPos[1]*this.page_annotator.scale); this.textareaContainer.css("background-color", this.color); this.textarea.css("font-size", this.page_annotator.annotator.fontSize+"px"); this.textarea.val(this.content); if (this.line) { this.line.remove(); this.line = null; } if (this.textareaContainer.is(':visible')) { var from = new paper.Point( this.circle.position.x, this.circle.position.y); var to = new paper.Point( this.textareaContainerPos[0], this.textareaContainerPos[1]); this.line = new paper.Path.Line(from, to); this.line.strokeColor = this.color; this.line.strokeWidth = 2; if (!$('textarea', this.textareaContainer).prop("disabled")) { this.line.shadowColor = "black"; this.line.shadowBlur = 5; } this.line.onFocus = function() { this.onFocus(); return this; }.bind(this); this.line.onBlur = function() { this.onBlur(); return this; }.bind(this); } }; TextArea.prototype.remove = function() { this.circle.remove(); if (this.line) { this.line.remove(); } this.textareaContainer.remove(); delete this.page_annotator.textAnnotations[this.name]; }; TextArea.prototype.ignoreKeyUp = function() { if (this.textarea.is(':focus')) { // The textarea currently is focused, ignore keypresses in drawer, // otherwise the element gets deleted when the user presses "delete". return true; } return false; }; TextArea.prototype.onFocus = function() { if (this._selecting) { return; } this._selecting = true; $('textarea', this.textareaContainer).prop("disabled", false); this.closeButton.prop("disabled", false); this.textareaContainer.css("box-shadow", "0px 0px 15px 5px"+this.color); this.circle.shadowColor = "black"; this.circle.shadowBlur = 10; if (this.line) { this.line.shadowColor = "black"; this.line.shadowBlur = 5; } this._selecting = false; }; TextArea.prototype.onBlur = function() { if (this._unselecting) { return; } this._unselecting = true; $('textarea', this.textareaContainer).prop("disabled", true); this.closeButton.prop("disabled", true); this.textareaContainer.draggable("disable"); this.textareaContainer.css("box-shadow", "none"); this.circle.shadowColor = null; this.circle.shadowBlur = 0; if (this.line) { this.line.shadowColor = null; this.line.shadowBlur = 0; } this.textarea.trigger('blur'); this._unselecting = false; }; TextArea.prototype.onResized = function(scale) { this.page_annotator.container.append(this.textareaContainer); this.draw(this.x, this.y); }; var PageAnnotator = function(annotator, pagenum, container, page) { this.annotator = annotator; this.pagenum = pagenum; this.container = container; this.page = page; this.pending_activation = {}; this.textAnnotations = {}; this.canvas = document.createElement("canvas"); this.canvas.style.position = "absolute"; this.canvas.style.left = 0; this.canvas.style.top = 0; this.canvas.style.right = 0; this.canvas.style.bottom = 0; this.scope = new paper.PaperScope(); this.scope.setup(this.canvas); this.scope.project.options.hitTolerance = 5; if (page) { this.setPage(page, container); } this.view = this.scope.getView(); this.view.on('mousedown', function() { var args = Array.prototype.slice.call(arguments); args.unshift(this); this.activate(); this.annotator.e.trigger('mousedown', [args]); }.bind(this)); this.view.on('mouseup', function() { var args = Array.prototype.slice.call(arguments); args.unshift(this); this.activate(); this.annotator.e.trigger('mouseup', [args]); }.bind(this)); this.view.on('mousedrag', function() { var args = Array.prototype.slice.call(arguments); args.unshift(this); this.activate(); this.annotator.e.trigger('mousedrag', [args]); }.bind(this)); this.view.on('mouseenter', function() { var args = Array.prototype.slice.call(arguments); args.unshift(this); this.activate(); this.annotator.e.trigger('mouseenter', [args]); }.bind(this)); this.view.on('mouseleave', function() { var args = Array.prototype.slice.call(arguments); args.unshift(this); this.activate(); this.annotator.e.trigger('mouseleave', [args]); }.bind(this)); // Sometimes the "mouseleave" event of paper.js doesn't fire when // switching views, also handle "mouseleave" on the canvas itself // as a workaround. $(this.canvas).on('mouseleave', function() { var args = Array.prototype.slice.call(arguments); args.unshift(this); this.activate(); this.annotator.e.trigger('mouseleave', [args]); }.bind(this)); this.view.on('mousemove', function() { var args = Array.prototype.slice.call(arguments); args.unshift(this); this.activate(); this.annotator.e.trigger('mousemove', [args]); }.bind(this)); this.view.on('click', function() { var args = Array.prototype.slice.call(arguments); args.unshift(this); this.activate(); this.annotator.e.trigger('click', [args]); }.bind(this)); $(window).on('keyup', function(event) { if (PageAnnotator.prototype.__active !== this) { return; } var args = Array.prototype.slice.call(arguments); args.unshift(this); this.activate(); this.annotator.e.trigger('keyup', [args]); }.bind(this)); }; PageAnnotator.prototype.exportSVG = function() { // Hide circle and line for text annotations so they don't show up in exported PDFs. each(this.textAnnotations, function(ta) { ta.circle.remove(); if (ta.line) { ta.line.remove(); } }.bind(this)); var svg = this.scope.project.exportSVG({ asString: true }); each(this.textAnnotations, function(ta) { this.scope.project.activeLayer.addChild(ta.circle); if (ta.line) { this.scope.project.activeLayer.addChild(ta.line); } }.bind(this)); return svg; }; PageAnnotator.prototype.activate = function() { PageAnnotator.prototype.__active = this; for (var drawerName in this.pending_activation) { if (!this.pending_activation.hasOwnProperty(drawerName)) { continue; } this.pending_activation[drawerName].activate(this); } this.pending_activation = {}; this.scope.activate(); }; PageAnnotator.prototype.registerPendingActivate = function(drawer) { this.pending_activation[drawer.name] = drawer; }; PageAnnotator.prototype.unregisterPendingActivate = function(drawer) { delete this.pending_activation[drawer.name]; }; PageAnnotator.prototype.setPage = function(page, container) { this.container = container; this.page = page; var pagesize = page.view; this.pagewidth = (pagesize[2] - pagesize[0]) * CSS_UNITS; this.pageheight = (pagesize[3] - pagesize[1]) * CSS_UNITS; if (this.container) { this.container.append(this.canvas); } }; PageAnnotator.prototype.update = function(scale) { if (this.container && !this.container.has(this.canvas).length) { this.container.append(this.canvas); } this.scale = scale; var width = this.pagewidth * scale; var height = this.pageheight * scale; this.view.viewSize = new paper.Size(width, height); this.view.center = new paper.Point( (width + (this.pagewidth * (1 - scale))) / 2, (height + (this.pageheight * (1 - scale))) / 2); this.view.zoom = scale; this.activate(); each(this.textAnnotations, function(ta) { ta.onResized(); }); this.view._needsUpdate = true; this.view.requestUpdate(); }; PageAnnotator.prototype.createPath = function(options) { var path = new paper.Path(options || {}); path.onMouseDrag = function(event) { this.annotator.drawer.onItemMoved(this, path, event); }.bind(this); return path; }; PageAnnotator.prototype.createRectangle = function(options) { var rect = new paper.Path.Rectangle(options || {}); rect.onMouseDrag = function(event) { if (rect.ready) { this.annotator.drawer.onItemMoved(this, rect, event); } }.bind(this); return rect; }; PageAnnotator.prototype.createEllipse = function(options) { var from = new paper.Point(options.from); var to = new paper.Point(options.to); var rectangle = new paper.Rectangle( from, to ); var ellipse = new paper.Path.Ellipse(rectangle); ellipse.strokeColor = options.strokeColor; ellipse.strokeWidth = options.strokeWidth; ellipse.name = options.name; ellipse.onMouseDrag = function(event) { if (ellipse.ready) { this.annotator.drawer.onItemMoved(this, ellipse, event); } }.bind(this); return ellipse; }; PageAnnotator.prototype.createTextArea = function(options) { var text = options.text || ""; var color = options.color || this.annotator.color; var author = options.author || ""; var textarea = new TextArea(this, text, color, author); if (options.name) { textarea.name = options.name; } if (options.modified) { textarea.modified = options.modified; } this.textAnnotations[textarea.name] = textarea; textarea.draw(options.x, options.y); if (options.anchor && textarea.circle) { textarea.circle.position = { x: options.anchor[0], y: options.anchor[1], }; } return textarea; }; PageAnnotator.prototype.drawItem = function(name, data) { this.activate(); var path = this.scope.project.getItem({"name": name}); if (!path) { path = this.createPath(); } try { path.importJSON(data); } catch (e) { path.remove(); path.name = name; this.annotator.deleteItem(this, path); console.log("Could not import item", data, e); return; } path.name = name; }; PageAnnotator.prototype.drawTextAnnotation = function(userid, name, data) { this.activate(); var textarea; if (this.textAnnotations.hasOwnProperty(name)) { textarea = this.textAnnotations[name]; textarea.author = data.author; textarea.modified = data.modified; textarea.color = data.color; textarea.content = data.content; textarea.circle.position = { x: Math.max(0, data.anchor[0]), y: Math.max(0, data.anchor[1]), }; textarea.draw(data.pos[0], data.pos[1]); } else { textarea = this.createTextArea({ 'x': data.pos[0], 'y': data.pos[1], 'text': data.content, 'color': data.color, 'name': name, 'anchor': data.anchor, 'author': data.author, 'modified': data.modified, }); } textarea.update(); return textarea; }; PageAnnotator.prototype.deleteItem = function(name) { this.activate(); var path = this.scope.project.getItem({"name": name}); if (!path) { return; } path.remove(); }; function parseStrokeWidth(value) { if (typeof(value) !== "number") { value = parseInt(value, 10); } if (value <= 0 || isNaN(value)) { return DEFAULT_STROKE_WIDTH; } return value; } function Annotator(socketurl, id, userid, displayname, token) { this.e = $({}); this.annotators = {}; this.cursors = {}; this.draw_mode = null; this.drawer = this.nulldrawer = new NullDrawer(); this.id = id; this.userid = userid; this.displayname = displayname; this.token = token; this.storage = new Storage('pdfdraw'); var color = this.storage.get('color'); if (!color) { color = getRandomColor(); this.storage.set('color', color); } this.color = color; var strokeWidth = this.storage.get('strokeWidth'); if (!strokeWidth) { strokeWidth = DEFAULT_STROKE_WIDTH; this.storage.set('strokeWidth', strokeWidth); } else { strokeWidth = parseStrokeWidth(strokeWidth); } this.strokeWidth = strokeWidth; this.fontSize = 12; this.pageCount = -1; this.currentPage = null; this.users = {}; this.has_document = false; this.setDrawMode('pointer'); this.pending_messages = []; this.socketurl = socketurl; this.socket = io(socketurl, { 'transports': ['websocket'], 'query': { 'token': token } }); // Also allow polling connections when the connection was interrupted once // (could be caused by proxy / firewall). this.socket.io.on('reconnect_attempt', function() { this.socket.io.opts.transports = ['polling', 'websocket']; }.bind(this)); this.socket.io.on('reconnect', this.onReconnected.bind(this)); this.socket.on('message', this.onMessage.bind(this)); this.socket.on('user.joined', this.onUserJoined.bind(this)); this.socket.on('user.left', this.onUserLeft.bind(this)); this.socket.on('connect', this.onConnected.bind(this)); this.socket.on('disconnect', this.onDisconnect.bind(this)); this.colorPicker = new iro.ColorPicker("#colorPicker", { width: 320, height: 320, color: this.color, transparency: true, }); var setColor = function(color) { this.color = color.hex8String; this.storage.set('color', this.color); $(".modeButton.colorMode, .modeButton.colorMode:focus") .css("background-color", this.color); if (!this._updating_settings) { this.drawer.updateSettings(PageAnnotator.prototype.__active); } }.bind(this); this.colorPicker.on("color:init", setColor); this.colorPicker.on("color:change", setColor); $('#inputStrokeWidth').val(this.strokeWidth); $('#strokeWidthValue').text(this.strokeWidth); $('#inputStrokeWidth').on('input', function(e) { var strokeWidth = parseStrokeWidth(e.target.value); this.storage.set('strokeWidth', strokeWidth); this.strokeWidth = strokeWidth; if (!this._updating_settings) { this.drawer.updateSettings(PageAnnotator.prototype.__active); } }.bind(this)); $('#inputStrokeWidth').on('change', function(e) { var strokeWidth = parseStrokeWidth(e.target.value); $('#strokeWidthValue').text(strokeWidth); }.bind(this)); this.userlist = $("
"); $("#mainContainer").append(this.userlist); this.connectionError = $("#connectionError"); this.connectingMessage = $("#connectingMessage"); this.e.on('mousedown', function(event, args) { if (this.settings_open) { event.preventDefault(); return; } this.drawer.onMouseDown.apply(this.drawer, args); }.bind(this)); this.e.on('mouseup', function(event, args) { if (this.settings_open) { event.preventDefault(); return; } this.drawer.onMouseUp.apply(this.drawer, args); }.bind(this)); this.e.on('mousedrag', function(event, args) { if (this.settings_open) { event.preventDefault(); return; } this.drawer.onMouseDrag.apply(this.drawer, args); }.bind(this)); this.e.on('mouseenter', function(event, args) { if (this.settings_open) { event.preventDefault(); return; } this.drawer.onMouseEnter.apply(this.drawer, args); }.bind(this)); this.e.on('mouseleave', function(event, args) { if (this.settings_open) { event.preventDefault(); return; } this.drawer.onMouseLeave.apply(this.drawer, args); }.bind(this)); this.e.on('mousemove', function(event, args) { if (this.settings_open) { event.preventDefault(); return; } this.drawer.onMouseMove.apply(this.drawer, args); }.bind(this)); this.e.on('keyup', function(event, args) { if (this.settings_open) { event.preventDefault(); return; } this.drawer.onKeyUp.apply(this.drawer, args); }.bind(this)); this.e.on('click', function(event, args) { if (this.settings_open) { this.drawer.updateSettings(args[0]); this.toggleSettings(); return; } this.drawer.onClick.apply(this.drawer, args); }.bind(this)); } Annotator.prototype.updateSettings = function(settings) { this._updating_settings = true; var prev = { 'color': this.colorPicker.color.hex8String, 'strokeWidth': this.strokeWidth, }; this.colorPicker.color.hex8String = settings.color; $('#inputStrokeWidth').val(settings.strokeWidth); $('#strokeWidthValue').text($('#inputStrokeWidth').val()); this._updating_settings = false; return prev; }; Annotator.prototype.toggleSettings = function() { $("#settingsDialog").toggle(); this.settings_open = !this.settings_open; }; Annotator.prototype.onDisconnect = function() { this.users = {}; this.updateUsersList(); this.connectionError.show(); }; Annotator.prototype.onConnected = function() { if (!this.connectingMessage) { return; } this.connectingMessage.hide(); this.connectingMessage = null; }; Annotator.prototype.onReconnected = function() { this.connectionError.hide(); }; function compareEntries(a, b) { // Compare by display name. a = a[1].toLocaleLowerCase(); b = b[1].toLocaleLowerCase(); return a.localeCompare(b); } Annotator.prototype.updateUsersList = function() { var entries = []; for (var userid in this.users) { if (!this.users.hasOwnProperty(userid)) { continue; } entries.push([userid, this.users[userid].displayname || "Anonymous"]); } this.userlist.empty(); if (!entries.length) { this.userlist.hide(); return; } entries.sort(compareEntries); for (var i = 0; i < entries.length; ++i) { userid = entries[i][0]; var displayname = entries[i][1]; var elem = $("
").text(displayname); if (userid === this.socket.id) { elem.addClass("own"); } this.userlist.append(elem); } this.userlist.show(); }; Annotator.prototype.onUserJoined = function(message) { for (var i = 0; i < message.length; ++i) { var userid = message[i].userid; if (!userid) { continue; } this.users[userid] = message[i]; } this.updateUsersList(); }; Annotator.prototype.onUserLeft = function(message) { var userid = message.userid; if (!userid || !this.users.hasOwnProperty(userid)) { return; } delete this.users[userid]; this.updateUsersList(); }; Annotator.prototype.documentLoaded = function(pdfDocument) { this.pageCount = pdfDocument.numPages; this.has_document= true; while (this.pending_messages.length) { var message = this.pending_messages.shift(); this.onMessage(message); } if (this.connectingMessage) { this.connectingMessage.show(); } }; Annotator.prototype.sendMessage = function(message) { // console.log("Send", message); this.socket.emit('message', message); }; Annotator.prototype.sendItem = function(page_annotator, item) { var data = item.exportJSON(); if (!data) { console.log("Can't export to JSON", item); return; } this.sendMessage({ 'type': 'item', 'item': { 'page': page_annotator.pagenum, 'name': item.name, 'data': data } }); }; Annotator.prototype.deleteItem = function(page_annotator, item) { this.sendMessage({ 'type': 'delete', 'delete': { 'page': page_annotator.pagenum, 'name': item.name } }); }; Annotator.prototype.onMessage = function(message) { // console.log("Received", message); if (!this.has_document) { this.pending_messages.push(message); return; } this.processMessage(message); }; Annotator.prototype.renderCursor = function(page_annotator, x, y) { var cursor = this.getCursor(this.userid, 5); cursor.draw(page_annotator, x, y, this.color, this.displayname); var data = { 'type': 'cursor', 'cursor': { 'action': 'show', 'page': page_annotator.pagenum, 'x': x, 'y': y, 'color': this.color } }; this.sendMessage(data); }; Annotator.prototype.getCursor = function(userid, size) { var cursor; if (!this.cursors.hasOwnProperty(userid)) { cursor = this.cursors[userid] = new Cursor(this, userid, size); } else { cursor = this.cursors[userid]; } return cursor; }; Annotator.prototype.showCursor = function(userid, data) { if (!this.users.hasOwnProperty(userid)) { return; } var displayname = this.users[userid].displayname || "Anonymous"; var cursor = this.getCursor(userid, 5); this.getPage(data.page).then(function(page_annotator) { cursor.draw(page_annotator, data.x, data.y, data.color, displayname); }); }; Annotator.prototype.hideCursor = function(userid) { if (!userid) { var data = { 'type': 'cursor', 'cursor': { 'action': 'hide' } }; this.sendMessage(data); userid = this.userid; } if (!this.cursors.hasOwnProperty(userid)) { return; } var cursor = this.cursors[userid]; delete this.cursors[userid]; cursor.destroy(); }; Annotator.prototype.destroyCursors = function() { for (var i in this.cursors) { if (!this.cursors.hasOwnProperty(i)) { continue; } this.cursors[i].destroy(); } this.cursors = {}; }; Annotator.prototype.getExistingPage = function(pagenum) { if (typeof(pagenum) === "string") { pagenum = parseInt(pagenum, 10); } if (!this.annotators.hasOwnProperty(pagenum)) { return null; } return this.annotators[pagenum]; }; Annotator.prototype.getPage = function(pagenum, page, container) { if (typeof(pagenum) === "string") { pagenum = parseInt(pagenum, 10); } return new Promise(function(resolve, reject) { var page_annotator; if (!this.annotators.hasOwnProperty(pagenum)) { page_annotator = this.annotators[pagenum] = new PageAnnotator(this, pagenum, container, page); } else { page_annotator = this.annotators[pagenum]; } if (page_annotator.page && page_annotator.container) { return resolve(page_annotator); } else if (page && container) { page_annotator.setPage(page, container); return resolve(page_annotator); } if (page_annotator.page || !PDFViewerApplication.pdfViewer.pdfDocument) { return resolve(page_annotator); } PDFViewerApplication.pdfViewer.pdfDocument.getPage(pagenum).then(function(page) { page_annotator.setPage(page, page_annotator.container); page_annotator.update(PDFViewerApplication.pdfViewer.currentScale); return resolve(page_annotator); }, function(error) { return reject(error); }); }.bind(this)); }; Annotator.prototype.destroyDrawer = function() { if (this.drawer && this.drawer !== this.nulldrawer) { var drawer = this.drawer; this.drawer = this.nulldrawer; drawer.destroy(); } }; Annotator.prototype.setDrawMode = function(mode) { $(".toolbarButton.selected").removeClass('selected'); this.draw_mode = mode; $("#" + (mode || "none") + "Mode").addClass('selected'); switch (mode) { case "select": case "color": case "pointer": case null: break; default: $("#drawModeToolbar").addClass('selected'); break; } this.destroyDrawer(); switch (this.draw_mode) { case "freehand": this.drawer = new FreehandDrawer(this); break; case "rectangle": this.drawer = new RectangleDrawer(this); break; case "ellipse": this.drawer = new EllipseDrawer(this); break; case "pointer": this.drawer = new PointerDrawer(this); break; case "select": this.drawer = new SelectDrawer(this); break; case "line": this.drawer = new LineDrawer(this); break; case "text": this.drawer = new TextDrawer(this); break; case null: break; default: console.log("Unknown draw mode", this.draw_mode); return; } }; Annotator.prototype.switchPage = function(pagenum) { if (pagenum === this.currentPage) { return; } this.currentPage = pagenum; this.sendMessage({ 'type': 'control', 'control': { 'type': 'page', 'page': pagenum } }); }; Annotator.prototype.processMessage = function(message) { switch (message.type) { case 'cursor': this.processCursorMessage(message.userid, message.cursor); break; case 'item': this.processItemMessage(message.userid, message.item); break; case 'delete': this.processDeleteMessage(message.userid, message.delete); break; case 'control': this.processControlMessage(message.userid, message.control); break; default: console.log('Unknown message', message); break; } }; Annotator.prototype.processCursorMessage = function(userid, message) { switch (message.action) { case 'hide': this.hideCursor(userid); break; case 'show': default: this.showCursor(userid, message); break; } }; Annotator.prototype.processItemMessage = function(userid, message) { if (typeof message.data === "string") { message.data = JSON.parse(message.data); } this.getPage(message.page).then(function(page_annotator) { switch (message.data.type) { case "text-annotation": page_annotator.drawTextAnnotation(userid, message.name, message.data); return; } page_annotator.drawItem(message.name, message.data); }); }; Annotator.prototype.processDeleteMessage = function(userid, message) { this.getPage(message.page).then(function(page_annotator) { page_annotator.deleteItem(message.name); }); }; Annotator.prototype.processControlMessage = function(userid, message) { switch (message.type) { case 'page': var page = message.page; if (page >= 1 && page <= PDFViewerApplication.pagesCount && page !== PDFViewerApplication.page) { this.currentPage = page; PDFViewerApplication.page = page; } break; default: console.log('Unsupported control message', message); return; } }; Annotator.prototype.exportSVG = function() { return new Promise(function(resolve, reject) { var result = []; var pages = []; var continueExport = function() { // Remove trailing empty pages. while (result.length && !result[result.length-1]) { result.pop(); } // No annotations yet? if (!result.length) { return resolve(result); } // Need to keep a last empty page to avoid duplicating the last overlay. if (result.length < this.pageCount) { var page = pages[result.length + 1]; if (page) { var svg = page.exportSVG(); result.push(svg); } } return resolve(result); }.bind(this); var remaining = this.pageCount; for (var i = 1; i <= this.pageCount; i++) { this.getPage(i).then(function(pagenum, page_annotator) { pages[pagenum] = page_annotator; var svg = null; if (page_annotator) { svg = page_annotator.exportSVG(); } result[pagenum - 1] = svg; remaining -= 1; if (remaining === 0) { continueExport(); } }.bind(this, i)); } }.bind(this)); }; Annotator.prototype.exportTextAnnotations = function() { var list = []; each(this.annotators, function(page_annotator) { each(page_annotator.textAnnotations, function(annotation) { var scaleX = page_annotator.pagewidth / (annotation.circle.project.view.bounds.width * CSS_UNITS); var scaleY = page_annotator.pageheight / (annotation.circle.project.view.bounds.height * CSS_UNITS); var color = annotation.color; if (color.length === 9 && color[0] === '#') { // PDF annotations don't support transparency. color = color.substr(0, 7); } var taObj = { "page": page_annotator.pagenum - 1, "x": annotation.circle.position.x * scaleX, "y": annotation.circle.position.y * scaleY, "author": annotation.author, "modified": annotation.modified, "text": annotation.content, "color": color, }; list.push(taObj); }); }); return list; }; Annotator.prototype.downloadPdf = function() { this.exportSVG().then(function(svg) { if (!svg.length) { // No items drawn yet, download source PDF. console.log("Download source PDF"); return; } var data = { "svg": svg, "token": this.token, "text": this.exportTextAnnotations(), }; // Download code from https://stackoverflow.com/a/23797348 var xhr = new XMLHttpRequest(); var base_url = this.socketurl; if (base_url[base_url.length-1] !== '/') { base_url += '/'; } xhr.open('POST', base_url + "download/" + this.id, true); xhr.responseType = 'arraybuffer'; xhr.onload = function() { $("#downloadInProgress").hide(); if (this.status !== 200) { $("#downloadFailed").show(); setTimeout(function() { $("#downloadFailed").hide(); }, 4000); return; } var filename = ""; var disposition = xhr.getResponseHeader('Content-Disposition'); if (disposition && disposition.indexOf('attachment') !== -1) { var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; var matches = filenameRegex.exec(disposition); if (matches != null && matches[1]) { filename = matches[1].replace(/['"]/g, ''); } } var type = xhr.getResponseHeader('Content-Type'); var blob = typeof File === 'function' ? new File([this.response], filename, { type: type }) : new Blob([this.response], { type: type }); if (typeof window.navigator.msSaveBlob !== 'undefined') { // IE workaround for "HTML7007: One or more blob URLs were revoked by // closing the blob for which they were created. These URLs will no // longer resolve as the data backing the URL has been freed." window.navigator.msSaveBlob(blob, filename); } else { var URL = window.URL || window.webkitURL; var downloadUrl = URL.createObjectURL(blob); if (filename) { // use HTML5 a[download] attribute to specify filename var a = document.createElement("a"); // safari doesn't support this yet if (typeof a.download === 'undefined') { window.location = downloadUrl; } else { a.href = downloadUrl; a.download = filename; document.body.appendChild(a); a.click(); } } else { window.location = downloadUrl; } setTimeout(function() { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup } }; xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(JSON.stringify(data)); $("#downloadInProgress").show(); }.bind(this)); }; $(document).ready(function() { var fileid = document.getElementsByTagName('head')[0].getAttribute('data-fileid'); var userid = document.getElementsByTagName('head')[0].getAttribute('data-userid'); var displayname = document.getElementsByTagName('head')[0].getAttribute('data-displayname'); var socketurl = document.getElementsByTagName('head')[0].getAttribute('data-socketurl'); var token = document.getElementsByTagName('head')[0].getAttribute('data-token'); var permissions = parseInt(document.getElementsByTagName('head')[0].getAttribute('data-permissions'), 10); if (!fileid || !socketurl || !token) { window.location.href = '/'; return; } var can_update = ((permissions & PERMISSION_UPDATE) === PERMISSION_UPDATE); var annotator = new Annotator(socketurl, fileid, userid, displayname, token); PDFViewerApplication.initializedPromise.then(function() { var eventBus = PDFViewerApplication.eventBus; eventBus.on('pagesloaded', function(event) { annotator.documentLoaded(PDFViewerApplication.pdfViewer.pdfDocument); if (!can_update) { $('#viewer .page[data-page-number!=1]').addClass('hiddenPage'); } }); eventBus.on('pagerendered', function(event) { var pagenum = event.pageNumber; if (!pagenum) { console.log("Rendered event without page number", event); return; } var container = $("#viewer .page[data-page-number='" + pagenum + "']"); if (!container.length) { console.log("Could not get page container", event); return; } PDFViewerApplication.pdfViewer.pdfDocument.getPage(pagenum).then(function(page) { annotator.getPage(pagenum, page, container).then(function(page_annotator) { page_annotator.update(PDFViewerApplication.pdfViewer.currentScale); }); }); }); eventBus.on('pagechanging', function(event) { var pagenum = event.pageNumber; if (can_update) { annotator.switchPage(pagenum); } else { $('#viewer .page').addClass('hiddenPage'); var page = $('#viewer .page[data-page-number=' + pagenum +']'); page.removeClass('hiddenPage'); // Need to redraw currently visible page to fix any layout issues. setTimeout(function() { PDFViewerApplication.pdfViewer.update(); }, 0); } }); }); $(".modeButton").click(function(event) { var button = $(event.target); var mode = button.data("mode") || null; if (mode === "color") { annotator.toggleSettings(); } else { annotator.setDrawMode(mode); } }); var $btnDrawMode = $(".toolbarButton.drawMode"); $btnDrawMode.click(function(event) { $('#drawMenuToolbar').toggleClass('hidden'); }); $('#drawMenuToolbar .toolbarButton').each(function(_, elem) { var $elem = $(elem); $elem.click(function() { var mode = $(this).data('mode'); // TODO(leon): This is an ugly hack to determine the previous mode $btnDrawMode.get(0).classList.forEach(function(c) { if (c !== 'drawMode' && c.indexOf('Mode') !== -1) { $btnDrawMode.removeClass(c); } }); $btnDrawMode.addClass(mode + 'Mode'); $('#drawMenuToolbar').addClass('hidden'); }); }); $("#downloadPdf").click(function(event) { annotator.downloadPdf(); }); $("#secondaryToolbarClose").click(function() { history.back(); }); if (!can_update) { // User may not modify the file. $('#outerContainer').addClass('readonly'); annotator.setDrawMode(null); } if (history.length <= 1) { // Annotation was opened in a new window. Closing windows through JS is not // possible - that's why we simply hide the button to not confuse users. $("#secondaryToolbarClose").hide(); } }); function setupPdfJs() { console.log("Loaded pdf.js", pdfjsLib.version, pdfjsLib.build); PDFViewerApplicationOptions.set("sidebarViewOnLoad", 0); PDFViewerApplicationOptions.set("showPreviousViewOnLoad", false); PDFViewerApplicationOptions.set("disablePageMode", true); PDFViewerApplicationOptions.set("isEvalSupported", false); PDFViewerApplicationOptions.set("cMapUrl", document.getElementsByTagName('head')[0].getAttribute('data-cmapurl')); PDFViewerApplicationOptions.set("workerSrc", document.getElementsByTagName('head')[0].getAttribute('data-workersrc')); } if (document.readyState === 'interactive' || document.readyState === 'complete') { setupPdfJs(); } else { document.addEventListener('DOMContentLoaded', setupPdfJs, true); } })();