diff --git a/src/lib/Controls.js b/src/lib/Controls.js
index f84cf238a..9b6915473 100644
--- a/src/lib/Controls.js
+++ b/src/lib/Controls.js
@@ -1,6 +1,7 @@
import throttle from 'lodash.throttle';
import Browser from './Browser';
import { CLASS_HIDDEN } from './constants';
+import fullscreen from './Fullscreen';
const SHOW_PREVIEW_CONTROLS_CLASS = 'box-show-preview-controls';
const CONTROLS_BUTTON_CLASS = 'bp-controls-btn';
@@ -24,6 +25,18 @@ class Controls {
/** @property {boolean} - Whether browser supports touch */
hasTouch = Browser.hasTouch();
+ /** @property {HTMLElement} - Page num input element */
+ pageNumInputEl;
+
+ /** @property {String} - HTML template for page num element */
+ pageNumTemplate = `
+
+ 1
+
+ /
+ 1
+
`.replace(/>\s*<');
+
/**
* [constructor]
*
@@ -239,6 +252,69 @@ class Controls {
isPageNumFocused() {
return document.activeElement.classList.contains(CONTROLS_PAGE_NUM_INPUT_CLASS);
}
+
+ /**
+ * Initializes page number selector.
+ *
+ * @private
+ * @param {number} pagesCount - Total number of page
+ * @return {void}
+ */
+ initPageNumEl(pagesCount) {
+ const pageNumEl = this.controlsEl.querySelector('.bp-page-num');
+
+ // Update total page number
+ const totalPageEl = pageNumEl.querySelector('.bp-total-pages');
+ totalPageEl.textContent = pagesCount;
+
+ // Keep reference to page number input and current page elements
+ this.pageNumInputEl = pageNumEl.querySelector('.bp-page-num-input');
+ this.pageNumInputEl.setAttribute('max', pagesCount);
+
+ this.currentPageEl = pageNumEl.querySelector('.bp-current-page');
+ }
+
+ /**
+ * Disables or enables previous/next pagination buttons depending on
+ * current page number.
+ *
+ * @return {void}
+ */
+ checkPaginationButtons(currentPageNum, pagesCount) {
+ const pageNumButtonEl = this.containerEl.querySelector('.bp-page-num');
+ const previousPageButtonEl = this.containerEl.querySelector('.bp-previous-page');
+ const nextPageButtonEl = this.containerEl.querySelector('.bp-next-page');
+
+ // Safari disables keyboard input in fullscreen before Safari 10.1
+ const isSafariFullscreen = Browser.getName() === 'Safari' && fullscreen.isFullscreen(this.containerEl);
+
+ // Disable page number selector if there is only one page or less
+ if (pageNumButtonEl) {
+ if (pagesCount <= 1 || isSafariFullscreen) {
+ pageNumButtonEl.disabled = true;
+ } else {
+ pageNumButtonEl.disabled = false;
+ }
+ }
+
+ // Disable previous page if on first page, otherwise enable
+ if (previousPageButtonEl) {
+ if (currentPageNum === 1) {
+ previousPageButtonEl.disabled = true;
+ } else {
+ previousPageButtonEl.disabled = false;
+ }
+ }
+
+ // Disable next page if on last page, otherwise enable
+ if (nextPageButtonEl) {
+ if (currentPageNum === pagesCount) {
+ nextPageButtonEl.disabled = true;
+ } else {
+ nextPageButtonEl.disabled = false;
+ }
+ }
+ }
}
export default Controls;
diff --git a/src/lib/Controls.scss b/src/lib/Controls.scss
index 2ea3831c0..5216ce2cc 100644
--- a/src/lib/Controls.scss
+++ b/src/lib/Controls.scss
@@ -16,6 +16,71 @@
position: relative;
table-layout: fixed;
transition: opacity .5s;
+
+ // Page num input CSS
+ .bp-page-num {
+ min-width: 48px;
+ width: auto; // Let page num expand as needed
+
+ span {
+ display: inline;
+ font-size: 14px;
+ }
+ }
+
+ .bp-page-num-wrapper {
+ background-color: #444;
+ border-radius: 3px;
+ margin: 5px;
+ padding: 7px 5px;
+ }
+
+ /* stylelint-disable property-no-vendor-prefix */
+ // Removes the spinner for number type inputs in Webkit browsers
+ input::-webkit-outer-spin-button,
+ input::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ }
+
+ // Removes the spinner for number type inputs in Firefox
+ input[type=number] {
+ -moz-appearance: textfield;
+ }
+
+ /* stylelint-enable property-no-vendor-prefix */
+
+ .bp-page-num-input {
+ font-size: 14px;
+ margin: 0 auto;
+ position: absolute;
+ text-align: center;
+ visibility: hidden;
+ width: 44px; // hard-coded to solve layout issues
+ }
+
+ &.show-page-number-input {
+ .bp-page-num-wrapper {
+ background-color: transparent;
+ border: none;
+ padding: 0;
+ }
+
+ .bp-page-num {
+ opacity: 1;
+ }
+
+ .bp-current-page,
+ .bp-page-num-divider,
+ .bp-total-pages {
+ display: none;
+ }
+
+ .bp-page-num-input {
+ display: inline-block;
+ position: static;
+ visibility: visible;
+ }
+ }
}
.box-show-preview-controls .bp-controls {
diff --git a/src/lib/viewers/doc/DocBaseViewer.js b/src/lib/viewers/doc/DocBaseViewer.js
index 17c5cc274..06dea4622 100644
--- a/src/lib/viewers/doc/DocBaseViewer.js
+++ b/src/lib/viewers/doc/DocBaseViewer.js
@@ -347,50 +347,6 @@ class DocBaseViewer extends BaseViewer {
this.cache.set(CURRENT_PAGE_MAP_KEY, currentPageMap, true /* useLocalStorage */);
}
- /**
- * Disables or enables previous/next pagination buttons depending on
- * current page number.
- *
- * @return {void}
- */
- checkPaginationButtons() {
- const pagesCount = this.pdfViewer.pagesCount;
- const currentPageNum = this.pdfViewer.currentPageNumber;
- const pageNumButtonEl = this.containerEl.querySelector('.bp-doc-page-num');
- const previousPageButtonEl = this.containerEl.querySelector('.bp-previous-page');
- const nextPageButtonEl = this.containerEl.querySelector('.bp-next-page');
-
- // Safari disables keyboard input in fullscreen before Safari 10.1
- const isSafariFullscreen = Browser.getName() === 'Safari' && fullscreen.isFullscreen(this.containerEl);
-
- // Disable page number selector if there is only one page or less
- if (pageNumButtonEl) {
- if (pagesCount <= 1 || isSafariFullscreen) {
- pageNumButtonEl.disabled = true;
- } else {
- pageNumButtonEl.disabled = false;
- }
- }
-
- // Disable previous page if on first page, otherwise enable
- if (previousPageButtonEl) {
- if (currentPageNum === 1) {
- previousPageButtonEl.disabled = true;
- } else {
- previousPageButtonEl.disabled = false;
- }
- }
-
- // Disable next page if on last page, otherwise enable
- if (nextPageButtonEl) {
- if (currentPageNum === this.pdfViewer.pagesCount) {
- nextPageButtonEl.disabled = true;
- } else {
- nextPageButtonEl.disabled = false;
- }
- }
- }
-
/**
* Zoom into document.
*
@@ -663,26 +619,6 @@ class DocBaseViewer extends BaseViewer {
});
}
- /**
- * Initializes page number selector.
- *
- * @private
- * @return {void}
- */
- initPageNumEl() {
- const pageNumEl = this.controls.controlsEl.querySelector('.bp-doc-page-num');
-
- // Update total page number
- const totalPageEl = pageNumEl.querySelector('.bp-doc-total-pages');
- totalPageEl.textContent = this.pdfViewer.pagesCount;
-
- // Keep reference to page number input and current page elements
- this.pageNumInputEl = pageNumEl.querySelector('.bp-doc-page-num-input');
- this.pageNumInputEl.setAttribute('max', this.pdfViewer.pagesCount);
-
- this.currentPageEl = pageNumEl.querySelector('.bp-doc-current-page');
- }
-
/**
* Fetches PDF and converts to blob for printing.
*
@@ -761,7 +697,7 @@ class DocBaseViewer extends BaseViewer {
loadUI() {
this.controls = new Controls(this.containerEl);
this.bindControlListeners();
- this.initPageNumEl();
+ this.controls.initPageNumEl(this.pdfViewer.pagesCount);
}
/**
@@ -774,13 +710,13 @@ class DocBaseViewer extends BaseViewer {
// show the input box with the current page number selected within it
this.controls.controlsEl.classList.add(SHOW_PAGE_NUM_INPUT_CLASS);
- this.pageNumInputEl.value = this.currentPageEl.textContent;
- this.pageNumInputEl.focus();
- this.pageNumInputEl.select();
+ this.controls.pageNumInputEl.value = this.controls.currentPageEl.textContent;
+ this.controls.pageNumInputEl.focus();
+ this.controls.pageNumInputEl.select();
// finish input when input is blurred or enter key is pressed
- this.pageNumInputEl.addEventListener('blur', this.pageNumInputBlurHandler);
- this.pageNumInputEl.addEventListener('keydown', this.pageNumInputKeydownHandler);
+ this.controls.pageNumInputEl.addEventListener('blur', this.pageNumInputBlurHandler);
+ this.controls.pageNumInputEl.addEventListener('keydown', this.pageNumInputKeydownHandler);
}
/**
@@ -791,8 +727,8 @@ class DocBaseViewer extends BaseViewer {
*/
hidePageNumInput() {
this.controls.controlsEl.classList.remove(SHOW_PAGE_NUM_INPUT_CLASS);
- this.pageNumInputEl.removeEventListener('blur', this.pageNumInputBlurHandler);
- this.pageNumInputEl.removeEventListener('keydown', this.pageNumInputKeydownHandler);
+ this.controls.pageNumInputEl.removeEventListener('blur', this.pageNumInputBlurHandler);
+ this.controls.pageNumInputEl.removeEventListener('keydown', this.pageNumInputKeydownHandler);
}
/**
@@ -813,15 +749,15 @@ class DocBaseViewer extends BaseViewer {
truePageNum = 1;
}
- if (this.pageNumInputEl) {
- this.pageNumInputEl.value = truePageNum;
+ if (this.controls.pageNumInputEl) {
+ this.controls.pageNumInputEl.value = truePageNum;
}
- if (this.currentPageEl) {
- this.currentPageEl.textContent = truePageNum;
+ if (this.controls.currentPageEl) {
+ this.controls.currentPageEl.textContent = truePageNum;
}
- this.checkPaginationButtons();
+ this.controls.checkPaginationButtons(this.pdfViewer.currentPageNumber, this.pdfViewer.pagesCount);
}
//--------------------------------------------------------------------------
@@ -969,7 +905,7 @@ class DocBaseViewer extends BaseViewer {
this.pdfViewer.currentScaleValue = 'auto';
this.loadUI();
- this.checkPaginationButtons();
+ this.controls.checkPaginationButtons(this.pdfViewer.currentPageNumber, this.pdfViewer.pagesCount);
// Set current page to previously opened page or first page
this.setPage(this.getCachedPage());
diff --git a/src/lib/viewers/doc/DocumentViewer.js b/src/lib/viewers/doc/DocumentViewer.js
index c2cce2bcb..382831005 100644
--- a/src/lib/viewers/doc/DocumentViewer.js
+++ b/src/lib/viewers/doc/DocumentViewer.js
@@ -1,5 +1,4 @@
import autobind from 'autobind-decorator';
-import pageNumTemplate from './pageNumButtonContent.html';
import DocBaseViewer from './DocBaseViewer';
import DocPreloader from './DocPreloader';
import fullscreen from '../../Fullscreen';
@@ -109,9 +108,7 @@ class DocumentViewer extends DocBaseViewer {
'bp-doc-previous-page-icon bp-previous-page',
ICON_DROP_UP
);
-
- const buttonContent = pageNumTemplate.replace(/>\s*<'); // removing new lines
- this.controls.add(__('enter_page_num'), this.showPageNumInput, 'bp-doc-page-num', buttonContent);
+ this.controls.add(__('enter_page_num'), this.showPageNumInput, 'bp-page-num', this.controls.pageNumTemplate);
this.controls.add(__('next_page'), this.nextPage, 'bp-doc-next-page-icon bp-next-page', ICON_DROP_DOWN);
this.controls.add(
diff --git a/src/lib/viewers/doc/PresentationViewer.js b/src/lib/viewers/doc/PresentationViewer.js
index 2e014d479..98a4d59c8 100644
--- a/src/lib/viewers/doc/PresentationViewer.js
+++ b/src/lib/viewers/doc/PresentationViewer.js
@@ -1,6 +1,5 @@
import autobind from 'autobind-decorator';
import throttle from 'lodash.throttle';
-import pageNumTemplate from './pageNumButtonContent.html';
import DocBaseViewer from './DocBaseViewer';
import PresentationPreloader from './PresentationPreloader';
import { CLASS_INVISIBLE } from '../../constants';
@@ -199,9 +198,7 @@ class PresentationViewer extends DocBaseViewer {
'bp-presentation-previous-page-icon bp-previous-page',
ICON_DROP_UP
);
-
- const buttonContent = pageNumTemplate.replace(/>\s*<'); // removing new lines
- this.controls.add(__('enter_page_num'), this.showPageNumInput, 'bp-doc-page-num', buttonContent);
+ this.controls.add(__('enter_page_num'), this.showPageNumInput, 'bp-page-num', this.controls.pageNumTemplate);
this.controls.add(
__('next_page'),
diff --git a/src/lib/viewers/doc/_docBase.scss b/src/lib/viewers/doc/_docBase.scss
index 563fda7e5..54c092cdc 100644
--- a/src/lib/viewers/doc/_docBase.scss
+++ b/src/lib/viewers/doc/_docBase.scss
@@ -80,73 +80,6 @@
}
}
-.bp-controls {
- // Page num input CSS
- .bp-doc-page-num {
- min-width: 48px;
- width: auto; // Let page num expand as needed
-
- span {
- display: inline;
- font-size: 14px;
- }
- }
-
- .bp-doc-page-num-wrapper {
- background-color: #444;
- border-radius: 3px;
- margin: 5px;
- padding: 7px 5px;
- }
-
- /* stylelint-disable property-no-vendor-prefix */
- // Removes the spinner for number type inputs in Webkit browsers
- input::-webkit-outer-spin-button,
- input::-webkit-inner-spin-button {
- -webkit-appearance: none;
- }
-
- // Removes the spinner for number type inputs in Firefox
- input[type=number] {
- -moz-appearance: textfield;
- }
-
- /* stylelint-enable property-no-vendor-prefix */
-
- .bp-doc-page-num-input {
- font-size: 14px;
- margin: 0 auto;
- position: absolute;
- text-align: center;
- visibility: hidden;
- width: 44px; // hard-coded to solve layout issues
- }
-
- &.show-page-number-input {
- .bp-doc-page-num-wrapper {
- background-color: transparent;
- border: none;
- padding: 0;
- }
-
- .bp-doc-page-num {
- opacity: 1;
- }
-
- .bp-doc-current-page,
- .bp-doc-page-num-divider,
- .bp-doc-total-pages {
- display: none;
- }
-
- .bp-doc-page-num-input {
- display: inline-block;
- position: static;
- visibility: visible;
- }
- }
-}
-
.bp-print-notification {
display: none;
font-size: 24px;
diff --git a/src/lib/viewers/doc/pageNumButtonContent.html b/src/lib/viewers/doc/pageNumButtonContent.html
deleted file mode 100644
index d6ae11d11..000000000
--- a/src/lib/viewers/doc/pageNumButtonContent.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
- 1
-
- /
- 1
-
diff --git a/src/lib/viewers/image/ImageBaseViewer.js b/src/lib/viewers/image/ImageBaseViewer.js
index bd411cd3f..6cf0ffac3 100644
--- a/src/lib/viewers/image/ImageBaseViewer.js
+++ b/src/lib/viewers/image/ImageBaseViewer.js
@@ -164,6 +164,20 @@ class ImageBaseViewer extends BaseViewer {
*/
loadUI() {
this.controls = new Controls(this.containerEl);
+ this.bindControlListeners();
+ }
+
+ //--------------------------------------------------------------------------
+ // Event Listeners
+ //--------------------------------------------------------------------------
+
+ /**
+ * Bind event listeners for document controls
+ *
+ * @private
+ * @return {void}
+ */
+ bindControlListeners() {
this.controls.add(__('zoom_out'), this.zoomOut, 'bp-image-zoom-out-icon', ICON_ZOOM_OUT);
this.controls.add(__('zoom_in'), this.zoomIn, 'bp-image-zoom-in-icon', ICON_ZOOM_IN);
}
diff --git a/src/lib/viewers/image/MultiImageViewer.js b/src/lib/viewers/image/MultiImageViewer.js
index 004c8230d..c9867b8ed 100644
--- a/src/lib/viewers/image/MultiImageViewer.js
+++ b/src/lib/viewers/image/MultiImageViewer.js
@@ -1,13 +1,21 @@
import autobind from 'autobind-decorator';
import ImageBaseViewer from './ImageBaseViewer';
+import Browser from '../../Browser';
import './MultiImage.scss';
-
-import { ICON_FILE_IMAGE, ICON_FULLSCREEN_IN, ICON_FULLSCREEN_OUT } from '../../icons/icons';
+import {
+ ICON_FILE_IMAGE,
+ ICON_FULLSCREEN_IN,
+ ICON_FULLSCREEN_OUT,
+ ICON_DROP_DOWN,
+ ICON_DROP_UP
+} from '../../icons/icons';
import { CLASS_INVISIBLE } from '../../constants';
+import { decodeKeydown } from '../../util';
const PADDING_BUFFER = 100;
const CSS_CLASS_IMAGE = 'bp-images';
const CSS_CLASS_IMAGE_WRAPPER = 'bp-images-wrapper';
+const SHOW_PAGE_NUM_INPUT_CLASS = 'show-page-number-input';
const ZOOM_UPDATE_PAN_DELAY = 50;
@autobind
@@ -85,11 +93,11 @@ class MultiImageViewer extends ImageBaseViewer {
const { viewer, representation } = this.options;
const metadata = representation.metadata;
const asset = viewer.ASSET;
+ this.pagesCount = metadata.pages;
const urlBase = this.createContentUrlWithAuthParams(template, asset);
-
const urls = [];
- for (let pageNum = 1; pageNum <= metadata.pages; pageNum++) {
+ for (let pageNum = 1; pageNum <= this.pagesCount; pageNum++) {
urls.push(urlBase.replace('{page}', pageNum));
}
@@ -192,6 +200,28 @@ class MultiImageViewer extends ImageBaseViewer {
*/
loadUI() {
super.loadUI();
+ this.controls.initPageNumEl(this.pagesCount);
+ this.controls.checkPaginationButtons(this.currentPageNumber, this.pagesCount);
+ }
+
+ /**
+ * Binds listeners for document controls. Overridden.
+ *
+ * @protected
+ * @return {void}
+ */
+ bindControlListeners() {
+ super.bindControlListeners();
+
+ this.controls.add(
+ __('previous_page'),
+ this.previousPage,
+ 'bp-image-previous-page-icon bp-previous-page',
+ ICON_DROP_UP
+ );
+ this.controls.add(__('enter_page_num'), this.showPageNumInput, 'bp-page-num', this.controls.pageNumTemplate);
+ this.controls.add(__('next_page'), this.nextPage, 'bp-image-next-page-icon bp-next-page', ICON_DROP_DOWN);
+
this.controls.add(
__('enter_fullscreen'),
this.toggleFullscreen,
@@ -236,12 +266,155 @@ class MultiImageViewer extends ImageBaseViewer {
* @return {void}
*/
setPage(pageNum) {
- if (pageNum <= 0 || pageNum > this.singleImageEls.length) {
+ if (pageNum <= 0 || pageNum > this.pagesCount) {
return;
}
this.currentPageNumber = pageNum;
this.singleImageEls[pageNum].scrollIntoView();
+ this.updateCurrentPage(pageNum);
+
+ this.emit('pagefocus', pageNum);
+ }
+
+ /**
+ * Update page number in page control widget.
+ *
+ * @private
+ * @param {number} pageNum - Number of page to update to
+ * @return {void}
+ */
+ updateCurrentPage(pageNum) {
+ let truePageNum = pageNum;
+ const pagesCount = this.pagesCount;
+
+ // refine the page number to fall within bounds
+ if (pageNum > pagesCount) {
+ truePageNum = pagesCount;
+ } else if (pageNum < 1) {
+ truePageNum = 1;
+ }
+
+ if (!this.controls) {
+ return;
+ }
+
+ if (this.controls.pageNumInputEl) {
+ this.controls.pageNumInputEl.value = truePageNum;
+ }
+
+ if (this.controls.currentPageEl) {
+ this.controls.currentPageEl.textContent = truePageNum;
+ }
+
+ this.controls.checkPaginationButtons(this.currentPageNumber, this.pagesCount);
+ }
+
+ /**
+ * Replaces the page number display with an input box that allows the user to type in a page number
+ *
+ * @private
+ * @return {void}
+ */
+ showPageNumInput() {
+ // show the input box with the current page number selected within it
+ this.controls.controlsEl.classList.add(SHOW_PAGE_NUM_INPUT_CLASS);
+
+ this.controls.pageNumInputEl.value = this.controls.currentPageEl.textContent;
+ this.controls.pageNumInputEl.focus();
+ this.controls.pageNumInputEl.select();
+
+ // finish input when input is blurred or enter key is pressed
+ this.controls.pageNumInputEl.addEventListener('blur', this.pageNumInputBlurHandler);
+ this.controls.pageNumInputEl.addEventListener('keydown', this.pageNumInputKeydownHandler);
+ }
+
+ /**
+ * Hide the page number input
+ *
+ * @private
+ * @return {void}
+ */
+ hidePageNumInput() {
+ this.controls.controlsEl.classList.remove(SHOW_PAGE_NUM_INPUT_CLASS);
+ this.controls.pageNumInputEl.removeEventListener('blur', this.pageNumInputBlurHandler);
+ this.controls.pageNumInputEl.removeEventListener('keydown', this.pageNumInputKeydownHandler);
+ }
+
+ /**
+ * Blur handler for page number input.
+ *
+ * @param {Event} event Blur event
+ * @return {void}
+ * @private
+ */
+ pageNumInputBlurHandler(event) {
+ const target = event.target;
+ const pageNum = parseInt(target.value, 10);
+
+ if (!isNaN(pageNum)) {
+ this.setPage(pageNum);
+ }
+
+ this.hidePageNumInput();
+ }
+
+ /**
+ * Keydown handler for page number input.
+ *
+ * @private
+ * @param {Event} event - Keydown event
+ * @return {void}
+ */
+ pageNumInputKeydownHandler(event) {
+ const key = decodeKeydown(event);
+
+ switch (key) {
+ case 'Enter':
+ case 'Tab':
+ // The keycode of the 'next' key on Android Chrome is 9, which maps to 'Tab'.
+ this.singleImageEls[this.currentPageNumber].focus();
+ // We normally trigger the blur handler by blurring the input
+ // field, but this doesn't work for IE in fullscreen. For IE,
+ // 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') {
+ event.target.blur();
+ }
+
+ event.stopPropagation();
+ event.preventDefault();
+ break;
+
+ case 'Escape':
+ this.hidePageNumInput();
+ this.singleImageEls[this.currentPageNumber].focus();
+
+ event.stopPropagation();
+ event.preventDefault();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Go to previous page
+ *
+ * @return {void}
+ */
+ previousPage() {
+ this.setPage(this.currentPageNumber - 1);
+ }
+
+ /**
+ * Go to next page
+ *
+ * @return {void}
+ */
+ nextPage() {
+ this.setPage(this.currentPageNumber + 1);
}
}