diff --git a/web/pdf_attachment_viewer.js b/web/pdf_attachment_viewer.js index 3720b47513e23..62c957b21abcf 100644 --- a/web/pdf_attachment_viewer.js +++ b/web/pdf_attachment_viewer.js @@ -30,16 +30,13 @@ import { * @property {Array|null} attachments - An array of attachment objects. */ -/** - * @class - */ -var PDFAttachmentViewer = (function PDFAttachmentViewerClosure() { +class PDFAttachmentViewer { /** - * @constructs PDFAttachmentViewer * @param {PDFAttachmentViewerOptions} options */ - function PDFAttachmentViewer(options) { + constructor(options) { this.attachments = null; + this.container = options.container; this.eventBus = options.eventBus; this.downloadManager = options.downloadManager; @@ -49,152 +46,145 @@ var PDFAttachmentViewer = (function PDFAttachmentViewerClosure() { this._appendAttachment.bind(this)); } - PDFAttachmentViewer.prototype = { - reset: function PDFAttachmentViewer_reset(keepRenderedCapability) { - this.attachments = null; + reset(keepRenderedCapability = false) { + this.attachments = null; - // Remove the attachments from the DOM. - this.container.textContent = ''; + // Remove the attachments from the DOM. + this.container.textContent = ''; - if (!keepRenderedCapability) { - // NOTE: The *only* situation in which the `_renderedCapability` should - // not be replaced is when appending file attachment annotations. - this._renderedCapability = createPromiseCapability(); - } - }, - - /** - * @private - */ - _dispatchEvent: - function PDFAttachmentViewer_dispatchEvent(attachmentsCount) { - this.eventBus.dispatch('attachmentsloaded', { - source: this, - attachmentsCount: attachmentsCount, - }); + if (!keepRenderedCapability) { + // NOTE: The *only* situation in which the `_renderedCapability` should + // not be replaced is when appending file attachment annotations. + this._renderedCapability = createPromiseCapability(); + } + } + + /** + * @private + */ + _dispatchEvent(attachmentsCount) { + this.eventBus.dispatch('attachmentsloaded', { + source: this, + attachmentsCount, + }); - this._renderedCapability.resolve(); - }, + this._renderedCapability.resolve(); + } - /** - * @private - */ - _bindPdfLink(button, content, filename) { - if (PDFJS.disableCreateObjectURL) { - throw new Error('bindPdfLink: ' + - 'Unsupported "PDFJS.disableCreateObjectURL" value.'); + /** + * @private + */ + _bindPdfLink(button, content, filename) { + if (PDFJS.disableCreateObjectURL) { + throw new Error('bindPdfLink: ' + + 'Unsupported "PDFJS.disableCreateObjectURL" value.'); + } + var blobUrl; + button.onclick = function() { + if (!blobUrl) { + blobUrl = createObjectURL(content, 'application/pdf'); } - var blobUrl; - button.onclick = function() { - if (!blobUrl) { - blobUrl = createObjectURL(content, 'application/pdf'); - } - var viewerUrl; - if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { - // The current URL is the viewer, let's use it and append the file. - viewerUrl = '?file=' + encodeURIComponent(blobUrl + '#' + filename); - } else if (PDFJSDev.test('CHROME')) { - // In the Chrome extension, the URL is rewritten using the history API - // in viewer.js, so an absolute URL must be generated. - // eslint-disable-next-line no-undef - viewerUrl = chrome.runtime.getURL('/content/web/viewer.html') + - '?file=' + encodeURIComponent(blobUrl + '#' + filename); - } else if (PDFJSDev.test('FIREFOX || MOZCENTRAL')) { - // Let Firefox's content handler catch the URL and display the PDF. - viewerUrl = blobUrl + '?' + encodeURIComponent(filename); - } - window.open(viewerUrl); - return false; - }; - }, - - /** - * @private - */ - _bindLink: - function PDFAttachmentViewer_bindLink(button, content, filename) { - button.onclick = function downloadFile(e) { - this.downloadManager.downloadData(content, filename, ''); - return false; - }.bind(this); - }, - - /** - * @param {PDFAttachmentViewerRenderParameters} params - */ - render: function PDFAttachmentViewer_render(params) { - params = params || {}; - var attachments = params.attachments || null; - var attachmentsCount = 0; - - if (this.attachments) { - var keepRenderedCapability = params.keepRenderedCapability === true; - this.reset(keepRenderedCapability); + var viewerUrl; + if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { + // The current URL is the viewer, let's use it and append the file. + viewerUrl = '?file=' + encodeURIComponent(blobUrl + '#' + filename); + } else if (PDFJSDev.test('CHROME')) { + // In the Chrome extension, the URL is rewritten using the history API + // in viewer.js, so an absolute URL must be generated. + // eslint-disable-next-line no-undef + viewerUrl = chrome.runtime.getURL('/content/web/viewer.html') + + '?file=' + encodeURIComponent(blobUrl + '#' + filename); + } else if (PDFJSDev.test('FIREFOX || MOZCENTRAL')) { + // Let Firefox's content handler catch the URL and display the PDF. + viewerUrl = blobUrl + '?' + encodeURIComponent(filename); } - this.attachments = attachments; + window.open(viewerUrl); + return false; + }; + } - if (!attachments) { - this._dispatchEvent(attachmentsCount); - return; - } + /** + * @private + */ + _bindLink(button, content, filename) { + button.onclick = () => { + this.downloadManager.downloadData(content, filename, ''); + return false; + }; + } - var names = Object.keys(attachments).sort(function(a, b) { - return a.toLowerCase().localeCompare(b.toLowerCase()); - }); - attachmentsCount = names.length; - - for (var i = 0; i < attachmentsCount; i++) { - var item = attachments[names[i]]; - var filename = removeNullCharacters(getFilenameFromUrl(item.filename)); - - var div = document.createElement('div'); - div.className = 'attachmentsItem'; - var button = document.createElement('button'); - button.textContent = filename; - if (/\.pdf$/i.test(filename) && !PDFJS.disableCreateObjectURL) { - this._bindPdfLink(button, item.content, filename); - } else { - this._bindLink(button, item.content, filename); - } + /** + * @param {PDFAttachmentViewerRenderParameters} params + */ + render(params = {}) { + var attachments = params.attachments || null; + var attachmentsCount = 0; - div.appendChild(button); - this.container.appendChild(div); - } + if (this.attachments) { + var keepRenderedCapability = params.keepRenderedCapability === true; + this.reset(keepRenderedCapability); + } + this.attachments = attachments; + if (!attachments) { this._dispatchEvent(attachmentsCount); - }, - - /** - * Used to append FileAttachment annotations to the sidebar. - * @private - */ - _appendAttachment: function PDFAttachmentViewer_appendAttachment(item) { - this._renderedCapability.promise.then(function (id, filename, content) { - var attachments = this.attachments; - - if (!attachments) { - attachments = Object.create(null); - } else { - for (var name in attachments) { - if (id === name) { - return; // Ignore the new attachment if it already exists. - } + return; + } + + var names = Object.keys(attachments).sort(function(a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()); + }); + attachmentsCount = names.length; + + for (var i = 0; i < attachmentsCount; i++) { + var item = attachments[names[i]]; + var filename = removeNullCharacters(getFilenameFromUrl(item.filename)); + + var div = document.createElement('div'); + div.className = 'attachmentsItem'; + var button = document.createElement('button'); + button.textContent = filename; + if (/\.pdf$/i.test(filename) && !PDFJS.disableCreateObjectURL) { + this._bindPdfLink(button, item.content, filename); + } else { + this._bindLink(button, item.content, filename); + } + + div.appendChild(button); + this.container.appendChild(div); + } + + this._dispatchEvent(attachmentsCount); + } + + /** + * Used to append FileAttachment annotations to the sidebar. + * @private + */ + _appendAttachment(item) { + this._renderedCapability.promise.then(function (id, filename, content) { + var attachments = this.attachments; + + if (!attachments) { + attachments = Object.create(null); + } else { + for (var name in attachments) { + if (id === name) { + return; // Ignore the new attachment if it already exists. } } - attachments[id] = { - filename: filename, - content: content, - }; - this.render({ - attachments: attachments, - keepRenderedCapability: true, - }); - }.bind(this, item.id, item.filename, item.content)); - }, - }; - - return PDFAttachmentViewer; -})(); + } + attachments[id] = { + filename, + content, + }; + this.render({ + attachments, + keepRenderedCapability: true, + }); + }.bind(this, item.id, item.filename, item.content)); + } +} export { PDFAttachmentViewer, diff --git a/web/pdf_outline_viewer.js b/web/pdf_outline_viewer.js index 5a2052b6a8bf5..42fc90e51630f 100644 --- a/web/pdf_outline_viewer.js +++ b/web/pdf_outline_viewer.js @@ -17,7 +17,7 @@ import { addLinkAttributes, PDFJS, removeNullCharacters } from './pdfjs'; -var DEFAULT_TITLE = '\u2013'; +const DEFAULT_TITLE = '\u2013'; /** * @typedef {Object} PDFOutlineViewerOptions @@ -31,194 +31,187 @@ var DEFAULT_TITLE = '\u2013'; * @property {Array|null} outline - An array of outline objects. */ -/** - * @class - */ -var PDFOutlineViewer = (function PDFOutlineViewerClosure() { +class PDFOutlineViewer { /** - * @constructs PDFOutlineViewer * @param {PDFOutlineViewerOptions} options */ - function PDFOutlineViewer(options) { + constructor(options) { this.outline = null; this.lastToggleIsShow = true; + this.container = options.container; this.linkService = options.linkService; this.eventBus = options.eventBus; } - PDFOutlineViewer.prototype = { - reset: function PDFOutlineViewer_reset() { - this.outline = null; - this.lastToggleIsShow = true; - - // Remove the outline from the DOM. - this.container.textContent = ''; - // Ensure that the left (right in RTL locales) margin is always reset, - // to prevent incorrect outline alignment if a new document is opened. - this.container.classList.remove('outlineWithDeepNesting'); - }, - - /** - * @private - */ - _dispatchEvent: function PDFOutlineViewer_dispatchEvent(outlineCount) { - this.eventBus.dispatch('outlineloaded', { - source: this, - outlineCount: outlineCount + reset() { + this.outline = null; + this.lastToggleIsShow = true; + + // Remove the outline from the DOM. + this.container.textContent = ''; + + // Ensure that the left (right in RTL locales) margin is always reset, + // to prevent incorrect outline alignment if a new document is opened. + this.container.classList.remove('outlineWithDeepNesting'); + } + + /** + * @private + */ + _dispatchEvent(outlineCount) { + this.eventBus.dispatch('outlineloaded', { + source: this, + outlineCount, + }); + } + + /** + * @private + */ + _bindLink(element, item) { + if (item.url) { + addLinkAttributes(element, { + url: item.url, + target: (item.newWindow ? PDFJS.LinkTarget.BLANK : undefined), }); - }, - - /** - * @private - */ - _bindLink: function PDFOutlineViewer_bindLink(element, item) { - if (item.url) { - addLinkAttributes(element, { - url: item.url, - target: (item.newWindow ? PDFJS.LinkTarget.BLANK : undefined), - }); - return; - } - var self = this, destination = item.dest; + return; + } + var destination = item.dest; - element.href = self.linkService.getDestinationHash(destination); - element.onclick = function () { - if (destination) { - self.linkService.navigateTo(destination); - } - return false; - }; - }, - - /** - * @private - */ - _setStyles: function PDFOutlineViewer_setStyles(element, item) { - var styleStr = ''; - if (item.bold) { - styleStr += 'font-weight: bold;'; - } - if (item.italic) { - styleStr += 'font-style: italic;'; + element.href = this.linkService.getDestinationHash(destination); + element.onclick = () => { + if (destination) { + this.linkService.navigateTo(destination); } + return false; + }; + } - if (styleStr) { - element.setAttribute('style', styleStr); - } - }, - - /** - * Prepend a button before an outline item which allows the user to toggle - * the visibility of all outline items at that level. - * - * @private - */ - _addToggleButton: function PDFOutlineViewer_addToggleButton(div) { - var toggler = document.createElement('div'); - toggler.className = 'outlineItemToggler'; - toggler.onclick = function(event) { - event.stopPropagation(); - toggler.classList.toggle('outlineItemsHidden'); - - if (event.shiftKey) { - var shouldShowAll = !toggler.classList.contains('outlineItemsHidden'); - this._toggleOutlineItem(div, shouldShowAll); - } - }.bind(this); - div.insertBefore(toggler, div.firstChild); - }, - - /** - * Toggle the visibility of the subtree of an outline item. - * - * @param {Element} root - the root of the outline (sub)tree. - * @param {boolean} show - whether to show the outline (sub)tree. If false, - * the outline subtree rooted at |root| will be collapsed. - * - * @private - */ - _toggleOutlineItem: - function PDFOutlineViewer_toggleOutlineItem(root, show) { - this.lastToggleIsShow = show; - var togglers = root.querySelectorAll('.outlineItemToggler'); - for (var i = 0, ii = togglers.length; i < ii; ++i) { - togglers[i].classList[show ? 'remove' : 'add']('outlineItemsHidden'); - } - }, - - /** - * Collapse or expand all subtrees of the outline. - */ - toggleOutlineTree: function PDFOutlineViewer_toggleOutlineTree() { - if (!this.outline) { - return; - } - this._toggleOutlineItem(this.container, !this.lastToggleIsShow); - }, - - /** - * @param {PDFOutlineViewerRenderParameters} params - */ - render: function PDFOutlineViewer_render(params) { - var outline = (params && params.outline) || null; - var outlineCount = 0; - - if (this.outline) { - this.reset(); - } - this.outline = outline; + /** + * @private + */ + _setStyles(element, item) { + var styleStr = ''; + if (item.bold) { + styleStr += 'font-weight: bold;'; + } + if (item.italic) { + styleStr += 'font-style: italic;'; + } - if (!outline) { - this._dispatchEvent(outlineCount); - return; - } + if (styleStr) { + element.setAttribute('style', styleStr); + } + } - var fragment = document.createDocumentFragment(); - var queue = [{ parent: fragment, items: this.outline }]; - var hasAnyNesting = false; - while (queue.length > 0) { - var levelData = queue.shift(); - for (var i = 0, len = levelData.items.length; i < len; i++) { - var item = levelData.items[i]; - - var div = document.createElement('div'); - div.className = 'outlineItem'; - - var element = document.createElement('a'); - this._bindLink(element, item); - this._setStyles(element, item); - element.textContent = - removeNullCharacters(item.title) || DEFAULT_TITLE; - - div.appendChild(element); - - if (item.items.length > 0) { - hasAnyNesting = true; - this._addToggleButton(div); - - var itemsDiv = document.createElement('div'); - itemsDiv.className = 'outlineItems'; - div.appendChild(itemsDiv); - queue.push({ parent: itemsDiv, items: item.items }); - } - - levelData.parent.appendChild(div); - outlineCount++; - } - } - if (hasAnyNesting) { - this.container.classList.add('outlineWithDeepNesting'); + /** + * Prepend a button before an outline item which allows the user to toggle + * the visibility of all outline items at that level. + * + * @private + */ + _addToggleButton(div) { + var toggler = document.createElement('div'); + toggler.className = 'outlineItemToggler'; + toggler.onclick = (evt) => { + evt.stopPropagation(); + toggler.classList.toggle('outlineItemsHidden'); + + if (evt.shiftKey) { + var shouldShowAll = !toggler.classList.contains('outlineItemsHidden'); + this._toggleOutlineItem(div, shouldShowAll); } + }; + div.insertBefore(toggler, div.firstChild); + } - this.container.appendChild(fragment); + /** + * Toggle the visibility of the subtree of an outline item. + * + * @param {Element} root - the root of the outline (sub)tree. + * @param {boolean} show - whether to show the outline (sub)tree. If false, + * the outline subtree rooted at |root| will be collapsed. + * + * @private + */ + _toggleOutlineItem(root, show) { + this.lastToggleIsShow = show; + var togglers = root.querySelectorAll('.outlineItemToggler'); + for (var i = 0, ii = togglers.length; i < ii; ++i) { + togglers[i].classList[show ? 'remove' : 'add']('outlineItemsHidden'); + } + } + /** + * Collapse or expand all subtrees of the outline. + */ + toggleOutlineTree() { + if (!this.outline) { + return; + } + this._toggleOutlineItem(this.container, !this.lastToggleIsShow); + } + + /** + * @param {PDFOutlineViewerRenderParameters} params + */ + render(params = {}) { + var outline = params.outline || null; + var outlineCount = 0; + + if (this.outline) { + this.reset(); + } + this.outline = outline; + + if (!outline) { this._dispatchEvent(outlineCount); + return; + } + + var fragment = document.createDocumentFragment(); + var queue = [{ parent: fragment, items: this.outline }]; + var hasAnyNesting = false; + while (queue.length > 0) { + var levelData = queue.shift(); + for (var i = 0, len = levelData.items.length; i < len; i++) { + var item = levelData.items[i]; + + var div = document.createElement('div'); + div.className = 'outlineItem'; + + var element = document.createElement('a'); + this._bindLink(element, item); + this._setStyles(element, item); + element.textContent = + removeNullCharacters(item.title) || DEFAULT_TITLE; + + div.appendChild(element); + + if (item.items.length > 0) { + hasAnyNesting = true; + this._addToggleButton(div); + + var itemsDiv = document.createElement('div'); + itemsDiv.className = 'outlineItems'; + div.appendChild(itemsDiv); + queue.push({ parent: itemsDiv, items: item.items }); + } + + levelData.parent.appendChild(div); + outlineCount++; + } } - }; + if (hasAnyNesting) { + this.container.classList.add('outlineWithDeepNesting'); + } + + this.container.appendChild(fragment); - return PDFOutlineViewer; -})(); + this._dispatchEvent(outlineCount); + } +} export { PDFOutlineViewer,