Skip to content

Commit

Permalink
Add printing to imagebase (#1085)
Browse files Browse the repository at this point in the history
* feat(printing): Move print() to imageBase

Moving the print function to imageBase will allow
MultiImageViewer to inherit the functionality.
  • Loading branch information
mickr authored Oct 24, 2019
1 parent 42a834c commit 33f37e4
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 133 deletions.
14 changes: 8 additions & 6 deletions src/lib/Browser.js
Original file line number Diff line number Diff line change
@@ -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"';
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/lib/PageControls.js
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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();
}

Expand Down
3 changes: 2 additions & 1 deletion src/lib/__tests__/PageControls-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down Expand Up @@ -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');
});

Expand Down
9 changes: 9 additions & 0 deletions src/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
25 changes: 13 additions & 12 deletions src/lib/events.js
Original file line number Diff line number Diff line change
@@ -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
};
Expand Down Expand Up @@ -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';
Expand All @@ -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 = {
Expand Down
40 changes: 39 additions & 1 deletion src/lib/viewers/image/ImageBaseViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
//--------------------------------------------------------------------------
Expand Down
38 changes: 1 addition & 37 deletions src/lib/viewers/image/ImageViewer.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -50,7 +48,7 @@ class ImageViewer extends ImageBaseViewer {
/**
* Loads an Image.
*
* @return {void}
* @return {Promise}
*/
load() {
super.load();
Expand Down Expand Up @@ -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
*
Expand Down
11 changes: 8 additions & 3 deletions src/lib/viewers/image/__tests__/ImageBaseViewer-test.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
<style>
html, body {
html,
body {
width: 100%;
height: 100%;
}
.container {
width: 500px;
height: 500px;
margin: 200px;
margin: 200px;
background-color: #eee;
}
</style>
<div class="container"><div class="bp-container"><div class="bp"></div></div></div>
<div class="container">
<div class="bp-container">
<div class="bp"><img /></div>
</div>
</div>
71 changes: 71 additions & 0 deletions src/lib/viewers/image/__tests__/ImageBaseViewer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
});
});
});
Loading

0 comments on commit 33f37e4

Please sign in to comment.