diff --git a/src/lib/Browser.js b/src/lib/Browser.js index 322d6f7bf..10d834313 100644 --- a/src/lib/Browser.js +++ b/src/lib/Browser.js @@ -1,3 +1,5 @@ +import { BROWSERS } from './constants'; + const MIME_H264_BASELINE = 'video/mp4; codecs="avc1.42E01E"'; const MIME_H264_MAIN = 'video/mp4; codecs="avc1.4D401E"'; const MIME_H264_HIGH = 'video/mp4; codecs="avc1.64001E"'; @@ -36,17 +38,17 @@ class Browser { } if (userAgent.indexOf('Edge/') > 0) { - name = 'Edge'; + name = BROWSERS.EDGE; } else if (userAgent.indexOf('OPR/') > 0 || userAgent.indexOf('Opera/') > 0) { - name = 'Opera'; + name = BROWSERS.OPERA; } else if (userAgent.indexOf('Chrome/') > 0) { - name = 'Chrome'; + name = BROWSERS.CHROME; } else if (userAgent.indexOf('Safari/') > 0) { - name = 'Safari'; + name = BROWSERS.SAFARI; } else if (userAgent.indexOf('Trident/') > 0) { - name = 'Explorer'; + name = BROWSERS.INTERNET_EXPLORER; } else if (userAgent.indexOf('Firefox/') > 0) { - name = 'Firefox'; + name = BROWSERS.FIREFOX; } return name; diff --git a/src/lib/PageControls.js b/src/lib/PageControls.js index b572550e4..84f0686aa 100644 --- a/src/lib/PageControls.js +++ b/src/lib/PageControls.js @@ -1,6 +1,7 @@ import EventEmitter from 'events'; import fullscreen from './Fullscreen'; import Browser from './Browser'; +import { BROWSERS } from './constants'; import { decodeKeydown } from './util'; import { ICON_DROP_DOWN, ICON_DROP_UP } from './icons/icons'; @@ -271,7 +272,7 @@ class PageControls extends EventEmitter { // we blur the page behind the controls - this unfortunately // is an IE-only solution that doesn't work with other browsers - if (Browser.getName() !== 'Explorer') { + if (Browser.getName() !== BROWSERS.INTERNET_EXPLORER) { event.target.blur(); } diff --git a/src/lib/__tests__/PageControls-test.js b/src/lib/__tests__/PageControls-test.js index 6ec36646d..68fb3b587 100644 --- a/src/lib/__tests__/PageControls-test.js +++ b/src/lib/__tests__/PageControls-test.js @@ -3,6 +3,7 @@ import PageControls from '../PageControls'; import Controls from '../Controls'; import fullscreen from '../Fullscreen'; import Browser from '../Browser'; +import { BROWSERS } from '../constants'; let pageControls; let stubs = {}; @@ -260,7 +261,7 @@ describe('lib/PageControls', () => { pageControls.contentEl = { focus: sandbox.stub(), }; - stubs.browser = sandbox.stub(Browser, 'getName').returns('Explorer'); + stubs.browser = sandbox.stub(Browser, 'getName').returns(BROWSERS.INTERNET_EXPLORER); stubs.hidePageNumInput = sandbox.stub(pageControls, 'hidePageNumInput'); }); diff --git a/src/lib/constants.js b/src/lib/constants.js index 54072dac9..bfe39e786 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -131,6 +131,15 @@ export const ANNOTATOR_EVENT = { scale: 'scaleannotations', }; +export const BROWSERS = { + CHROME: 'Chrome', + EDGE: 'Edge', + FIREFOX: 'Firefox', + INTERNET_EXPLORER: 'Explorer', + OPERA: 'Opera', + SAFARI: 'Safari', +}; + export const METADATA = { FIELD_HASXREFS: 'hasxrefs', SCOPE_GLOBAL: 'global', diff --git a/src/lib/events.js b/src/lib/events.js index 4569e0f82..bcfc7c46b 100644 --- a/src/lib/events.js +++ b/src/lib/events.js @@ -1,16 +1,17 @@ // Events emitted by Viewers export const VIEWER_EVENT = { + default: 'viewerevent', // The default viewer event. download: 'download', // Begin downloading the file. - reload: 'reload', // Reload preview. + error: 'error', // When an error occurs. load: 'load', // Preview is finished loading. - progressStart: 'progressstart', // Begin using loading indicator. - progressEnd: 'progressend', // Stop using loading indicator. - notificationShow: 'notificationshow', // Show notification modal. - notificationHide: 'notificationhide', // Hide notification modal. mediaEndAutoplay: 'mediaendautoplay', // Media playback has completed, with autoplay enabled. - error: 'error', // When an error occurs. - default: 'viewerevent', // The default viewer event. metric: 'viewermetric', // A viewer metric. + notificationHide: 'notificationhide', // Hide notification modal. + notificationShow: 'notificationshow', // Show notification modal. + printSuccess: 'printsuccess', // When printing is successful + progressEnd: 'progressend', // Stop using loading indicator. + progressStart: 'progressstart', // Begin using loading indicator. + reload: 'reload', // Reload preview. thumbnailsClose: 'thumbnailsClose', // When thumbnails sidebar closes thumbnailsOpen: 'thumbnailsOpen', // When thumbnails sidebar opens }; @@ -55,14 +56,14 @@ export const PREVIEW_ERROR = 'preview_error'; export const PREVIEW_METRIC = 'preview_metric'; // Milestone events for loading performance export const LOAD_METRIC = { - previewLoadEvent: 'load', // Event name for preview_metric events related to loading times. - previewPreloadEvent: 'preload', // Event name for preview_metrics based on preload times. - fileInfoTime: 'file_info_time', // Round trip time from file info request to received file info. + contentLoadTime: 'full_document_load_time', // How long it took to load the document so it could be previewed. convertTime: 'convert_time', // Time it took from receiving file info to being able to request the rep. downloadResponseTime: 'download_response_time', // Time it took for TTFB when requesting a rep. - contentLoadTime: 'full_document_load_time', // How long it took to load the document so it could be previewed. + fileInfoTime: 'file_info_time', // Round trip time from file info request to received file info. preloadTime: 'preload_time', // How long it takes to preload the document. + previewLoadEvent: 'load', // Event name for preview_metric events related to loading times. previewLoadTime: 'preview_loading', // Total preview load time. Maps to "value" of load event + previewPreloadEvent: 'preload', // Event name for preview_metrics based on preload times. }; export const DURATION_METRIC = 'preview_duration_metric'; @@ -72,8 +73,8 @@ export const PREVIEW_END_EVENT = 'preview_end'; export const PREVIEW_DOWNLOAD_ATTEMPT_EVENT = 'preview_download_attempt'; // Events around download reachability export const DOWNLOAD_REACHABILITY_METRICS = { - NOTIFICATION_SHOWN: 'dl_reachability_notification_shown', DOWNLOAD_BLOCKED: 'dl_reachability_host_blocked', + NOTIFICATION_SHOWN: 'dl_reachability_notification_shown', }; // Events fired when using find in preview export const USER_DOCUMENT_FIND_EVENTS = { diff --git a/src/lib/viewers/image/ImageBaseViewer.js b/src/lib/viewers/image/ImageBaseViewer.js index 41aa37ba4..667a58922 100644 --- a/src/lib/viewers/image/ImageBaseViewer.js +++ b/src/lib/viewers/image/ImageBaseViewer.js @@ -4,8 +4,9 @@ import Browser from '../../Browser'; import PreviewError from '../../PreviewError'; import { ICON_ZOOM_IN, ICON_ZOOM_OUT } from '../../icons/icons'; -import { CLASS_INVISIBLE } from '../../constants'; +import { BROWSERS, CLASS_INVISIBLE } from '../../constants'; import { ERROR_CODE, VIEWER_EVENT } from '../../events'; +import { openContentInsideIframe } from '../../util'; const CSS_CLASS_PANNING = 'panning'; const CSS_CLASS_ZOOMABLE = 'zoomable'; @@ -434,6 +435,43 @@ class ImageBaseViewer extends BaseViewer { } } + /** + * Prints image using an an iframe. + * + * @return {void} + */ + print() { + const browserName = Browser.getName(); + + /** + * Called async to ensure resource is loaded for print preview. Then removes listener to prevent + * multiple handlers. + * + * @return {void} + */ + const defaultPrintHandler = () => { + if (browserName === BROWSERS.INTERNET_EXPLORER || browserName === BROWSERS.EDGE) { + this.printframe.contentWindow.document.execCommand('print', false, null); + } else { + this.printframe.contentWindow.print(); + } + + this.printframe.removeEventListener('load', defaultPrintHandler); + this.emit(VIEWER_EVENT.printSuccess); + }; + + this.printframe = openContentInsideIframe(this.imageEl.outerHTML); + this.printImages = this.printframe.contentDocument.querySelectorAll('img'); + + for (let i = 0; i < this.printImages.length; i += 1) { + this.printImages[i].setAttribute('style', 'display: block; margin: 0 auto; width: 100%'); + } + + this.printframe.contentWindow.focus(); + + this.printframe.addEventListener(VIEWER_EVENT.load, defaultPrintHandler); + } + //-------------------------------------------------------------------------- // Abstract //-------------------------------------------------------------------------- diff --git a/src/lib/viewers/image/ImageViewer.js b/src/lib/viewers/image/ImageViewer.js index 00f066722..8a2d58b66 100644 --- a/src/lib/viewers/image/ImageViewer.js +++ b/src/lib/viewers/image/ImageViewer.js @@ -1,8 +1,6 @@ -import Browser from '../../Browser'; import ImageBaseViewer from './ImageBaseViewer'; import { ICON_FULLSCREEN_IN, ICON_FULLSCREEN_OUT, ICON_ROTATE_LEFT } from '../../icons/icons'; import { CLASS_INVISIBLE } from '../../constants'; -import * as util from '../../util'; import './Image.scss'; const CSS_CLASS_IMAGE = 'bp-image'; @@ -50,7 +48,7 @@ class ImageViewer extends ImageBaseViewer { /** * Loads an Image. * - * @return {void} + * @return {Promise} */ load() { super.load(); @@ -285,40 +283,6 @@ class ImageViewer extends ImageBaseViewer { this.controls.add(__('exit_fullscreen'), this.toggleFullscreen, 'bp-exit-fullscreen-icon', ICON_FULLSCREEN_OUT); } - /** - * Prints image using an an iframe. - * - * @return {void} - */ - print() { - const browserName = Browser.getName(); - - /** - * Called async to ensure resource is loaded for print preview. Then removes listener to prevent - * multiple handlers. - * - * @return {void} - */ - const defaultPrintHandler = () => { - if (browserName === 'Explorer' || browserName === 'Edge') { - this.printframe.contentWindow.document.execCommand('print', false, null); - } else { - this.printframe.contentWindow.print(); - } - - this.printframe.removeEventListener('load', defaultPrintHandler); - this.emit('printsuccess'); - }; - - this.printframe = util.openContentInsideIframe(this.imageEl.outerHTML); - this.printframe.addEventListener('load', defaultPrintHandler); - this.printframe.contentWindow.focus(); - - this.printImage = this.printframe.contentDocument.querySelector('img'); - this.printImage.style.display = 'block'; - this.printImage.style.margin = '0 auto'; - } - /** * Determines if Image file has been rotated 90 or 270 degrees to the left * diff --git a/src/lib/viewers/image/__tests__/ImageBaseViewer-test.html b/src/lib/viewers/image/__tests__/ImageBaseViewer-test.html index d88065f69..abcd56334 100644 --- a/src/lib/viewers/image/__tests__/ImageBaseViewer-test.html +++ b/src/lib/viewers/image/__tests__/ImageBaseViewer-test.html @@ -1,13 +1,18 @@ -
\ No newline at end of file +
+
+
+
+
diff --git a/src/lib/viewers/image/__tests__/ImageBaseViewer-test.js b/src/lib/viewers/image/__tests__/ImageBaseViewer-test.js index cdaeaf345..bcc794a32 100644 --- a/src/lib/viewers/image/__tests__/ImageBaseViewer-test.js +++ b/src/lib/viewers/image/__tests__/ImageBaseViewer-test.js @@ -7,6 +7,7 @@ import fullscreen from '../../../Fullscreen'; import PreviewError from '../../../PreviewError'; import { ICON_ZOOM_IN, ICON_ZOOM_OUT } from '../../../icons/icons'; import { VIEWER_EVENT } from '../../../events'; +import * as util from '../../../util'; const CSS_CLASS_PANNING = 'panning'; const CSS_CLASS_ZOOMABLE = 'zoomable'; @@ -648,4 +649,74 @@ describe('lib/viewers/image/ImageBaseViewer', () => { expect(imageBase.updateCursor).to.be.called; }); }); + + describe('print()', () => { + beforeEach(() => { + stubs.execCommand = sandbox.stub(); + stubs.focus = sandbox.stub(); + stubs.print = sandbox.stub(); + stubs.mockIframe = { + addEventListener() {}, + contentWindow: { + document: { + execCommand: stubs.execCommand, + }, + focus: stubs.focus, + print: stubs.print, + }, + contentDocument: { + querySelectorAll: sandbox.stub().returns(containerEl.querySelectorAll('img')), + }, + removeEventListener() {}, + }; + + stubs.openContentInsideIframe = sandbox.stub(util, 'openContentInsideIframe').returns(stubs.mockIframe); + stubs.getName = sandbox.stub(Browser, 'getName'); + }); + + it('should open the content inside an iframe, center, and focus', () => { + imageBase.print(); + expect(stubs.openContentInsideIframe).to.be.called; + expect(imageBase.printImages[0].getAttribute('style')).to.be.equal( + 'display: block; margin: 0 auto; width: 100%', + ); + expect(stubs.focus).to.be.called; + }); + + it('should execute the print command if the browser is Explorer', done => { + stubs.getName.returns('Explorer'); + stubs.mockIframe.addEventListener = (type, callback) => { + callback(); + expect(stubs.execCommand).to.be.calledWith('print', false, null); + + done(); + }; + + imageBase.print(); + }); + + it('should execute the print command if the browser is Edge', done => { + stubs.getName.returns('Edge'); + stubs.mockIframe.addEventListener = (type, callback) => { + callback(); + expect(stubs.execCommand).to.be.calledWith('print', false, null); + + done(); + }; + + imageBase.print(); + }); + + it('should call the contentWindow print for other browsers', done => { + stubs.getName.returns('Chrome'); + stubs.mockIframe.addEventListener = (type, callback) => { + callback(); + expect(stubs.print).to.be.called; + + done(); + }; + + imageBase.print(); + }); + }); }); diff --git a/src/lib/viewers/image/__tests__/ImageViewer-test.js b/src/lib/viewers/image/__tests__/ImageViewer-test.js index 7e3ba5eb2..ee7441649 100644 --- a/src/lib/viewers/image/__tests__/ImageViewer-test.js +++ b/src/lib/viewers/image/__tests__/ImageViewer-test.js @@ -2,7 +2,6 @@ import ImageViewer from '../ImageViewer'; import BaseViewer from '../../BaseViewer'; import Browser from '../../../Browser'; -import * as util from '../../../util'; const sandbox = sinon.sandbox.create(); const imageUrl = @@ -347,75 +346,6 @@ describe('lib/viewers/image/ImageViewer', () => { }); }); - describe('print()', () => { - beforeEach(() => { - stubs.execCommand = sandbox.stub(); - stubs.focus = sandbox.stub(); - stubs.print = sandbox.stub(); - stubs.mockIframe = { - addEventListener() {}, - contentWindow: { - document: { - execCommand: stubs.execCommand, - }, - focus: stubs.focus, - print: stubs.print, - }, - contentDocument: { - querySelector: sandbox.stub().returns(containerEl.querySelector('img')), - }, - removeEventListener() {}, - }; - - stubs.openContentInsideIframe = sandbox.stub(util, 'openContentInsideIframe').returns(stubs.mockIframe); - stubs.getName = sandbox.stub(Browser, 'getName'); - }); - - it('should open the content inside an iframe, center, and focus', () => { - image.print(); - expect(stubs.openContentInsideIframe).to.be.called; - expect(image.printImage.style.display).to.equal('block'); - expect(image.printImage.style.margin).to.equal('0px auto'); - expect(stubs.focus).to.be.called; - }); - - it('should execute the print command if the browser is Explorer', done => { - stubs.getName.returns('Explorer'); - stubs.mockIframe.addEventListener = (type, callback) => { - callback(); - expect(stubs.execCommand).to.be.calledWith('print', false, null); - - done(); - }; - - image.print(); - }); - - it('should execute the print command if the browser is Edge', done => { - stubs.getName.returns('Edge'); - stubs.mockIframe.addEventListener = (type, callback) => { - callback(); - expect(stubs.execCommand).to.be.calledWith('print', false, null); - - done(); - }; - - image.print(); - }); - - it('should call the contentWindow print for other browsers', done => { - stubs.getName.returns('Chrome'); - stubs.mockIframe.addEventListener = (type, callback) => { - callback(); - expect(stubs.print).to.be.called; - - done(); - }; - - image.print(); - }); - }); - describe('isRotated()', () => { it('should return false if image is not rotated', () => { const result = image.isRotated(); diff --git a/src/lib/viewers/text/PlainTextViewer.js b/src/lib/viewers/text/PlainTextViewer.js index 2642a37b2..da2754a60 100644 --- a/src/lib/viewers/text/PlainTextViewer.js +++ b/src/lib/viewers/text/PlainTextViewer.js @@ -2,7 +2,7 @@ import './Text.scss'; import TextBaseViewer from './TextBaseViewer'; import Browser from '../../Browser'; import Popup from '../../Popup'; -import { CLASS_HIDDEN, CLASS_IS_SCROLLABLE, TEXT_STATIC_ASSETS_VERSION } from '../../constants'; +import { BROWSERS, CLASS_HIDDEN, CLASS_IS_SCROLLABLE, TEXT_STATIC_ASSETS_VERSION } from '../../constants'; import { ICON_PRINT_CHECKMARK } from '../../icons/icons'; import { HIGHLIGHTTABLE_EXTENSIONS } from '../../extensions'; import { openContentInsideIframe, createAssetUrlCreator, createStylesheet } from '../../util'; @@ -314,7 +314,7 @@ class PlainTextViewer extends TextBaseViewer { */ printIframe() { this.printframe.contentWindow.focus(); - if (Browser.getName() === 'Explorer' || Browser.getName() === 'Edge') { + if (Browser.getName() === BROWSERS.INTERNET_EXPLORER || Browser.getName() === BROWSERS.EDGE) { this.printframe.contentWindow.document.execCommand('print', false, null); } else { this.printframe.contentWindow.print();