From 2dc1af8028119e2662df67ca94c9f1876fe35bfe Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Thu, 29 Jan 2015 13:37:49 +0100 Subject: [PATCH 1/8] Break dependencies between PresentationMode and other code, and add PresentationMode related utility methods to PDFViewer This patch: - Adds a couple of utility methods to `PDFViewer` to enable checking `presentationModeState` without cumbersome comparisons. - Disentangles `PresentationMode` from `PDFHistory` and `HandTool`, by adding event listeners for `presentationmodechanged` to both of them. - Adds a utility function to `PDFViewerApplication` for requesting PresentationMode. - Prevents initialization of `PresentationMode` if the browser does not support the fullscreen API. --- web/hand_tool.js | 11 ++++++ web/pdf_history.js | 8 +++- web/pdf_viewer.js | 34 ++++++++--------- web/presentation_mode.js | 8 ++-- web/secondary_toolbar.js | 3 +- web/viewer.js | 82 +++++++++++++++++++++------------------- 6 files changed, 83 insertions(+), 63 deletions(-) diff --git a/web/hand_tool.js b/web/hand_tool.js index b929a1ed1..70d04f07c 100644 --- a/web/hand_tool.js +++ b/web/hand_tool.js @@ -51,6 +51,17 @@ var HandTool = { } }.bind(this), function rejected(reason) {}); }.bind(this)); + + window.addEventListener('presentationmodechanged', function (evt) { + if (evt.detail.switchInProgress) { + return; + } + if (evt.detail.active) { + this.enterPresentationMode(); + } else { + this.exitPresentationMode(); + } + }.bind(this)); } }, diff --git a/web/pdf_history.js b/web/pdf_history.js index e15e03a66..4000803ba 100644 --- a/web/pdf_history.js +++ b/web/pdf_history.js @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals PDFJS, PresentationMode */ 'use strict'; @@ -31,6 +30,7 @@ var PDFHistory = { this.reInitialized = false; this.allowHashChange = true; this.historyUnlocked = true; + this.isViewerInPresentationMode = false; this.previousHash = window.location.hash.substring(1); this.currentBookmark = ''; @@ -122,6 +122,10 @@ var PDFHistory = { // the 'DOMContentLoaded' event is not fired on 'pageshow'. window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false); }, false); + + window.addEventListener('presentationmodechanged', function(e) { + self.isViewerInPresentationMode = !!e.detail.active; + }); }, _isStateObjectDefined: function pdfHistory_isStateObjectDefined(state) { @@ -286,7 +290,7 @@ var PDFHistory = { return null; } var params = { hash: this.currentBookmark, page: this.currentPage }; - if (PresentationMode.active) { + if (this.isViewerInPresentationMode) { params.hash = null; } return params; diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index 9cee8bf4c..16315bbed 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -374,11 +374,8 @@ var PDFViewer = (function pdfViewer() { if (!noScroll) { var page = this._currentPageNumber, dest; - var inPresentationMode = - this.presentationModeState === PresentationModeState.CHANGING || - this.presentationModeState === PresentationModeState.FULLSCREEN; - if (this.location && !inPresentationMode && - !IGNORE_CURRENT_POSITION_ON_ZOOM) { + if (this.location && !IGNORE_CURRENT_POSITION_ON_ZOOM && + !(this.isInPresentationMode || this.isChangingPresentationMode)) { page = this.location.pageNumber; dest = [null, { name: 'XYZ' }, this.location.left, this.location.top, null]; @@ -402,11 +399,9 @@ var PDFViewer = (function pdfViewer() { if (!currentPage) { return; } - var inPresentationMode = - this.presentationModeState === PresentationModeState.FULLSCREEN; - var hPadding = (inPresentationMode || this.removePageBorders) ? + var hPadding = (this.isInPresentationMode || this.removePageBorders) ? 0 : SCROLLBAR_PADDING; - var vPadding = (inPresentationMode || this.removePageBorders) ? + var vPadding = (this.isInPresentationMode || this.removePageBorders) ? 0 : VERTICAL_PADDING; var pageWidthScale = (this.container.clientWidth - hPadding) / currentPage.width * currentPage.scale; @@ -452,8 +447,7 @@ var PDFViewer = (function pdfViewer() { dest) { var pageView = this.pages[pageNumber - 1]; - if (this.presentationModeState === - PresentationModeState.FULLSCREEN) { + if (this.isInPresentationMode) { if (this.linkService.page !== pageView.id) { // Avoid breaking getVisiblePages in presentation mode. this.linkService.page = pageView.id; @@ -607,7 +601,7 @@ var PDFViewer = (function pdfViewer() { currentId = visiblePages[0].id; } - if (this.presentationModeState !== PresentationModeState.FULLSCREEN) { + if (!this.isInPresentationMode) { this.currentPageNumber = currentId; } @@ -632,13 +626,21 @@ var PDFViewer = (function pdfViewer() { this.container.blur(); }, + get isInPresentationMode() { + return this.presentationModeState === PresentationModeState.FULLSCREEN; + }, + + get isChangingPresentationMode() { + return this.PresentationModeState === PresentationModeState.CHANGING; + }, + get isHorizontalScrollbarEnabled() { - return (this.presentationModeState === PresentationModeState.FULLSCREEN ? + return (this.isInPresentationMode ? false : (this.container.scrollWidth > this.container.clientWidth)); }, _getVisiblePages: function () { - if (this.presentationModeState !== PresentationModeState.FULLSCREEN) { + if (!this.isInPresentationMode) { return getVisibleElements(this.container, this.pages, true); } else { // The algorithm in getVisibleElements doesn't work in all browsers and @@ -709,13 +711,11 @@ var PDFViewer = (function pdfViewer() { * @returns {TextLayerBuilder} */ createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) { - var isViewerInPresentationMode = - this.presentationModeState === PresentationModeState.FULLSCREEN; return new TextLayerBuilder({ textLayerDiv: textLayerDiv, pageIndex: pageIndex, viewport: viewport, - findController: isViewerInPresentationMode ? null : this.findController + findController: this.isInPresentationMode ? null : this.findController }); }, diff --git a/web/presentation_mode.js b/web/presentation_mode.js index f94ef7b08..6c4fe4643 100644 --- a/web/presentation_mode.js +++ b/web/presentation_mode.js @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals scrollIntoView, HandTool, PDFViewerApplication */ +/* globals scrollIntoView, PDFViewerApplication */ 'use strict'; @@ -23,6 +23,7 @@ var SELECTOR = 'presentationControls'; var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1000; // in ms var PresentationMode = { + initialized: false, active: false, args: null, contextMenuOpen: false, @@ -31,6 +32,7 @@ var PresentationMode = { //#endif initialize: function presentationModeInitialize(options) { + this.initialized = true; this.container = options.container; this.secondaryToolbar = options.secondaryToolbar; @@ -93,7 +95,7 @@ var PresentationMode = { }, request: function presentationModeRequest() { - if (!PDFViewerApplication.supportsFullscreen || this.isFullscreen || + if (!this.initialized || this.isFullscreen || !this.viewer.hasChildNodes()) { return false; } @@ -147,7 +149,6 @@ var PresentationMode = { window.addEventListener('contextmenu', this.contextMenu, false); this.showControls(); - HandTool.enterPresentationMode(); this.contextMenuOpen = false; this.container.setAttribute('contextmenu', 'viewerContextMenu'); @@ -178,7 +179,6 @@ var PresentationMode = { this.hideControls(); PDFViewerApplication.clearMouseScrollState(); - HandTool.exitPresentationMode(); this.container.removeAttribute('contextmenu'); this.contextMenuOpen = false; diff --git a/web/secondary_toolbar.js b/web/secondary_toolbar.js index 7c94b3185..0b3f3d596 100644 --- a/web/secondary_toolbar.js +++ b/web/secondary_toolbar.js @@ -25,7 +25,6 @@ var SecondaryToolbar = { initialize: function secondaryToolbarInitialize(options) { this.toolbar = options.toolbar; - this.presentationMode = options.presentationMode; this.documentProperties = options.documentProperties; this.buttonContainer = this.toolbar.firstElementChild; @@ -72,7 +71,7 @@ var SecondaryToolbar = { // Event handling functions. presentationModeClick: function secondaryToolbarPresentationModeClick(evt) { - this.presentationMode.request(); + PDFViewerApplication.requestPresentationMode(); this.close(); }, diff --git a/web/viewer.js b/web/viewer.js index 62c9f9182..b80d9781f 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -174,7 +174,6 @@ var PDFViewerApplication = { SecondaryToolbar.initialize({ toolbar: document.getElementById('secondaryToolbar'), - presentationMode: PresentationMode, toggleButton: document.getElementById('secondaryToolbarToggle'), presentationModeButton: document.getElementById('secondaryPresentationMode'), @@ -190,14 +189,16 @@ var PDFViewerApplication = { documentPropertiesButton: document.getElementById('documentProperties') }); - PresentationMode.initialize({ - container: container, - secondaryToolbar: SecondaryToolbar, - firstPage: document.getElementById('contextFirstPage'), - lastPage: document.getElementById('contextLastPage'), - pageRotateCw: document.getElementById('contextPageRotateCw'), - pageRotateCcw: document.getElementById('contextPageRotateCcw') - }); + if (this.supportsFullscreen) { + PresentationMode.initialize({ + container: container, + secondaryToolbar: SecondaryToolbar, + firstPage: document.getElementById('contextFirstPage'), + lastPage: document.getElementById('contextLastPage'), + pageRotateCw: document.getElementById('contextPageRotateCw'), + pageRotateCcw: document.getElementById('contextPageRotateCcw') + }); + } PasswordPrompt.initialize({ overlayName: 'passwordOverlay', @@ -1334,6 +1335,13 @@ var PDFViewerApplication = { this.pdfViewer.scrollPageIntoView(pageNumber); }, + requestPresentationMode: function pdfViewRequestPresentationMode() { + if (!this.supportsFullscreen) { + return; + } + PresentationMode.request(); + }, + /** * This function flips the page in presentation mode if the user scrolls up * or down with large enough motion and prevents page flipping too often. @@ -1976,7 +1984,7 @@ function handleMouseWheel(evt) { evt.wheelDelta / MOUSE_WHEEL_DELTA_FACTOR; var direction = (ticks < 0) ? 'zoomOut' : 'zoomIn'; - if (PresentationMode.active) { + if (PDFViewerApplication.pdfViewer.isInPresentationMode) { evt.preventDefault(); PDFViewerApplication.mouseScroll(ticks * MOUSE_WHEEL_DELTA_FACTOR); } else if (evt.ctrlKey || evt.metaKey) { @@ -1990,9 +1998,9 @@ window.addEventListener('DOMMouseScroll', handleMouseWheel); window.addEventListener('mousewheel', handleMouseWheel); window.addEventListener('click', function click(evt) { - if (!PresentationMode.active) { + if (!PDFViewerApplication.pdfViewer.isInPresentationMode) { if (SecondaryToolbar.opened && - PDFViewerApplication.pdfViewer.containsElement(evt.target)) { + PDFViewerApplication.pdfViewer.containsElement(evt.target)) { SecondaryToolbar.close(); } } else if (evt.button === 0) { @@ -2013,15 +2021,13 @@ window.addEventListener('keydown', function keydown(evt) { (evt.shiftKey ? 4 : 0) | (evt.metaKey ? 8 : 0); + var pdfViewer = PDFViewerApplication.pdfViewer; + var isViewerInPresentationMode = pdfViewer && pdfViewer.isInPresentationMode; + // First, handle the key bindings that are independent whether an input // control is selected or not. if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) { // either CTRL or META key with optional SHIFT. - var pdfViewer = PDFViewerApplication.pdfViewer; - var inPresentationMode = pdfViewer && - (pdfViewer.presentationModeState === PresentationModeState.CHANGING || - pdfViewer.presentationModeState === PresentationModeState.FULLSCREEN); - switch (evt.keyCode) { case 70: // f if (!PDFViewerApplication.supportsIntegratedFind) { @@ -2040,7 +2046,7 @@ window.addEventListener('keydown', function keydown(evt) { case 107: // FF '+' and '=' case 187: // Chrome '+' case 171: // FF with German keyboard - if (!inPresentationMode) { + if (!isViewerInPresentationMode) { PDFViewerApplication.zoomIn(); } handled = true; @@ -2048,14 +2054,14 @@ window.addEventListener('keydown', function keydown(evt) { case 173: // FF/Mac '-' case 109: // FF '-' case 189: // Chrome '-' - if (!inPresentationMode) { + if (!isViewerInPresentationMode) { PDFViewerApplication.zoomOut(); } handled = true; break; case 48: // '0' case 96: // '0' on Numpad of Swedish keyboard - if (!inPresentationMode) { + if (!isViewerInPresentationMode) { // keeping it unhandled (to restore page zoom to 100%) setTimeout(function () { // ... and resetting the scale after browser adjusts its scale @@ -2083,7 +2089,7 @@ window.addEventListener('keydown', function keydown(evt) { if (cmd === 3 || cmd === 10) { switch (evt.keyCode) { case 80: // p - SecondaryToolbar.presentationModeClick(); + PDFViewerApplication.requestPresentationMode(); handled = true; break; case 71: // g @@ -2117,15 +2123,15 @@ window.addEventListener('keydown', function keydown(evt) { case 38: // up arrow case 33: // pg up case 8: // backspace - if (!PresentationMode.active && - PDFViewerApplication.currentScaleValue !== 'page-fit') { + if (!isViewerInPresentationMode && + PDFViewerApplication.currentScaleValue !== 'page-fit') { break; } /* in presentation mode */ /* falls through */ case 37: // left arrow // horizontal scrolling using arrow keys - if (PDFViewerApplication.pdfViewer.isHorizontalScrollbarEnabled) { + if (pdfViewer.isHorizontalScrollbarEnabled) { break; } /* falls through */ @@ -2148,14 +2154,14 @@ window.addEventListener('keydown', function keydown(evt) { case 40: // down arrow case 34: // pg down case 32: // spacebar - if (!PresentationMode.active && + if (!isViewerInPresentationMode && PDFViewerApplication.currentScaleValue !== 'page-fit') { break; } /* falls through */ case 39: // right arrow // horizontal scrolling using arrow keys - if (PDFViewerApplication.pdfViewer.isHorizontalScrollbarEnabled) { + if (pdfViewer.isHorizontalScrollbarEnabled) { break; } /* falls through */ @@ -2166,13 +2172,13 @@ window.addEventListener('keydown', function keydown(evt) { break; case 36: // home - if (PresentationMode.active || PDFViewerApplication.page > 1) { + if (isViewerInPresentationMode || PDFViewerApplication.page > 1) { PDFViewerApplication.page = 1; handled = true; } break; case 35: // end - if (PresentationMode.active || (PDFViewerApplication.pdfDocument && + if (isViewerInPresentationMode || (PDFViewerApplication.pdfDocument && PDFViewerApplication.page < PDFViewerApplication.pagesCount)) { PDFViewerApplication.page = PDFViewerApplication.pagesCount; handled = true; @@ -2180,7 +2186,7 @@ window.addEventListener('keydown', function keydown(evt) { break; case 72: // 'h' - if (!PresentationMode.active) { + if (!isViewerInPresentationMode) { HandTool.toggle(); } break; @@ -2193,7 +2199,7 @@ window.addEventListener('keydown', function keydown(evt) { if (cmd === 4) { // shift-key switch (evt.keyCode) { case 32: // spacebar - if (!PresentationMode.active && + if (!isViewerInPresentationMode && PDFViewerApplication.currentScaleValue !== 'page-fit') { break; } @@ -2207,25 +2213,25 @@ window.addEventListener('keydown', function keydown(evt) { } } - if (!handled && !PresentationMode.active) { + if (!handled && !isViewerInPresentationMode) { // 33=Page Up 34=Page Down 35=End 36=Home // 37=Left 38=Up 39=Right 40=Down if (evt.keyCode >= 33 && evt.keyCode <= 40 && - !PDFViewerApplication.pdfViewer.containsElement(curElement)) { + !pdfViewer.containsElement(curElement)) { // The page container is not focused, but a page navigation key has been // pressed. Change the focus to the viewer container to make sure that // navigation by keyboard works as expected. - PDFViewerApplication.pdfViewer.focus(); + pdfViewer.focus(); } // 32=Spacebar if (evt.keyCode === 32 && curElementTagName !== 'BUTTON') { //#if (FIREFOX || MOZCENTRAL) // // Workaround for issue in Firefox, that prevents scroll keys from // // working when elements with 'tabindex' are focused. (#3498) -// PDFViewerApplication.pdfViewer.blur(); +// pdfViewer.blur(); //#else - if (!PDFViewerApplication.pdfViewer.containsElement(curElement)) { - PDFViewerApplication.pdfViewer.focus(); + if (!pdfViewer.containsElement(curElement)) { + pdfViewer.focus(); } //#endif } @@ -2234,13 +2240,13 @@ window.addEventListener('keydown', function keydown(evt) { if (cmd === 2) { // alt-key switch (evt.keyCode) { case 37: // left arrow - if (PresentationMode.active) { + if (isViewerInPresentationMode) { PDFHistory.back(); handled = true; } break; case 39: // right arrow - if (PresentationMode.active) { + if (isViewerInPresentationMode) { PDFHistory.forward(); handled = true; } From 95b2ec124bb8bdcfabfc69e377b7d28e9dcab569 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sat, 31 Jan 2015 16:46:23 +0100 Subject: [PATCH 2/8] Move the PresentationMode-specific scrollWheel code from PDFViewerApplication --- web/presentation_mode.js | 77 +++++++++++++++++++++++++++++++++++++++- web/viewer.js | 75 ++++---------------------------------- 2 files changed, 82 insertions(+), 70 deletions(-) diff --git a/web/presentation_mode.js b/web/presentation_mode.js index 6c4fe4643..6fc223ba6 100644 --- a/web/presentation_mode.js +++ b/web/presentation_mode.js @@ -30,6 +30,8 @@ var PresentationMode = { //#if (GENERIC || CHROME) prevCoords: { x: null, y: null }, //#endif + mouseScrollTimeStamp: 0, + mouseScrollDelta: 0, initialize: function presentationModeInitialize(options) { this.initialized = true; @@ -146,6 +148,7 @@ var PresentationMode = { window.addEventListener('mousemove', this.mouseMove, false); window.addEventListener('mousedown', this.mouseDown, false); + window.addEventListener('keydown', this.keyDown, false); window.addEventListener('contextmenu', this.contextMenu, false); this.showControls(); @@ -175,10 +178,11 @@ var PresentationMode = { window.removeEventListener('mousemove', this.mouseMove, false); window.removeEventListener('mousedown', this.mouseDown, false); + window.removeEventListener('keydown', this.keyDown, false); window.removeEventListener('contextmenu', this.contextMenu, false); this.hideControls(); - PDFViewerApplication.clearMouseScrollState(); + this.clearMouseScrollState(); this.container.removeAttribute('contextmenu'); this.contextMenuOpen = false; @@ -246,8 +250,79 @@ var PresentationMode = { } }, + keyDown: function presentationModeKeyDown(evt) { + PresentationMode.clearMouseScrollState(); + }, + contextMenu: function presentationModeContextMenu(evt) { PresentationMode.contextMenuOpen = true; + }, + + /** + * This function flips the page in presentation mode if the user scrolls up + * or down with large enough motion and prevents page flipping too often. + * @param {number} mouseScrollDelta The delta value from the mouse event. + */ + mouseScroll: function presentationModeMouseScroll(mouseScrollDelta) { + if (!this.initialized) { + return; + } + var MOUSE_SCROLL_COOLDOWN_TIME = 50; + + var currentTime = (new Date()).getTime(); + var storedTime = this.mouseScrollTimeStamp; + + // In case one page has already been flipped there is a cooldown time + // which has to expire before next page can be scrolled on to. + if (currentTime > storedTime && + currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { + return; + } + + // In case the user decides to scroll to the opposite direction than before + // clear the accumulated delta. + if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) || + (this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) { + this.clearMouseScrollState(); + } + + this.mouseScrollDelta += mouseScrollDelta; + + var PAGE_FLIP_THRESHOLD = 120; + if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) { + + var PageFlipDirection = { + UP: -1, + DOWN: 1 + }; + + // In presentation mode scroll one page at a time. + var pageFlipDirection = (this.mouseScrollDelta > 0) ? + PageFlipDirection.UP : + PageFlipDirection.DOWN; + this.clearMouseScrollState(); + var currentPage = PDFViewerApplication.page; + + // In case we are already on the first or the last page there is no need + // to do anything. + if ((currentPage === 1 && pageFlipDirection === PageFlipDirection.UP) || + (currentPage === PDFViewerApplication.pagesCount && + pageFlipDirection === PageFlipDirection.DOWN)) { + return; + } + + PDFViewerApplication.page += pageFlipDirection; + this.mouseScrollTimeStamp = currentTime; + } + }, + + /** + * This function clears the member attributes used with mouse scrolling in + * presentation mode. + */ + clearMouseScrollState: function presentationModeClearMouseScrollState() { + this.mouseScrollTimeStamp = 0; + this.mouseScrollDelta = 0; } }; diff --git a/web/viewer.js b/web/viewer.js index b80d9781f..994835dce 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -111,8 +111,6 @@ var PDFViewerApplication = { updateScaleControls: true, isInitialViewSet: false, animationStartedPromise: null, - mouseScrollTimeStamp: 0, - mouseScrollDelta: 0, preferenceSidebarViewOnLoad: SidebarView.NONE, preferencePdfBugEnabled: false, preferenceShowPreviousViewOnLoad: true, @@ -1342,72 +1340,11 @@ var PDFViewerApplication = { PresentationMode.request(); }, - /** - * This function flips the page in presentation mode if the user scrolls up - * or down with large enough motion and prevents page flipping too often. - * - * @this {PDFView} - * @param {number} mouseScrollDelta The delta value from the mouse event. - */ - mouseScroll: function pdfViewMouseScroll(mouseScrollDelta) { - var MOUSE_SCROLL_COOLDOWN_TIME = 50; - - var currentTime = (new Date()).getTime(); - var storedTime = this.mouseScrollTimeStamp; - - // In case one page has already been flipped there is a cooldown time - // which has to expire before next page can be scrolled on to. - if (currentTime > storedTime && - currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { + scrollPresentationMode: function pdfViewScrollPresentationMode(delta) { + if (!this.supportsFullscreen) { return; } - - // In case the user decides to scroll to the opposite direction than before - // clear the accumulated delta. - if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) || - (this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) { - this.clearMouseScrollState(); - } - - this.mouseScrollDelta += mouseScrollDelta; - - var PAGE_FLIP_THRESHOLD = 120; - if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) { - - var PageFlipDirection = { - UP: -1, - DOWN: 1 - }; - - // In presentation mode scroll one page at a time. - var pageFlipDirection = (this.mouseScrollDelta > 0) ? - PageFlipDirection.UP : - PageFlipDirection.DOWN; - this.clearMouseScrollState(); - var currentPage = this.page; - - // In case we are already on the first or the last page there is no need - // to do anything. - if ((currentPage === 1 && pageFlipDirection === PageFlipDirection.UP) || - (currentPage === this.pagesCount && - pageFlipDirection === PageFlipDirection.DOWN)) { - return; - } - - this.page += pageFlipDirection; - this.mouseScrollTimeStamp = currentTime; - } - }, - - /** - * This function clears the member attributes used with mouse scrolling in - * presentation mode. - * - * @this {PDFView} - */ - clearMouseScrollState: function pdfViewClearMouseScrollState() { - this.mouseScrollTimeStamp = 0; - this.mouseScrollDelta = 0; + PresentationMode.mouseScroll(delta); } }; //#if GENERIC @@ -1986,9 +1923,10 @@ function handleMouseWheel(evt) { if (PDFViewerApplication.pdfViewer.isInPresentationMode) { evt.preventDefault(); - PDFViewerApplication.mouseScroll(ticks * MOUSE_WHEEL_DELTA_FACTOR); + PDFViewerApplication.scrollPresentationMode(ticks * + MOUSE_WHEEL_DELTA_FACTOR); } else if (evt.ctrlKey || evt.metaKey) { - // Only zoom the pages, not the entire viewer + // Only zoom the pages, not the entire viewer. evt.preventDefault(); PDFViewerApplication[direction](Math.abs(ticks)); } @@ -2256,7 +2194,6 @@ window.addEventListener('keydown', function keydown(evt) { if (handled) { evt.preventDefault(); - PDFViewerApplication.clearMouseScrollState(); } }); From e7fd5b4d4d2bcdfdbffed588fdfa344239020d44 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 1 Feb 2015 11:31:18 +0100 Subject: [PATCH 3/8] Refactor the options passed to |PresentationMode.initialize| and clean-up some code in viewer.js and presentation_mode.js This patch: - Passes in a reference to the current PDFThumbnailViewer, which is used to ensure that the current thumbnail becomes visible when exiting PresentationMode. - Changes the way that the event listeners for the contextmenu items are defined, to avoid passing in a reference to the SecondaryToolbar. - Ensures that |supportsFullscreen| always returns a boolean. Currently `supportsFullscreen` will, when the browser supports the fullscreen API, return e.g. `function mozRequestFullScreen()` instead of `true`. - Simplifies the |click| handler code when PresentationMode is active. This code has been obsolete ever since PR 2919 landed. - Removes hack used to workaround a bug in WebKit browsers, which caused |mousemove| events to be fired when the cursor changed. This was fixed close to a year ago, see http://code.google.com/p/chromium/issues/detail?id=103041. --- web/presentation_mode.js | 60 ++++++++++++---------------------------- web/viewer.js | 35 +++++++++++------------ 2 files changed, 35 insertions(+), 60 deletions(-) diff --git a/web/presentation_mode.js b/web/presentation_mode.js index 6fc223ba6..4ef1a0e7b 100644 --- a/web/presentation_mode.js +++ b/web/presentation_mode.js @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals scrollIntoView, PDFViewerApplication */ +/* globals PDFViewerApplication */ 'use strict'; @@ -27,41 +27,26 @@ var PresentationMode = { active: false, args: null, contextMenuOpen: false, -//#if (GENERIC || CHROME) - prevCoords: { x: null, y: null }, -//#endif mouseScrollTimeStamp: 0, mouseScrollDelta: 0, initialize: function presentationModeInitialize(options) { this.initialized = true; this.container = options.container; - this.secondaryToolbar = options.secondaryToolbar; + this.pdfThumbnailViewer = options.pdfThumbnailViewer || null; + var contextMenuItems = options.contextMenuItems || null; this.viewer = this.container.firstElementChild; - this.firstPage = options.firstPage; - this.lastPage = options.lastPage; - this.pageRotateCw = options.pageRotateCw; - this.pageRotateCcw = options.pageRotateCcw; - - this.firstPage.addEventListener('click', function() { - this.contextMenuOpen = false; - this.secondaryToolbar.firstPageClick(); - }.bind(this)); - this.lastPage.addEventListener('click', function() { - this.contextMenuOpen = false; - this.secondaryToolbar.lastPageClick(); - }.bind(this)); - - this.pageRotateCw.addEventListener('click', function() { - this.contextMenuOpen = false; - this.secondaryToolbar.pageRotateCwClick(); - }.bind(this)); - this.pageRotateCcw.addEventListener('click', function() { - this.contextMenuOpen = false; - this.secondaryToolbar.pageRotateCcwClick(); - }.bind(this)); + if (contextMenuItems) { + for (var i = 0, ii = contextMenuItems.length; i < ii; i++) { + var item = contextMenuItems[i]; + item.element.addEventListener('click', function (handler) { + this.contextMenuOpen = false; + handler(); + }.bind(this, item.handler)); + } + } }, get isFullscreen() { @@ -186,9 +171,11 @@ var PresentationMode = { this.container.removeAttribute('contextmenu'); this.contextMenuOpen = false; - // Ensure that the thumbnail of the current page is visible - // when exiting presentation mode. - scrollIntoView(document.getElementById('thumbnailContainer' + page)); + if (this.pdfThumbnailViewer) { + // Ensure that the thumbnail of the current page is visible + // when exiting presentation mode. + this.pdfThumbnailViewer.ensureThumbnailVisible(page); + } }, showControls: function presentationModeShowControls() { @@ -213,19 +200,6 @@ var PresentationMode = { }, mouseMove: function presentationModeMouseMove(evt) { -//#if (GENERIC || CHROME) - // Workaround for a bug in WebKit browsers that causes the 'mousemove' event - // to be fired when the cursor is changed. For details, see: - // http://code.google.com/p/chromium/issues/detail?id=103041. - - var currCoords = { x: evt.clientX, y: evt.clientY }; - var prevCoords = PresentationMode.prevCoords; - PresentationMode.prevCoords = currCoords; - - if (currCoords.x === prevCoords.x && currCoords.y === prevCoords.y) { - return; - } -//#endif PresentationMode.showControls(); }, diff --git a/web/viewer.js b/web/viewer.js index 994835dce..819be1a84 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -15,7 +15,7 @@ * limitations under the License. */ /* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, ProgressBar, - DownloadManager, getFileName, scrollIntoView, getPDFFileNameFromURL, + DownloadManager, getFileName, getPDFFileNameFromURL, PDFHistory, Preferences, SidebarView, ViewHistory, Stats, PDFThumbnailViewer, URL, noContextMenuHandler, SecondaryToolbar, PasswordPrompt, PresentationMode, HandTool, Promise, @@ -188,13 +188,20 @@ var PDFViewerApplication = { }); if (this.supportsFullscreen) { + var toolbar = SecondaryToolbar; PresentationMode.initialize({ container: container, - secondaryToolbar: SecondaryToolbar, - firstPage: document.getElementById('contextFirstPage'), - lastPage: document.getElementById('contextLastPage'), - pageRotateCw: document.getElementById('contextPageRotateCw'), - pageRotateCcw: document.getElementById('contextPageRotateCcw') + pdfThumbnailViewer: this.pdfThumbnailViewer, + contextMenuItems: [ + { element: document.getElementById('contextFirstPage'), + handler: toolbar.firstPageClick.bind(toolbar) }, + { element: document.getElementById('contextLastPage'), + handler: toolbar.lastPageClick.bind(toolbar) }, + { element: document.getElementById('contextPageRotateCw'), + handler: toolbar.pageRotateCwClick.bind(toolbar) }, + { element: document.getElementById('contextPageRotateCcw'), + handler: toolbar.pageRotateCcwClick.bind(toolbar) } + ] }); } @@ -317,8 +324,8 @@ var PDFViewerApplication = { get supportsFullscreen() { var doc = document.documentElement; - var support = doc.requestFullscreen || doc.mozRequestFullScreen || - doc.webkitRequestFullScreen || doc.msRequestFullscreen; + var support = !!(doc.requestFullscreen || doc.mozRequestFullScreen || + doc.webkitRequestFullScreen || doc.msRequestFullscreen); if (document.fullscreenEnabled === false || document.mozFullScreenEnabled === false || @@ -1936,15 +1943,9 @@ window.addEventListener('DOMMouseScroll', handleMouseWheel); window.addEventListener('mousewheel', handleMouseWheel); window.addEventListener('click', function click(evt) { - if (!PDFViewerApplication.pdfViewer.isInPresentationMode) { - if (SecondaryToolbar.opened && - PDFViewerApplication.pdfViewer.containsElement(evt.target)) { - SecondaryToolbar.close(); - } - } else if (evt.button === 0) { - // Necessary since preventDefault() in 'mousedown' won't stop - // the event propagation in all circumstances in presentation mode. - evt.preventDefault(); + if (SecondaryToolbar.opened && + PDFViewerApplication.pdfViewer.containsElement(evt.target)) { + SecondaryToolbar.close(); } }, false); From f15d5c8cfeddfe62508ed343642154cd0bef1be1 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 28 Jan 2015 13:58:40 +0100 Subject: [PATCH 4/8] Rename the presentation_mode.js file and adjust the function names Also moves the |fullscreenchange| event listeners into PDFPresentationMode. --- ...ation_mode.js => pdf_presentation_mode.js} | 105 +++++++++--------- web/viewer.html | 2 +- web/viewer.js | 10 +- 3 files changed, 59 insertions(+), 58 deletions(-) rename web/{presentation_mode.js => pdf_presentation_mode.js} (76%) diff --git a/web/presentation_mode.js b/web/pdf_presentation_mode.js similarity index 76% rename from web/presentation_mode.js rename to web/pdf_presentation_mode.js index 4ef1a0e7b..098068433 100644 --- a/web/presentation_mode.js +++ b/web/pdf_presentation_mode.js @@ -22,7 +22,7 @@ var DELAY_BEFORE_HIDING_CONTROLS = 3000; // in ms var SELECTOR = 'presentationControls'; var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1000; // in ms -var PresentationMode = { +var PDFPresentationMode = { initialized: false, active: false, args: null, @@ -30,7 +30,7 @@ var PresentationMode = { mouseScrollTimeStamp: 0, mouseScrollDelta: 0, - initialize: function presentationModeInitialize(options) { + initialize: function pdfPresentationModeInitialize(options) { this.initialized = true; this.container = options.container; this.pdfThumbnailViewer = options.pdfThumbnailViewer || null; @@ -38,6 +38,13 @@ var PresentationMode = { this.viewer = this.container.firstElementChild; + window.addEventListener('fullscreenchange', this._fullscreenChange); + window.addEventListener('mozfullscreenchange', this._fullscreenChange); +//#if !(FIREFOX || MOZCENTRAL) + window.addEventListener('webkitfullscreenchange', this._fullscreenChange); + window.addEventListener('MSFullscreenChange', this._fullscreenChange); +//#endif + if (contextMenuItems) { for (var i = 0, ii = contextMenuItems.length; i < ii; i++) { var item = contextMenuItems[i]; @@ -56,6 +63,15 @@ var PresentationMode = { document.msFullscreenElement); }, + _fullscreenChange: function pdfPresentationModeFullscreenChange() { + var self = PDFPresentationMode; + if (self.isFullscreen) { + self._enter(); + } else { + self._exit(); + } + }, + /** * Initialize a timeout that is used to specify switchInProgress when the * browser transitions to fullscreen mode. Since resize events are triggered @@ -64,7 +80,7 @@ var PresentationMode = { * out of view when Presentation Mode is enabled. * Note: This is only an issue at certain zoom levels, e.g. 'page-width'. */ - _setSwitchInProgress: function presentationMode_setSwitchInProgress() { + _setSwitchInProgress: function pdfPresentationMode_setSwitchInProgress() { if (this.switchInProgress) { clearTimeout(this.switchInProgress); } @@ -74,14 +90,14 @@ var PresentationMode = { }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS); }, - _resetSwitchInProgress: function presentationMode_resetSwitchInProgress() { + _resetSwitchInProgress: function pdfPresentationMode_resetSwitchInProgress() { if (this.switchInProgress) { clearTimeout(this.switchInProgress); delete this.switchInProgress; } }, - request: function presentationModeRequest() { + request: function pdfPresentationModeRequest() { if (!this.initialized || this.isFullscreen || !this.viewer.hasChildNodes()) { return false; @@ -109,16 +125,17 @@ var PresentationMode = { return true; }, - _notifyStateChange: function presentationModeNotifyStateChange() { + _notifyStateChange: function pdfPresentationModeNotifyStateChange() { + var self = PDFPresentationMode; var event = document.createEvent('CustomEvent'); event.initCustomEvent('presentationmodechanged', true, true, { - active: PresentationMode.active, - switchInProgress: !!PresentationMode.switchInProgress + active: self.active, + switchInProgress: !!self.switchInProgress }); window.dispatchEvent(event); }, - enter: function presentationModeEnter() { + _enter: function pdfPresentationModeEnter() { this.active = true; this._resetSwitchInProgress(); this._notifyStateChange(); @@ -131,12 +148,12 @@ var PresentationMode = { PDFViewerApplication.setScale('page-fit', true); }.bind(this), 0); - window.addEventListener('mousemove', this.mouseMove, false); - window.addEventListener('mousedown', this.mouseDown, false); - window.addEventListener('keydown', this.keyDown, false); - window.addEventListener('contextmenu', this.contextMenu, false); + window.addEventListener('mousemove', this._mouseMove, false); + window.addEventListener('mousedown', this._mouseDown, false); + window.addEventListener('keydown', this._keyDown, false); + window.addEventListener('contextmenu', this._contextMenu, false); - this.showControls(); + this._showControls(); this.contextMenuOpen = false; this.container.setAttribute('contextmenu', 'viewerContextMenu'); @@ -146,7 +163,7 @@ var PresentationMode = { window.getSelection().removeAllRanges(); }, - exit: function presentationModeExit() { + _exit: function pdfPresentationModeExit() { var page = PDFViewerApplication.page; // Ensure that the correct page is scrolled into view when exiting @@ -161,13 +178,13 @@ var PresentationMode = { this.args = null; }.bind(this), 0); - window.removeEventListener('mousemove', this.mouseMove, false); - window.removeEventListener('mousedown', this.mouseDown, false); - window.removeEventListener('keydown', this.keyDown, false); - window.removeEventListener('contextmenu', this.contextMenu, false); + window.removeEventListener('mousemove', this._mouseMove, false); + window.removeEventListener('mousedown', this._mouseDown, false); + window.removeEventListener('keydown', this._keyDown, false); + window.removeEventListener('contextmenu', this._contextMenu, false); - this.hideControls(); - this.clearMouseScrollState(); + this._hideControls(); + this._clearMouseScrollState(); this.container.removeAttribute('contextmenu'); this.contextMenuOpen = false; @@ -178,19 +195,19 @@ var PresentationMode = { } }, - showControls: function presentationModeShowControls() { + _showControls: function pdfPresentationModeShowControls() { if (this.controlsTimeout) { clearTimeout(this.controlsTimeout); } else { this.container.classList.add(SELECTOR); } - this.controlsTimeout = setTimeout(function hideControlsTimeout() { + this.controlsTimeout = setTimeout(function showControlsTimeout() { this.container.classList.remove(SELECTOR); delete this.controlsTimeout; }.bind(this), DELAY_BEFORE_HIDING_CONTROLS); }, - hideControls: function presentationModeHideControls() { + _hideControls: function pdfPresentationModeHideControls() { if (!this.controlsTimeout) { return; } @@ -199,12 +216,12 @@ var PresentationMode = { delete this.controlsTimeout; }, - mouseMove: function presentationModeMouseMove(evt) { - PresentationMode.showControls(); + _mouseMove: function pdfPresentationModeMouseMove(evt) { + PDFPresentationMode._showControls(); }, - mouseDown: function presentationModeMouseDown(evt) { - var self = PresentationMode; + _mouseDown: function pdfPresentationModeMouseDown(evt) { + var self = PDFPresentationMode; if (self.contextMenuOpen) { self.contextMenuOpen = false; evt.preventDefault(); @@ -224,12 +241,12 @@ var PresentationMode = { } }, - keyDown: function presentationModeKeyDown(evt) { - PresentationMode.clearMouseScrollState(); + _keyDown: function pdfPresentationModeKeyDown(evt) { + PDFPresentationMode._clearMouseScrollState(); }, - contextMenu: function presentationModeContextMenu(evt) { - PresentationMode.contextMenuOpen = true; + _contextMenu: function pdfPresentationModeContextMenu(evt) { + PDFPresentationMode.contextMenuOpen = true; }, /** @@ -237,7 +254,7 @@ var PresentationMode = { * or down with large enough motion and prevents page flipping too often. * @param {number} mouseScrollDelta The delta value from the mouse event. */ - mouseScroll: function presentationModeMouseScroll(mouseScrollDelta) { + mouseScroll: function pdfPresentationModeMouseScroll(mouseScrollDelta) { if (!this.initialized) { return; } @@ -257,7 +274,7 @@ var PresentationMode = { // clear the accumulated delta. if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) || (this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) { - this.clearMouseScrollState(); + this._clearMouseScrollState(); } this.mouseScrollDelta += mouseScrollDelta; @@ -274,7 +291,7 @@ var PresentationMode = { var pageFlipDirection = (this.mouseScrollDelta > 0) ? PageFlipDirection.UP : PageFlipDirection.DOWN; - this.clearMouseScrollState(); + this._clearMouseScrollState(); var currentPage = PDFViewerApplication.page; // In case we are already on the first or the last page there is no need @@ -294,24 +311,8 @@ var PresentationMode = { * This function clears the member attributes used with mouse scrolling in * presentation mode. */ - clearMouseScrollState: function presentationModeClearMouseScrollState() { + _clearMouseScrollState: function pdfPresentationModeClearMouseScrollState() { this.mouseScrollTimeStamp = 0; this.mouseScrollDelta = 0; } }; - -(function presentationModeClosure() { - function presentationModeChange(e) { - if (PresentationMode.isFullscreen) { - PresentationMode.enter(); - } else { - PresentationMode.exit(); - } - } - - window.addEventListener('fullscreenchange', presentationModeChange, false); - window.addEventListener('mozfullscreenchange', presentationModeChange, false); - window.addEventListener('webkitfullscreenchange', presentationModeChange, - false); - window.addEventListener('MSFullscreenChange', presentationModeChange, false); -})(); diff --git a/web/viewer.html b/web/viewer.html index 41adfb626..70144eb9f 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -81,7 +81,7 @@ http://sourceforge.net/adobe/cmap/wiki/License/ - + diff --git a/web/viewer.js b/web/viewer.js index 819be1a84..40ab7a4c0 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -18,7 +18,7 @@ DownloadManager, getFileName, getPDFFileNameFromURL, PDFHistory, Preferences, SidebarView, ViewHistory, Stats, PDFThumbnailViewer, URL, noContextMenuHandler, SecondaryToolbar, - PasswordPrompt, PresentationMode, HandTool, Promise, + PasswordPrompt, PDFPresentationMode, HandTool, Promise, DocumentProperties, PDFOutlineView, PDFAttachmentView, OverlayManager, PDFFindController, PDFFindBar, getVisibleElements, watchScroll, PDFViewer, PDFRenderingQueue, PresentationModeState, @@ -84,7 +84,7 @@ var mozL10n = document.mozL10n || document.webL10n; //#include pdf_find_controller.js //#include pdf_history.js //#include secondary_toolbar.js -//#include presentation_mode.js +//#include pdf_presentation_mode.js //#include hand_tool.js //#include overlay_manager.js //#include password_prompt.js @@ -189,7 +189,7 @@ var PDFViewerApplication = { if (this.supportsFullscreen) { var toolbar = SecondaryToolbar; - PresentationMode.initialize({ + PDFPresentationMode.initialize({ container: container, pdfThumbnailViewer: this.pdfThumbnailViewer, contextMenuItems: [ @@ -1344,14 +1344,14 @@ var PDFViewerApplication = { if (!this.supportsFullscreen) { return; } - PresentationMode.request(); + PDFPresentationMode.request(); }, scrollPresentationMode: function pdfViewScrollPresentationMode(delta) { if (!this.supportsFullscreen) { return; } - PresentationMode.mouseScroll(delta); + PDFPresentationMode.mouseScroll(delta); } }; //#if GENERIC From d5089f42fadfcd4c203177c11400c09535edb5de Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 1 Feb 2015 22:53:59 +0100 Subject: [PATCH 5/8] Initial refactoring of the PDFPresentationMode code Noteworthy changes: - Adds JSDoc comments to PDFPresentationMode. - Removes a couple of, no longer necessary, helper functions. --- web/pdf_presentation_mode.js | 189 ++++++++++++++++++++--------------- web/viewer.js | 4 + 2 files changed, 110 insertions(+), 83 deletions(-) diff --git a/web/pdf_presentation_mode.js b/web/pdf_presentation_mode.js index 098068433..ca5d46a34 100644 --- a/web/pdf_presentation_mode.js +++ b/web/pdf_presentation_mode.js @@ -22,6 +22,16 @@ var DELAY_BEFORE_HIDING_CONTROLS = 3000; // in ms var SELECTOR = 'presentationControls'; var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1000; // in ms +/** + * @typedef {Object} PDFPresentationModeOptions + * @property {HTMLDivElement} container - The container for the viewer element. + * @property {HTMLDivElement} viewer - (optional) The viewer element. + * @property {PDFThumbnailViewer} pdfThumbnailViewer - (optional) The thumbnail + * viewer. + * @property {Array} contextMenuItems - (optional) The menuitems that are added + * to the context menu in Presentation Mode. + */ + var PDFPresentationMode = { initialized: false, active: false, @@ -30,14 +40,16 @@ var PDFPresentationMode = { mouseScrollTimeStamp: 0, mouseScrollDelta: 0, + /** + * @param {PDFPresentationModeOptions} options + */ initialize: function pdfPresentationModeInitialize(options) { this.initialized = true; this.container = options.container; + this.viewer = options.viewer || options.container.firstElementChild; this.pdfThumbnailViewer = options.pdfThumbnailViewer || null; var contextMenuItems = options.contextMenuItems || null; - this.viewer = this.container.firstElementChild; - window.addEventListener('fullscreenchange', this._fullscreenChange); window.addEventListener('mozfullscreenchange', this._fullscreenChange); //#if !(FIREFOX || MOZCENTRAL) @@ -57,12 +69,15 @@ var PDFPresentationMode = { }, get isFullscreen() { - return (document.fullscreenElement || - document.mozFullScreen || - document.webkitIsFullScreen || - document.msFullscreenElement); + return !!(document.fullscreenElement || + document.mozFullScreen || + document.webkitIsFullScreen || + document.msFullscreenElement); }, + /** + * @private + */ _fullscreenChange: function pdfPresentationModeFullscreenChange() { var self = PDFPresentationMode; if (self.isFullscreen) { @@ -73,12 +88,12 @@ var PDFPresentationMode = { }, /** - * Initialize a timeout that is used to specify switchInProgress when the - * browser transitions to fullscreen mode. Since resize events are triggered - * multiple times during the switch to fullscreen mode, this is necessary in - * order to prevent the page from being scrolled partially, or completely, - * out of view when Presentation Mode is enabled. - * Note: This is only an issue at certain zoom levels, e.g. 'page-width'. + * Used to initialize a timeout when requesting Presentation Mode, + * i.e. when the browser is requested to enter fullscreen mode. + * This timeout is used to prevent the current page from being scrolled + * partially, or completely, out of view when entering Presentation Mode. + * NOTE: This issue seems limited to certain zoom levels (e.g. 'page-width'). + * @private */ _setSwitchInProgress: function pdfPresentationMode_setSwitchInProgress() { if (this.switchInProgress) { @@ -90,6 +105,9 @@ var PDFPresentationMode = { }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS); }, + /** + * @private + */ _resetSwitchInProgress: function pdfPresentationMode_resetSwitchInProgress() { if (this.switchInProgress) { clearTimeout(this.switchInProgress); @@ -97,8 +115,12 @@ var PDFPresentationMode = { } }, + /** + * Request the browser to enter fullscreen mode. + * @returns {boolean} Indicating if the request was successful. + */ request: function pdfPresentationModeRequest() { - if (!this.initialized || this.isFullscreen || + if (!this.initialized || this.switchInProgress || this.active || !this.viewer.hasChildNodes()) { return false; } @@ -125,6 +147,9 @@ var PDFPresentationMode = { return true; }, + /** + * @private + */ _notifyStateChange: function pdfPresentationModeNotifyStateChange() { var self = PDFPresentationMode; var event = document.createEvent('CustomEvent'); @@ -135,6 +160,9 @@ var PDFPresentationMode = { window.dispatchEvent(event); }, + /** + * @private + */ _enter: function pdfPresentationModeEnter() { this.active = true; this._resetSwitchInProgress(); @@ -142,15 +170,14 @@ var PDFPresentationMode = { // Ensure that the correct page is scrolled into view when entering // Presentation Mode, by waiting until fullscreen mode in enabled. - // Note: This is only necessary in non-Mozilla browsers. setTimeout(function enterPresentationModeTimeout() { PDFViewerApplication.page = this.args.page; PDFViewerApplication.setScale('page-fit', true); }.bind(this), 0); - window.addEventListener('mousemove', this._mouseMove, false); + window.addEventListener('mousemove', this._showControls, false); window.addEventListener('mousedown', this._mouseDown, false); - window.addEventListener('keydown', this._keyDown, false); + window.addEventListener('keydown', this._resetMouseScrollState, false); window.addEventListener('contextmenu', this._contextMenu, false); this._showControls(); @@ -163,12 +190,14 @@ var PDFPresentationMode = { window.getSelection().removeAllRanges(); }, + /** + * @private + */ _exit: function pdfPresentationModeExit() { var page = PDFViewerApplication.page; // Ensure that the correct page is scrolled into view when exiting // Presentation Mode, by waiting until fullscreen mode is disabled. - // Note: This is only necessary in non-Mozilla browsers. setTimeout(function exitPresentationModeTimeout() { this.active = false; this._notifyStateChange(); @@ -178,48 +207,53 @@ var PDFPresentationMode = { this.args = null; }.bind(this), 0); - window.removeEventListener('mousemove', this._mouseMove, false); + window.removeEventListener('mousemove', this._showControls, false); window.removeEventListener('mousedown', this._mouseDown, false); - window.removeEventListener('keydown', this._keyDown, false); + window.removeEventListener('keydown', this._resetMouseScrollState, false); window.removeEventListener('contextmenu', this._contextMenu, false); this._hideControls(); - this._clearMouseScrollState(); + this._resetMouseScrollState(); this.container.removeAttribute('contextmenu'); this.contextMenuOpen = false; if (this.pdfThumbnailViewer) { - // Ensure that the thumbnail of the current page is visible - // when exiting presentation mode. this.pdfThumbnailViewer.ensureThumbnailVisible(page); } }, + /** + * @private + */ _showControls: function pdfPresentationModeShowControls() { - if (this.controlsTimeout) { - clearTimeout(this.controlsTimeout); + var self = PDFPresentationMode; + if (self.controlsTimeout) { + clearTimeout(self.controlsTimeout); } else { - this.container.classList.add(SELECTOR); + self.container.classList.add(SELECTOR); } - this.controlsTimeout = setTimeout(function showControlsTimeout() { - this.container.classList.remove(SELECTOR); - delete this.controlsTimeout; - }.bind(this), DELAY_BEFORE_HIDING_CONTROLS); + self.controlsTimeout = setTimeout(function showControlsTimeout() { + self.container.classList.remove(SELECTOR); + delete self.controlsTimeout; + }, DELAY_BEFORE_HIDING_CONTROLS); }, + /** + * @private + */ _hideControls: function pdfPresentationModeHideControls() { - if (!this.controlsTimeout) { + var self = PDFPresentationMode; + if (!self.controlsTimeout) { return; } - this.container.classList.remove(SELECTOR); - clearTimeout(this.controlsTimeout); - delete this.controlsTimeout; - }, - - _mouseMove: function pdfPresentationModeMouseMove(evt) { - PDFPresentationMode._showControls(); + clearTimeout(self.controlsTimeout); + self.container.classList.remove(SELECTOR); + delete self.controlsTimeout; }, + /** + * @private + */ _mouseDown: function pdfPresentationModeMouseDown(evt) { var self = PDFPresentationMode; if (self.contextMenuOpen) { @@ -227,7 +261,6 @@ var PDFPresentationMode = { evt.preventDefault(); return; } - if (evt.button === 0) { // Enable clicking of links in presentation mode. Please note: // Only links pointing to destinations in the current PDF document work. @@ -241,78 +274,68 @@ var PDFPresentationMode = { } }, - _keyDown: function pdfPresentationModeKeyDown(evt) { - PDFPresentationMode._clearMouseScrollState(); - }, - + /** + * @private + */ _contextMenu: function pdfPresentationModeContextMenu(evt) { PDFPresentationMode.contextMenuOpen = true; }, /** - * This function flips the page in presentation mode if the user scrolls up - * or down with large enough motion and prevents page flipping too often. - * @param {number} mouseScrollDelta The delta value from the mouse event. + * Switches page when the user scrolls (using a scroll wheel or a touchpad) + * with large enough motion, to prevent accidental page switches. + * @param {number} delta - The delta value from the mouse event. */ - mouseScroll: function pdfPresentationModeMouseScroll(mouseScrollDelta) { - if (!this.initialized) { + mouseScroll: function pdfPresentationModeMouseScroll(delta) { + if (!this.initialized && !this.active) { return; } var MOUSE_SCROLL_COOLDOWN_TIME = 50; + var PAGE_SWITCH_THRESHOLD = 120; + var PageSwitchDirection = { + UP: -1, + DOWN: 1 + }; var currentTime = (new Date()).getTime(); var storedTime = this.mouseScrollTimeStamp; - // In case one page has already been flipped there is a cooldown time - // which has to expire before next page can be scrolled on to. + // If we've already switched page, avoid accidentally switching page again. if (currentTime > storedTime && currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { return; } - - // In case the user decides to scroll to the opposite direction than before - // clear the accumulated delta. - if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) || - (this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) { - this._clearMouseScrollState(); + // If the user changes scroll direction, reset the accumulated scroll delta. + if ((this.mouseScrollDelta > 0 && delta < 0) || + (this.mouseScrollDelta < 0 && delta > 0)) { + this._resetMouseScrollState(); } + this.mouseScrollDelta += delta; - this.mouseScrollDelta += mouseScrollDelta; + if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) { + var pageSwitchDirection = (this.mouseScrollDelta > 0) ? + PageSwitchDirection.UP : PageSwitchDirection.DOWN; + var page = PDFViewerApplication.page; + this._resetMouseScrollState(); - var PAGE_FLIP_THRESHOLD = 120; - if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) { - - var PageFlipDirection = { - UP: -1, - DOWN: 1 - }; - - // In presentation mode scroll one page at a time. - var pageFlipDirection = (this.mouseScrollDelta > 0) ? - PageFlipDirection.UP : - PageFlipDirection.DOWN; - this._clearMouseScrollState(); - var currentPage = PDFViewerApplication.page; - - // In case we are already on the first or the last page there is no need - // to do anything. - if ((currentPage === 1 && pageFlipDirection === PageFlipDirection.UP) || - (currentPage === PDFViewerApplication.pagesCount && - pageFlipDirection === PageFlipDirection.DOWN)) { + // If we're already on the first/last page, we don't need to do anything. + if ((page === 1 && pageSwitchDirection === PageSwitchDirection.UP) || + (page === PDFViewerApplication.pagesCount && + pageSwitchDirection === PageSwitchDirection.DOWN)) { return; } - - PDFViewerApplication.page += pageFlipDirection; + PDFViewerApplication.page = (page + pageSwitchDirection); this.mouseScrollTimeStamp = currentTime; } }, /** - * This function clears the member attributes used with mouse scrolling in - * presentation mode. + * Resets the properties used for tracking mouse scrolling events. + * @private */ - _clearMouseScrollState: function pdfPresentationModeClearMouseScrollState() { - this.mouseScrollTimeStamp = 0; - this.mouseScrollDelta = 0; + _resetMouseScrollState: function pdfPresentationModeResetMouseScrollState() { + var self = PDFPresentationMode; + self.mouseScrollTimeStamp = 0; + self.mouseScrollDelta = 0; } }; diff --git a/web/viewer.js b/web/viewer.js index 40ab7a4c0..82a00c77f 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -191,6 +191,7 @@ var PDFViewerApplication = { var toolbar = SecondaryToolbar; PDFPresentationMode.initialize({ container: container, + viewer: viewer, pdfThumbnailViewer: this.pdfThumbnailViewer, contextMenuItems: [ { element: document.getElementById('contextFirstPage'), @@ -1347,6 +1348,9 @@ var PDFViewerApplication = { PDFPresentationMode.request(); }, + /** + * @param {number} delta - The delta value from the mouse event. + */ scrollPresentationMode: function pdfViewScrollPresentationMode(delta) { if (!this.supportsFullscreen) { return; From 4edee12e92618c5450baa4531a380d69825a7fe0 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 3 Feb 2015 12:17:57 +0100 Subject: [PATCH 6/8] Re-ordering the PDFPresentationMode code so that the "public" functions are placed towards the top of the file --- web/pdf_presentation_mode.js | 236 +++++++++++++++++------------------ 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/web/pdf_presentation_mode.js b/web/pdf_presentation_mode.js index ca5d46a34..5952ce21f 100644 --- a/web/pdf_presentation_mode.js +++ b/web/pdf_presentation_mode.js @@ -68,53 +68,6 @@ var PDFPresentationMode = { } }, - get isFullscreen() { - return !!(document.fullscreenElement || - document.mozFullScreen || - document.webkitIsFullScreen || - document.msFullscreenElement); - }, - - /** - * @private - */ - _fullscreenChange: function pdfPresentationModeFullscreenChange() { - var self = PDFPresentationMode; - if (self.isFullscreen) { - self._enter(); - } else { - self._exit(); - } - }, - - /** - * Used to initialize a timeout when requesting Presentation Mode, - * i.e. when the browser is requested to enter fullscreen mode. - * This timeout is used to prevent the current page from being scrolled - * partially, or completely, out of view when entering Presentation Mode. - * NOTE: This issue seems limited to certain zoom levels (e.g. 'page-width'). - * @private - */ - _setSwitchInProgress: function pdfPresentationMode_setSwitchInProgress() { - if (this.switchInProgress) { - clearTimeout(this.switchInProgress); - } - this.switchInProgress = setTimeout(function switchInProgressTimeout() { - delete this.switchInProgress; - this._notifyStateChange(); - }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS); - }, - - /** - * @private - */ - _resetSwitchInProgress: function pdfPresentationMode_resetSwitchInProgress() { - if (this.switchInProgress) { - clearTimeout(this.switchInProgress); - delete this.switchInProgress; - } - }, - /** * Request the browser to enter fullscreen mode. * @returns {boolean} Indicating if the request was successful. @@ -147,6 +100,73 @@ var PDFPresentationMode = { return true; }, + /** + * Switches page when the user scrolls (using a scroll wheel or a touchpad) + * with large enough motion, to prevent accidental page switches. + * @param {number} delta - The delta value from the mouse event. + */ + mouseScroll: function pdfPresentationModeMouseScroll(delta) { + if (!this.initialized && !this.active) { + return; + } + var MOUSE_SCROLL_COOLDOWN_TIME = 50; + var PAGE_SWITCH_THRESHOLD = 120; + var PageSwitchDirection = { + UP: -1, + DOWN: 1 + }; + + var currentTime = (new Date()).getTime(); + var storedTime = this.mouseScrollTimeStamp; + + // If we've already switched page, avoid accidentally switching page again. + if (currentTime > storedTime && + currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { + return; + } + // If the user changes scroll direction, reset the accumulated scroll delta. + if ((this.mouseScrollDelta > 0 && delta < 0) || + (this.mouseScrollDelta < 0 && delta > 0)) { + this._resetMouseScrollState(); + } + this.mouseScrollDelta += delta; + + if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) { + var pageSwitchDirection = (this.mouseScrollDelta > 0) ? + PageSwitchDirection.UP : PageSwitchDirection.DOWN; + var page = PDFViewerApplication.page; + this._resetMouseScrollState(); + + // If we're already on the first/last page, we don't need to do anything. + if ((page === 1 && pageSwitchDirection === PageSwitchDirection.UP) || + (page === PDFViewerApplication.pagesCount && + pageSwitchDirection === PageSwitchDirection.DOWN)) { + return; + } + PDFViewerApplication.page = (page + pageSwitchDirection); + this.mouseScrollTimeStamp = currentTime; + } + }, + + get isFullscreen() { + return !!(document.fullscreenElement || + document.mozFullScreen || + document.webkitIsFullScreen || + document.msFullscreenElement); + }, + + /** + * @private + */ + _fullscreenChange: function pdfPresentationModeFullscreenChange() { + var self = PDFPresentationMode; + if (self.isFullscreen) { + self._enter(); + } else { + self._exit(); + } + }, + /** * @private */ @@ -160,6 +180,34 @@ var PDFPresentationMode = { window.dispatchEvent(event); }, + /** + * Used to initialize a timeout when requesting Presentation Mode, + * i.e. when the browser is requested to enter fullscreen mode. + * This timeout is used to prevent the current page from being scrolled + * partially, or completely, out of view when entering Presentation Mode. + * NOTE: This issue seems limited to certain zoom levels (e.g. 'page-width'). + * @private + */ + _setSwitchInProgress: function pdfPresentationMode_setSwitchInProgress() { + if (this.switchInProgress) { + clearTimeout(this.switchInProgress); + } + this.switchInProgress = setTimeout(function switchInProgressTimeout() { + delete this.switchInProgress; + this._notifyStateChange(); + }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS); + }, + + /** + * @private + */ + _resetSwitchInProgress: function pdfPresentationMode_resetSwitchInProgress() { + if (this.switchInProgress) { + clearTimeout(this.switchInProgress); + delete this.switchInProgress; + } + }, + /** * @private */ @@ -222,35 +270,6 @@ var PDFPresentationMode = { } }, - /** - * @private - */ - _showControls: function pdfPresentationModeShowControls() { - var self = PDFPresentationMode; - if (self.controlsTimeout) { - clearTimeout(self.controlsTimeout); - } else { - self.container.classList.add(SELECTOR); - } - self.controlsTimeout = setTimeout(function showControlsTimeout() { - self.container.classList.remove(SELECTOR); - delete self.controlsTimeout; - }, DELAY_BEFORE_HIDING_CONTROLS); - }, - - /** - * @private - */ - _hideControls: function pdfPresentationModeHideControls() { - var self = PDFPresentationMode; - if (!self.controlsTimeout) { - return; - } - clearTimeout(self.controlsTimeout); - self.container.classList.remove(SELECTOR); - delete self.controlsTimeout; - }, - /** * @private */ @@ -282,51 +301,32 @@ var PDFPresentationMode = { }, /** - * Switches page when the user scrolls (using a scroll wheel or a touchpad) - * with large enough motion, to prevent accidental page switches. - * @param {number} delta - The delta value from the mouse event. + * @private */ - mouseScroll: function pdfPresentationModeMouseScroll(delta) { - if (!this.initialized && !this.active) { + _showControls: function pdfPresentationModeShowControls() { + var self = PDFPresentationMode; + if (self.controlsTimeout) { + clearTimeout(self.controlsTimeout); + } else { + self.container.classList.add(SELECTOR); + } + self.controlsTimeout = setTimeout(function showControlsTimeout() { + self.container.classList.remove(SELECTOR); + delete self.controlsTimeout; + }, DELAY_BEFORE_HIDING_CONTROLS); + }, + + /** + * @private + */ + _hideControls: function pdfPresentationModeHideControls() { + var self = PDFPresentationMode; + if (!self.controlsTimeout) { return; } - var MOUSE_SCROLL_COOLDOWN_TIME = 50; - var PAGE_SWITCH_THRESHOLD = 120; - var PageSwitchDirection = { - UP: -1, - DOWN: 1 - }; - - var currentTime = (new Date()).getTime(); - var storedTime = this.mouseScrollTimeStamp; - - // If we've already switched page, avoid accidentally switching page again. - if (currentTime > storedTime && - currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { - return; - } - // If the user changes scroll direction, reset the accumulated scroll delta. - if ((this.mouseScrollDelta > 0 && delta < 0) || - (this.mouseScrollDelta < 0 && delta > 0)) { - this._resetMouseScrollState(); - } - this.mouseScrollDelta += delta; - - if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) { - var pageSwitchDirection = (this.mouseScrollDelta > 0) ? - PageSwitchDirection.UP : PageSwitchDirection.DOWN; - var page = PDFViewerApplication.page; - this._resetMouseScrollState(); - - // If we're already on the first/last page, we don't need to do anything. - if ((page === 1 && pageSwitchDirection === PageSwitchDirection.UP) || - (page === PDFViewerApplication.pagesCount && - pageSwitchDirection === PageSwitchDirection.DOWN)) { - return; - } - PDFViewerApplication.page = (page + pageSwitchDirection); - this.mouseScrollTimeStamp = currentTime; - } + clearTimeout(self.controlsTimeout); + self.container.classList.remove(SELECTOR); + delete self.controlsTimeout; }, /** From 4211df63eb23bebb5ecbe783a15a9be53d6691dc Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 3 Feb 2015 18:09:11 +0100 Subject: [PATCH 7/8] Refactor PDFPresentationMode to be more class-like --- web/pdf_presentation_mode.js | 611 +++++++++++++++++++---------------- web/viewer.js | 12 +- 2 files changed, 340 insertions(+), 283 deletions(-) diff --git a/web/pdf_presentation_mode.js b/web/pdf_presentation_mode.js index 5952ce21f..467d7e249 100644 --- a/web/pdf_presentation_mode.js +++ b/web/pdf_presentation_mode.js @@ -18,9 +18,9 @@ 'use strict'; +var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500; // in ms var DELAY_BEFORE_HIDING_CONTROLS = 3000; // in ms var SELECTOR = 'presentationControls'; -var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1000; // in ms /** * @typedef {Object} PDFPresentationModeOptions @@ -32,30 +32,25 @@ var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1000; // in ms * to the context menu in Presentation Mode. */ -var PDFPresentationMode = { - initialized: false, - active: false, - args: null, - contextMenuOpen: false, - mouseScrollTimeStamp: 0, - mouseScrollDelta: 0, - +/** + * @class + */ +var PDFPresentationMode = (function PDFPresentationModeClosure() { /** + * @constructs PDFPresentationMode * @param {PDFPresentationModeOptions} options */ - initialize: function pdfPresentationModeInitialize(options) { - this.initialized = true; + function PDFPresentationMode(options) { this.container = options.container; this.viewer = options.viewer || options.container.firstElementChild; this.pdfThumbnailViewer = options.pdfThumbnailViewer || null; var contextMenuItems = options.contextMenuItems || null; - window.addEventListener('fullscreenchange', this._fullscreenChange); - window.addEventListener('mozfullscreenchange', this._fullscreenChange); -//#if !(FIREFOX || MOZCENTRAL) - window.addEventListener('webkitfullscreenchange', this._fullscreenChange); - window.addEventListener('MSFullscreenChange', this._fullscreenChange); -//#endif + this.active = false; + this.args = null; + this.contextMenuOpen = false; + this.mouseScrollTimeStamp = 0; + this.mouseScrollDelta = 0; if (contextMenuItems) { for (var i = 0, ii = contextMenuItems.length; i < ii; i++) { @@ -66,276 +61,336 @@ var PDFPresentationMode = { }.bind(this, item.handler)); } } - }, + } - /** - * Request the browser to enter fullscreen mode. - * @returns {boolean} Indicating if the request was successful. - */ - request: function pdfPresentationModeRequest() { - if (!this.initialized || this.switchInProgress || this.active || - !this.viewer.hasChildNodes()) { - return false; - } - this._setSwitchInProgress(); - this._notifyStateChange(); + PDFPresentationMode.prototype = { + /** + * Request the browser to enter fullscreen mode. + * @returns {boolean} Indicating if the request was successful. + */ + request: function PDFPresentationMode_request() { + if (this.switchInProgress || this.active || + !this.viewer.hasChildNodes()) { + return false; + } + this._addFullscreenChangeListeners(); + this._setSwitchInProgress(); + this._notifyStateChange(); - if (this.container.requestFullscreen) { - this.container.requestFullscreen(); - } else if (this.container.mozRequestFullScreen) { - this.container.mozRequestFullScreen(); - } else if (this.container.webkitRequestFullscreen) { - this.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); - } else if (this.container.msRequestFullscreen) { - this.container.msRequestFullscreen(); - } else { - return false; - } + if (this.container.requestFullscreen) { + this.container.requestFullscreen(); + } else if (this.container.mozRequestFullScreen) { + this.container.mozRequestFullScreen(); + } else if (this.container.webkitRequestFullscreen) { + this.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } else if (this.container.msRequestFullscreen) { + this.container.msRequestFullscreen(); + } else { + return false; + } - this.args = { - page: PDFViewerApplication.page, - previousScale: PDFViewerApplication.currentScaleValue - }; + this.args = { + page: PDFViewerApplication.page, + previousScale: PDFViewerApplication.currentScaleValue + }; - return true; - }, + return true; + }, - /** - * Switches page when the user scrolls (using a scroll wheel or a touchpad) - * with large enough motion, to prevent accidental page switches. - * @param {number} delta - The delta value from the mouse event. - */ - mouseScroll: function pdfPresentationModeMouseScroll(delta) { - if (!this.initialized && !this.active) { - return; - } - var MOUSE_SCROLL_COOLDOWN_TIME = 50; - var PAGE_SWITCH_THRESHOLD = 120; - var PageSwitchDirection = { - UP: -1, - DOWN: 1 - }; - - var currentTime = (new Date()).getTime(); - var storedTime = this.mouseScrollTimeStamp; - - // If we've already switched page, avoid accidentally switching page again. - if (currentTime > storedTime && - currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { - return; - } - // If the user changes scroll direction, reset the accumulated scroll delta. - if ((this.mouseScrollDelta > 0 && delta < 0) || - (this.mouseScrollDelta < 0 && delta > 0)) { - this._resetMouseScrollState(); - } - this.mouseScrollDelta += delta; - - if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) { - var pageSwitchDirection = (this.mouseScrollDelta > 0) ? - PageSwitchDirection.UP : PageSwitchDirection.DOWN; - var page = PDFViewerApplication.page; - this._resetMouseScrollState(); - - // If we're already on the first/last page, we don't need to do anything. - if ((page === 1 && pageSwitchDirection === PageSwitchDirection.UP) || - (page === PDFViewerApplication.pagesCount && - pageSwitchDirection === PageSwitchDirection.DOWN)) { + /** + * Switches page when the user scrolls (using a scroll wheel or a touchpad) + * with large enough motion, to prevent accidental page switches. + * @param {number} delta - The delta value from the mouse event. + */ + mouseScroll: function PDFPresentationMode_mouseScroll(delta) { + if (!this.active) { return; } - PDFViewerApplication.page = (page + pageSwitchDirection); - this.mouseScrollTimeStamp = currentTime; - } - }, + var MOUSE_SCROLL_COOLDOWN_TIME = 50; + var PAGE_SWITCH_THRESHOLD = 120; + var PageSwitchDirection = { + UP: -1, + DOWN: 1 + }; - get isFullscreen() { - return !!(document.fullscreenElement || - document.mozFullScreen || - document.webkitIsFullScreen || - document.msFullscreenElement); - }, + var currentTime = (new Date()).getTime(); + var storedTime = this.mouseScrollTimeStamp; - /** - * @private - */ - _fullscreenChange: function pdfPresentationModeFullscreenChange() { - var self = PDFPresentationMode; - if (self.isFullscreen) { - self._enter(); - } else { - self._exit(); - } - }, - - /** - * @private - */ - _notifyStateChange: function pdfPresentationModeNotifyStateChange() { - var self = PDFPresentationMode; - var event = document.createEvent('CustomEvent'); - event.initCustomEvent('presentationmodechanged', true, true, { - active: self.active, - switchInProgress: !!self.switchInProgress - }); - window.dispatchEvent(event); - }, - - /** - * Used to initialize a timeout when requesting Presentation Mode, - * i.e. when the browser is requested to enter fullscreen mode. - * This timeout is used to prevent the current page from being scrolled - * partially, or completely, out of view when entering Presentation Mode. - * NOTE: This issue seems limited to certain zoom levels (e.g. 'page-width'). - * @private - */ - _setSwitchInProgress: function pdfPresentationMode_setSwitchInProgress() { - if (this.switchInProgress) { - clearTimeout(this.switchInProgress); - } - this.switchInProgress = setTimeout(function switchInProgressTimeout() { - delete this.switchInProgress; - this._notifyStateChange(); - }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS); - }, - - /** - * @private - */ - _resetSwitchInProgress: function pdfPresentationMode_resetSwitchInProgress() { - if (this.switchInProgress) { - clearTimeout(this.switchInProgress); - delete this.switchInProgress; - } - }, - - /** - * @private - */ - _enter: function pdfPresentationModeEnter() { - this.active = true; - this._resetSwitchInProgress(); - this._notifyStateChange(); - - // Ensure that the correct page is scrolled into view when entering - // Presentation Mode, by waiting until fullscreen mode in enabled. - setTimeout(function enterPresentationModeTimeout() { - PDFViewerApplication.page = this.args.page; - PDFViewerApplication.setScale('page-fit', true); - }.bind(this), 0); - - window.addEventListener('mousemove', this._showControls, false); - window.addEventListener('mousedown', this._mouseDown, false); - window.addEventListener('keydown', this._resetMouseScrollState, false); - window.addEventListener('contextmenu', this._contextMenu, false); - - this._showControls(); - this.contextMenuOpen = false; - this.container.setAttribute('contextmenu', 'viewerContextMenu'); - - // Text selection is disabled in Presentation Mode, thus it's not possible - // for the user to deselect text that is selected (e.g. with "Select all") - // when entering Presentation Mode, hence we remove any active selection. - window.getSelection().removeAllRanges(); - }, - - /** - * @private - */ - _exit: function pdfPresentationModeExit() { - var page = PDFViewerApplication.page; - - // Ensure that the correct page is scrolled into view when exiting - // Presentation Mode, by waiting until fullscreen mode is disabled. - setTimeout(function exitPresentationModeTimeout() { - this.active = false; - this._notifyStateChange(); - - PDFViewerApplication.setScale(this.args.previousScale, true); - PDFViewerApplication.page = page; - this.args = null; - }.bind(this), 0); - - window.removeEventListener('mousemove', this._showControls, false); - window.removeEventListener('mousedown', this._mouseDown, false); - window.removeEventListener('keydown', this._resetMouseScrollState, false); - window.removeEventListener('contextmenu', this._contextMenu, false); - - this._hideControls(); - this._resetMouseScrollState(); - this.container.removeAttribute('contextmenu'); - this.contextMenuOpen = false; - - if (this.pdfThumbnailViewer) { - this.pdfThumbnailViewer.ensureThumbnailVisible(page); - } - }, - - /** - * @private - */ - _mouseDown: function pdfPresentationModeMouseDown(evt) { - var self = PDFPresentationMode; - if (self.contextMenuOpen) { - self.contextMenuOpen = false; - evt.preventDefault(); - return; - } - if (evt.button === 0) { - // Enable clicking of links in presentation mode. Please note: - // Only links pointing to destinations in the current PDF document work. - var isInternalLink = (evt.target.href && - evt.target.classList.contains('internalLink')); - if (!isInternalLink) { - // Unless an internal link was clicked, advance one page. - evt.preventDefault(); - PDFViewerApplication.page += (evt.shiftKey ? -1 : 1); + // If we've already switched page, avoid accidentally switching again. + if (currentTime > storedTime && + currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) { + return; } + // If the scroll direction changed, reset the accumulated scroll delta. + if ((this.mouseScrollDelta > 0 && delta < 0) || + (this.mouseScrollDelta < 0 && delta > 0)) { + this._resetMouseScrollState(); + } + this.mouseScrollDelta += delta; + + if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) { + var pageSwitchDirection = (this.mouseScrollDelta > 0) ? + PageSwitchDirection.UP : PageSwitchDirection.DOWN; + var page = PDFViewerApplication.page; + this._resetMouseScrollState(); + + // If we're at the first/last page, we don't need to do anything. + if ((page === 1 && pageSwitchDirection === PageSwitchDirection.UP) || + (page === PDFViewerApplication.pagesCount && + pageSwitchDirection === PageSwitchDirection.DOWN)) { + return; + } + PDFViewerApplication.page = (page + pageSwitchDirection); + this.mouseScrollTimeStamp = currentTime; + } + }, + + get isFullscreen() { + return !!(document.fullscreenElement || + document.mozFullScreen || + document.webkitIsFullScreen || + document.msFullscreenElement); + }, + + /** + * @private + */ + _notifyStateChange: function PDFPresentationMode_notifyStateChange() { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('presentationmodechanged', true, true, { + active: this.active, + switchInProgress: !!this.switchInProgress + }); + window.dispatchEvent(event); + }, + + /** + * Used to initialize a timeout when requesting Presentation Mode, + * i.e. when the browser is requested to enter fullscreen mode. + * This timeout is used to prevent the current page from being scrolled + * partially, or completely, out of view when entering Presentation Mode. + * NOTE: This issue seems limited to certain zoom levels (e.g. page-width). + * @private + */ + _setSwitchInProgress: function PDFPresentationMode_setSwitchInProgress() { + if (this.switchInProgress) { + clearTimeout(this.switchInProgress); + } + this.switchInProgress = setTimeout(function switchInProgressTimeout() { + this._removeFullscreenChangeListeners(); + delete this.switchInProgress; + this._notifyStateChange(); + }.bind(this), DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS); + }, + + /** + * @private + */ + _resetSwitchInProgress: + function PDFPresentationMode_resetSwitchInProgress() { + if (this.switchInProgress) { + clearTimeout(this.switchInProgress); + delete this.switchInProgress; + } + }, + + /** + * @private + */ + _enter: function PDFPresentationMode_enter() { + this.active = true; + this._resetSwitchInProgress(); + this._notifyStateChange(); + + // Ensure that the correct page is scrolled into view when entering + // Presentation Mode, by waiting until fullscreen mode in enabled. + setTimeout(function enterPresentationModeTimeout() { + PDFViewerApplication.page = this.args.page; + PDFViewerApplication.setScale('page-fit', true); + }.bind(this), 0); + + this._addWindowListeners(); + this._showControls(); + this.contextMenuOpen = false; + this.container.setAttribute('contextmenu', 'viewerContextMenu'); + + // Text selection is disabled in Presentation Mode, thus it's not possible + // for the user to deselect text that is selected (e.g. with "Select all") + // when entering Presentation Mode, hence we remove any active selection. + window.getSelection().removeAllRanges(); + }, + + /** + * @private + */ + _exit: function PDFPresentationMode_exit() { + var page = PDFViewerApplication.page; + + // Ensure that the correct page is scrolled into view when exiting + // Presentation Mode, by waiting until fullscreen mode is disabled. + setTimeout(function exitPresentationModeTimeout() { + this.active = false; + this._removeFullscreenChangeListeners(); + this._notifyStateChange(); + + PDFViewerApplication.setScale(this.args.previousScale, true); + PDFViewerApplication.page = page; + this.args = null; + }.bind(this), 0); + + this._removeWindowListeners(); + this._hideControls(); + this._resetMouseScrollState(); + this.container.removeAttribute('contextmenu'); + this.contextMenuOpen = false; + + if (this.pdfThumbnailViewer) { + this.pdfThumbnailViewer.ensureThumbnailVisible(page); + } + }, + + /** + * @private + */ + _mouseDown: function PDFPresentationMode_mouseDown(evt) { + if (this.contextMenuOpen) { + this.contextMenuOpen = false; + evt.preventDefault(); + return; + } + if (evt.button === 0) { + // Enable clicking of links in presentation mode. Please note: + // Only links pointing to destinations in the current PDF document work. + var isInternalLink = (evt.target.href && + evt.target.classList.contains('internalLink')); + if (!isInternalLink) { + // Unless an internal link was clicked, advance one page. + evt.preventDefault(); + PDFViewerApplication.page += (evt.shiftKey ? -1 : 1); + } + } + }, + + /** + * @private + */ + _contextMenu: function PDFPresentationMode_contextMenu() { + this.contextMenuOpen = true; + }, + + /** + * @private + */ + _showControls: function PDFPresentationMode_showControls() { + if (this.controlsTimeout) { + clearTimeout(this.controlsTimeout); + } else { + this.container.classList.add(SELECTOR); + } + this.controlsTimeout = setTimeout(function showControlsTimeout() { + this.container.classList.remove(SELECTOR); + delete this.controlsTimeout; + }.bind(this), DELAY_BEFORE_HIDING_CONTROLS); + }, + + /** + * @private + */ + _hideControls: function PDFPresentationMode_hideControls() { + if (!this.controlsTimeout) { + return; + } + clearTimeout(this.controlsTimeout); + this.container.classList.remove(SELECTOR); + delete this.controlsTimeout; + }, + + /** + * Resets the properties used for tracking mouse scrolling events. + * @private + */ + _resetMouseScrollState: + function PDFPresentationMode_resetMouseScrollState() { + this.mouseScrollTimeStamp = 0; + this.mouseScrollDelta = 0; + }, + + /** + * @private + */ + _addWindowListeners: function PDFPresentationMode_addWindowListeners() { + this.showControlsBind = this._showControls.bind(this); + this.mouseDownBind = this._mouseDown.bind(this); + this.resetMouseScrollStateBind = this._resetMouseScrollState.bind(this); + this.contextMenuBind = this._contextMenu.bind(this); + + window.addEventListener('mousemove', this.showControlsBind); + window.addEventListener('mousedown', this.mouseDownBind); + window.addEventListener('keydown', this.resetMouseScrollStateBind); + window.addEventListener('contextmenu', this.contextMenuBind); + }, + + /** + * @private + */ + _removeWindowListeners: + function PDFPresentationMode_removeWindowListeners() { + window.removeEventListener('mousemove', this.showControlsBind); + window.removeEventListener('mousedown', this.mouseDownBind); + window.removeEventListener('keydown', this.resetMouseScrollStateBind); + window.removeEventListener('contextmenu', this.contextMenuBind); + + delete this.showControlsBind; + delete this.mouseDownBind; + delete this.resetMouseScrollStateBind; + delete this.contextMenuBind; + }, + + /** + * @private + */ + _fullscreenChange: function PDFPresentationMode_fullscreenChange() { + if (this.isFullscreen) { + this._enter(); + } else { + this._exit(); + } + }, + + /** + * @private + */ + _addFullscreenChangeListeners: + function PDFPresentationMode_addFullscreenChangeListeners() { + this.fullscreenChangeBind = this._fullscreenChange.bind(this); + + window.addEventListener('fullscreenchange', this.fullscreenChangeBind); + window.addEventListener('mozfullscreenchange', this.fullscreenChangeBind); +//#if !(FIREFOX || MOZCENTRAL) + window.addEventListener('webkitfullscreenchange', + this.fullscreenChangeBind); + window.addEventListener('MSFullscreenChange', this.fullscreenChangeBind); +//#endif + }, + + /** + * @private + */ + _removeFullscreenChangeListeners: + function PDFPresentationMode_removeFullscreenChangeListeners() { + window.removeEventListener('fullscreenchange', this.fullscreenChangeBind); + window.removeEventListener('mozfullscreenchange', + this.fullscreenChangeBind); +//#if !(FIREFOX || MOZCENTRAL) + window.removeEventListener('webkitfullscreenchange', + this.fullscreenChangeBind); + window.removeEventListener('MSFullscreenChange', + this.fullscreenChangeBind); +//#endif + + delete this.fullscreenChangeBind; } - }, + }; - /** - * @private - */ - _contextMenu: function pdfPresentationModeContextMenu(evt) { - PDFPresentationMode.contextMenuOpen = true; - }, - - /** - * @private - */ - _showControls: function pdfPresentationModeShowControls() { - var self = PDFPresentationMode; - if (self.controlsTimeout) { - clearTimeout(self.controlsTimeout); - } else { - self.container.classList.add(SELECTOR); - } - self.controlsTimeout = setTimeout(function showControlsTimeout() { - self.container.classList.remove(SELECTOR); - delete self.controlsTimeout; - }, DELAY_BEFORE_HIDING_CONTROLS); - }, - - /** - * @private - */ - _hideControls: function pdfPresentationModeHideControls() { - var self = PDFPresentationMode; - if (!self.controlsTimeout) { - return; - } - clearTimeout(self.controlsTimeout); - self.container.classList.remove(SELECTOR); - delete self.controlsTimeout; - }, - - /** - * Resets the properties used for tracking mouse scrolling events. - * @private - */ - _resetMouseScrollState: function pdfPresentationModeResetMouseScrollState() { - var self = PDFPresentationMode; - self.mouseScrollTimeStamp = 0; - self.mouseScrollDelta = 0; - } -}; + return PDFPresentationMode; +})(); diff --git a/web/viewer.js b/web/viewer.js index 82a00c77f..33e11f1d5 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -107,6 +107,8 @@ var PDFViewerApplication = { pdfThumbnailViewer: null, /** @type {PDFRenderingQueue} */ pdfRenderingQueue: null, + /** @type {PDFPresentationMode} */ + pdfPresentationMode: null, pageRotation: 0, updateScaleControls: true, isInitialViewSet: false, @@ -189,7 +191,7 @@ var PDFViewerApplication = { if (this.supportsFullscreen) { var toolbar = SecondaryToolbar; - PDFPresentationMode.initialize({ + this.pdfPresentationMode = new PDFPresentationMode({ container: container, viewer: viewer, pdfThumbnailViewer: this.pdfThumbnailViewer, @@ -1342,20 +1344,20 @@ var PDFViewerApplication = { }, requestPresentationMode: function pdfViewRequestPresentationMode() { - if (!this.supportsFullscreen) { + if (!this.pdfPresentationMode) { return; } - PDFPresentationMode.request(); + this.pdfPresentationMode.request(); }, /** * @param {number} delta - The delta value from the mouse event. */ scrollPresentationMode: function pdfViewScrollPresentationMode(delta) { - if (!this.supportsFullscreen) { + if (!this.pdfPresentationMode) { return; } - PDFPresentationMode.mouseScroll(delta); + this.pdfPresentationMode.mouseScroll(delta); } }; //#if GENERIC From 601bd4a930c5e89ed272acb5752d06b42731e4a3 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 4 Feb 2015 14:10:45 +0100 Subject: [PATCH 8/8] Preface all "fullscreen" CSS rules with a |pdfPresentationMode| class, and add it to the |viewerContainer| while Presentation Mode is active --- web/pdf_presentation_mode.js | 11 +++++++---- web/pdf_viewer.css | 8 ++++---- web/viewer.css | 32 ++++++++++++++++++-------------- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/web/pdf_presentation_mode.js b/web/pdf_presentation_mode.js index 467d7e249..3dd028595 100644 --- a/web/pdf_presentation_mode.js +++ b/web/pdf_presentation_mode.js @@ -20,7 +20,8 @@ var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500; // in ms var DELAY_BEFORE_HIDING_CONTROLS = 3000; // in ms -var SELECTOR = 'presentationControls'; +var ACTIVE_SELECTOR = 'pdfPresentationMode'; +var CONTROLS_SELECTOR = 'pdfPresentationModeControls'; /** * @typedef {Object} PDFPresentationModeOptions @@ -201,6 +202,7 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { this.active = true; this._resetSwitchInProgress(); this._notifyStateChange(); + this.container.classList.add(ACTIVE_SELECTOR); // Ensure that the correct page is scrolled into view when entering // Presentation Mode, by waiting until fullscreen mode in enabled. @@ -225,6 +227,7 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { */ _exit: function PDFPresentationMode_exit() { var page = PDFViewerApplication.page; + this.container.classList.remove(ACTIVE_SELECTOR); // Ensure that the correct page is scrolled into view when exiting // Presentation Mode, by waiting until fullscreen mode is disabled. @@ -285,10 +288,10 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { if (this.controlsTimeout) { clearTimeout(this.controlsTimeout); } else { - this.container.classList.add(SELECTOR); + this.container.classList.add(CONTROLS_SELECTOR); } this.controlsTimeout = setTimeout(function showControlsTimeout() { - this.container.classList.remove(SELECTOR); + this.container.classList.remove(CONTROLS_SELECTOR); delete this.controlsTimeout; }.bind(this), DELAY_BEFORE_HIDING_CONTROLS); }, @@ -301,7 +304,7 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { return; } clearTimeout(this.controlsTimeout); - this.container.classList.remove(SELECTOR); + this.container.classList.remove(CONTROLS_SELECTOR); delete this.controlsTimeout; }, diff --git a/web/pdf_viewer.css b/web/pdf_viewer.css index d1e59ab7e..47f2450b6 100644 --- a/web/pdf_viewer.css +++ b/web/pdf_viewer.css @@ -57,22 +57,22 @@ box-shadow: 0px 2px 10px #ff0; } -:-webkit-full-screen .pdfViewer .page { +.pdfPresentationMode:-webkit-full-screen .pdfViewer .page { margin-bottom: 100%; border: 0; } -:-moz-full-screen .pdfViewer .page { +.pdfPresentationMode:-moz-full-screen .pdfViewer .page { margin-bottom: 100%; border: 0; } -:-ms-fullscreen .pdfViewer .page { +.pdfPresentationMode:-ms-fullscreen .pdfViewer .page { margin-bottom: 100% !important; border: 0; } -:fullscreen .pdfViewer .page { +.pdfPresentationMode:fullscreen .pdfViewer .page { margin-bottom: 100%; border: 0; } diff --git a/web/viewer.css b/web/viewer.css index 4432b302b..c248633b7 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -47,7 +47,7 @@ select { display: none !important; } -#viewerContainer:-webkit-full-screen { +#viewerContainer.pdfPresentationMode:-webkit-full-screen { top: 0px; border-top: 2px solid transparent; background-color: #000; @@ -58,7 +58,7 @@ select { -webkit-user-select: none; } -#viewerContainer:-moz-full-screen { +#viewerContainer.pdfPresentationMode:-moz-full-screen { top: 0px; border-top: 2px solid transparent; background-color: #000; @@ -69,7 +69,7 @@ select { -moz-user-select: none; } -#viewerContainer:-ms-fullscreen { +#viewerContainer.pdfPresentationMode:-ms-fullscreen { top: 0px !important; border-top: 2px solid transparent; width: 100%; @@ -79,11 +79,11 @@ select { -ms-user-select: none; } -#viewerContainer:-ms-fullscreen::-ms-backdrop { +#viewerContainer.pdfPresentationMode:-ms-fullscreen::-ms-backdrop { background-color: #000; } -#viewerContainer:fullscreen { +#viewerContainer.pdfPresentationMode:fullscreen { top: 0px; border-top: 2px solid transparent; background-color: #000; @@ -96,36 +96,40 @@ select { -ms-user-select: none; } -:-webkit-full-screen a:not(.internalLink) { +.pdfPresentationMode:-webkit-full-screen a:not(.internalLink) { display: none; } -:-moz-full-screen a:not(.internalLink) { +.pdfPresentationMode:-moz-full-screen a:not(.internalLink) { display: none; } -:-ms-fullscreen a:not(.internalLink) { +.pdfPresentationMode:-ms-fullscreen a:not(.internalLink) { display: none !important; } -:fullscreen a:not(.internalLink) { +.pdfPresentationMode:fullscreen a:not(.internalLink) { display: none; } -:-webkit-full-screen .textLayer > div { +.pdfPresentationMode:-webkit-full-screen .textLayer > div { cursor: none; } -:-moz-full-screen .textLayer > div { +.pdfPresentationMode:-moz-full-screen .textLayer > div { cursor: none; } -:fullscreen .textLayer > div { +.pdfPresentationMode:-ms-fullscreen .textLayer > div { cursor: none; } -#viewerContainer.presentationControls, -#viewerContainer.presentationControls .textLayer > div { +.pdfPresentationMode:fullscreen .textLayer > div { + cursor: none; +} + +.pdfPresentationMode.pdfPresentationModeControls > *, +.pdfPresentationMode.pdfPresentationModeControls .textLayer > div { cursor: default; }