2
0

pdf_presentation_mode.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /* Copyright 2017 Mozilla Foundation
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. 'use strict';
  16. Object.defineProperty(exports, "__esModule", {
  17. value: true
  18. });
  19. exports.PDFPresentationMode = undefined;
  20. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  21. var _ui_utils = require('./ui_utils');
  22. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  23. var DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500;
  24. var DELAY_BEFORE_HIDING_CONTROLS = 3000;
  25. var ACTIVE_SELECTOR = 'pdfPresentationMode';
  26. var CONTROLS_SELECTOR = 'pdfPresentationModeControls';
  27. var MOUSE_SCROLL_COOLDOWN_TIME = 50;
  28. var PAGE_SWITCH_THRESHOLD = 0.1;
  29. var SWIPE_MIN_DISTANCE_THRESHOLD = 50;
  30. var SWIPE_ANGLE_THRESHOLD = Math.PI / 6;
  31. var PDFPresentationMode = function () {
  32. function PDFPresentationMode(options) {
  33. var _this = this;
  34. _classCallCheck(this, PDFPresentationMode);
  35. this.container = options.container;
  36. this.viewer = options.viewer || options.container.firstElementChild;
  37. this.pdfViewer = options.pdfViewer;
  38. this.eventBus = options.eventBus;
  39. var contextMenuItems = options.contextMenuItems || null;
  40. this.active = false;
  41. this.args = null;
  42. this.contextMenuOpen = false;
  43. this.mouseScrollTimeStamp = 0;
  44. this.mouseScrollDelta = 0;
  45. this.touchSwipeState = null;
  46. if (contextMenuItems) {
  47. contextMenuItems.contextFirstPage.addEventListener('click', function () {
  48. _this.contextMenuOpen = false;
  49. _this.eventBus.dispatch('firstpage');
  50. });
  51. contextMenuItems.contextLastPage.addEventListener('click', function () {
  52. _this.contextMenuOpen = false;
  53. _this.eventBus.dispatch('lastpage');
  54. });
  55. contextMenuItems.contextPageRotateCw.addEventListener('click', function () {
  56. _this.contextMenuOpen = false;
  57. _this.eventBus.dispatch('rotatecw');
  58. });
  59. contextMenuItems.contextPageRotateCcw.addEventListener('click', function () {
  60. _this.contextMenuOpen = false;
  61. _this.eventBus.dispatch('rotateccw');
  62. });
  63. }
  64. }
  65. _createClass(PDFPresentationMode, [{
  66. key: 'request',
  67. value: function request() {
  68. if (this.switchInProgress || this.active || !this.viewer.hasChildNodes()) {
  69. return false;
  70. }
  71. this._addFullscreenChangeListeners();
  72. this._setSwitchInProgress();
  73. this._notifyStateChange();
  74. if (this.container.requestFullscreen) {
  75. this.container.requestFullscreen();
  76. } else if (this.container.mozRequestFullScreen) {
  77. this.container.mozRequestFullScreen();
  78. } else if (this.container.webkitRequestFullscreen) {
  79. this.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  80. } else if (this.container.msRequestFullscreen) {
  81. this.container.msRequestFullscreen();
  82. } else {
  83. return false;
  84. }
  85. this.args = {
  86. page: this.pdfViewer.currentPageNumber,
  87. previousScale: this.pdfViewer.currentScaleValue
  88. };
  89. return true;
  90. }
  91. }, {
  92. key: '_mouseWheel',
  93. value: function _mouseWheel(evt) {
  94. if (!this.active) {
  95. return;
  96. }
  97. evt.preventDefault();
  98. var delta = (0, _ui_utils.normalizeWheelEventDelta)(evt);
  99. var currentTime = new Date().getTime();
  100. var storedTime = this.mouseScrollTimeStamp;
  101. if (currentTime > storedTime && currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
  102. return;
  103. }
  104. if (this.mouseScrollDelta > 0 && delta < 0 || this.mouseScrollDelta < 0 && delta > 0) {
  105. this._resetMouseScrollState();
  106. }
  107. this.mouseScrollDelta += delta;
  108. if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) {
  109. var totalDelta = this.mouseScrollDelta;
  110. this._resetMouseScrollState();
  111. var success = totalDelta > 0 ? this._goToPreviousPage() : this._goToNextPage();
  112. if (success) {
  113. this.mouseScrollTimeStamp = currentTime;
  114. }
  115. }
  116. }
  117. }, {
  118. key: '_goToPreviousPage',
  119. value: function _goToPreviousPage() {
  120. var page = this.pdfViewer.currentPageNumber;
  121. if (page <= 1) {
  122. return false;
  123. }
  124. this.pdfViewer.currentPageNumber = page - 1;
  125. return true;
  126. }
  127. }, {
  128. key: '_goToNextPage',
  129. value: function _goToNextPage() {
  130. var page = this.pdfViewer.currentPageNumber;
  131. if (page >= this.pdfViewer.pagesCount) {
  132. return false;
  133. }
  134. this.pdfViewer.currentPageNumber = page + 1;
  135. return true;
  136. }
  137. }, {
  138. key: '_notifyStateChange',
  139. value: function _notifyStateChange() {
  140. this.eventBus.dispatch('presentationmodechanged', {
  141. source: this,
  142. active: this.active,
  143. switchInProgress: !!this.switchInProgress
  144. });
  145. }
  146. }, {
  147. key: '_setSwitchInProgress',
  148. value: function _setSwitchInProgress() {
  149. var _this2 = this;
  150. if (this.switchInProgress) {
  151. clearTimeout(this.switchInProgress);
  152. }
  153. this.switchInProgress = setTimeout(function () {
  154. _this2._removeFullscreenChangeListeners();
  155. delete _this2.switchInProgress;
  156. _this2._notifyStateChange();
  157. }, DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS);
  158. }
  159. }, {
  160. key: '_resetSwitchInProgress',
  161. value: function _resetSwitchInProgress() {
  162. if (this.switchInProgress) {
  163. clearTimeout(this.switchInProgress);
  164. delete this.switchInProgress;
  165. }
  166. }
  167. }, {
  168. key: '_enter',
  169. value: function _enter() {
  170. var _this3 = this;
  171. this.active = true;
  172. this._resetSwitchInProgress();
  173. this._notifyStateChange();
  174. this.container.classList.add(ACTIVE_SELECTOR);
  175. setTimeout(function () {
  176. _this3.pdfViewer.currentPageNumber = _this3.args.page;
  177. _this3.pdfViewer.currentScaleValue = 'page-fit';
  178. }, 0);
  179. this._addWindowListeners();
  180. this._showControls();
  181. this.contextMenuOpen = false;
  182. this.container.setAttribute('contextmenu', 'viewerContextMenu');
  183. window.getSelection().removeAllRanges();
  184. }
  185. }, {
  186. key: '_exit',
  187. value: function _exit() {
  188. var _this4 = this;
  189. var page = this.pdfViewer.currentPageNumber;
  190. this.container.classList.remove(ACTIVE_SELECTOR);
  191. setTimeout(function () {
  192. _this4.active = false;
  193. _this4._removeFullscreenChangeListeners();
  194. _this4._notifyStateChange();
  195. _this4.pdfViewer.currentScaleValue = _this4.args.previousScale;
  196. _this4.pdfViewer.currentPageNumber = page;
  197. _this4.args = null;
  198. }, 0);
  199. this._removeWindowListeners();
  200. this._hideControls();
  201. this._resetMouseScrollState();
  202. this.container.removeAttribute('contextmenu');
  203. this.contextMenuOpen = false;
  204. }
  205. }, {
  206. key: '_mouseDown',
  207. value: function _mouseDown(evt) {
  208. if (this.contextMenuOpen) {
  209. this.contextMenuOpen = false;
  210. evt.preventDefault();
  211. return;
  212. }
  213. if (evt.button === 0) {
  214. var isInternalLink = evt.target.href && evt.target.classList.contains('internalLink');
  215. if (!isInternalLink) {
  216. evt.preventDefault();
  217. this.pdfViewer.currentPageNumber += evt.shiftKey ? -1 : 1;
  218. }
  219. }
  220. }
  221. }, {
  222. key: '_contextMenu',
  223. value: function _contextMenu() {
  224. this.contextMenuOpen = true;
  225. }
  226. }, {
  227. key: '_showControls',
  228. value: function _showControls() {
  229. var _this5 = this;
  230. if (this.controlsTimeout) {
  231. clearTimeout(this.controlsTimeout);
  232. } else {
  233. this.container.classList.add(CONTROLS_SELECTOR);
  234. }
  235. this.controlsTimeout = setTimeout(function () {
  236. _this5.container.classList.remove(CONTROLS_SELECTOR);
  237. delete _this5.controlsTimeout;
  238. }, DELAY_BEFORE_HIDING_CONTROLS);
  239. }
  240. }, {
  241. key: '_hideControls',
  242. value: function _hideControls() {
  243. if (!this.controlsTimeout) {
  244. return;
  245. }
  246. clearTimeout(this.controlsTimeout);
  247. this.container.classList.remove(CONTROLS_SELECTOR);
  248. delete this.controlsTimeout;
  249. }
  250. }, {
  251. key: '_resetMouseScrollState',
  252. value: function _resetMouseScrollState() {
  253. this.mouseScrollTimeStamp = 0;
  254. this.mouseScrollDelta = 0;
  255. }
  256. }, {
  257. key: '_touchSwipe',
  258. value: function _touchSwipe(evt) {
  259. if (!this.active) {
  260. return;
  261. }
  262. if (evt.touches.length > 1) {
  263. this.touchSwipeState = null;
  264. return;
  265. }
  266. switch (evt.type) {
  267. case 'touchstart':
  268. this.touchSwipeState = {
  269. startX: evt.touches[0].pageX,
  270. startY: evt.touches[0].pageY,
  271. endX: evt.touches[0].pageX,
  272. endY: evt.touches[0].pageY
  273. };
  274. break;
  275. case 'touchmove':
  276. if (this.touchSwipeState === null) {
  277. return;
  278. }
  279. this.touchSwipeState.endX = evt.touches[0].pageX;
  280. this.touchSwipeState.endY = evt.touches[0].pageY;
  281. evt.preventDefault();
  282. break;
  283. case 'touchend':
  284. if (this.touchSwipeState === null) {
  285. return;
  286. }
  287. var delta = 0;
  288. var dx = this.touchSwipeState.endX - this.touchSwipeState.startX;
  289. var dy = this.touchSwipeState.endY - this.touchSwipeState.startY;
  290. var absAngle = Math.abs(Math.atan2(dy, dx));
  291. if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD && (absAngle <= SWIPE_ANGLE_THRESHOLD || absAngle >= Math.PI - SWIPE_ANGLE_THRESHOLD)) {
  292. delta = dx;
  293. } else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD && Math.abs(absAngle - Math.PI / 2) <= SWIPE_ANGLE_THRESHOLD) {
  294. delta = dy;
  295. }
  296. if (delta > 0) {
  297. this._goToPreviousPage();
  298. } else if (delta < 0) {
  299. this._goToNextPage();
  300. }
  301. break;
  302. }
  303. }
  304. }, {
  305. key: '_addWindowListeners',
  306. value: function _addWindowListeners() {
  307. this.showControlsBind = this._showControls.bind(this);
  308. this.mouseDownBind = this._mouseDown.bind(this);
  309. this.mouseWheelBind = this._mouseWheel.bind(this);
  310. this.resetMouseScrollStateBind = this._resetMouseScrollState.bind(this);
  311. this.contextMenuBind = this._contextMenu.bind(this);
  312. this.touchSwipeBind = this._touchSwipe.bind(this);
  313. window.addEventListener('mousemove', this.showControlsBind);
  314. window.addEventListener('mousedown', this.mouseDownBind);
  315. window.addEventListener('wheel', this.mouseWheelBind);
  316. window.addEventListener('keydown', this.resetMouseScrollStateBind);
  317. window.addEventListener('contextmenu', this.contextMenuBind);
  318. window.addEventListener('touchstart', this.touchSwipeBind);
  319. window.addEventListener('touchmove', this.touchSwipeBind);
  320. window.addEventListener('touchend', this.touchSwipeBind);
  321. }
  322. }, {
  323. key: '_removeWindowListeners',
  324. value: function _removeWindowListeners() {
  325. window.removeEventListener('mousemove', this.showControlsBind);
  326. window.removeEventListener('mousedown', this.mouseDownBind);
  327. window.removeEventListener('wheel', this.mouseWheelBind);
  328. window.removeEventListener('keydown', this.resetMouseScrollStateBind);
  329. window.removeEventListener('contextmenu', this.contextMenuBind);
  330. window.removeEventListener('touchstart', this.touchSwipeBind);
  331. window.removeEventListener('touchmove', this.touchSwipeBind);
  332. window.removeEventListener('touchend', this.touchSwipeBind);
  333. delete this.showControlsBind;
  334. delete this.mouseDownBind;
  335. delete this.mouseWheelBind;
  336. delete this.resetMouseScrollStateBind;
  337. delete this.contextMenuBind;
  338. delete this.touchSwipeBind;
  339. }
  340. }, {
  341. key: '_fullscreenChange',
  342. value: function _fullscreenChange() {
  343. if (this.isFullscreen) {
  344. this._enter();
  345. } else {
  346. this._exit();
  347. }
  348. }
  349. }, {
  350. key: '_addFullscreenChangeListeners',
  351. value: function _addFullscreenChangeListeners() {
  352. this.fullscreenChangeBind = this._fullscreenChange.bind(this);
  353. window.addEventListener('fullscreenchange', this.fullscreenChangeBind);
  354. window.addEventListener('mozfullscreenchange', this.fullscreenChangeBind);
  355. window.addEventListener('webkitfullscreenchange', this.fullscreenChangeBind);
  356. window.addEventListener('MSFullscreenChange', this.fullscreenChangeBind);
  357. }
  358. }, {
  359. key: '_removeFullscreenChangeListeners',
  360. value: function _removeFullscreenChangeListeners() {
  361. window.removeEventListener('fullscreenchange', this.fullscreenChangeBind);
  362. window.removeEventListener('mozfullscreenchange', this.fullscreenChangeBind);
  363. window.removeEventListener('webkitfullscreenchange', this.fullscreenChangeBind);
  364. window.removeEventListener('MSFullscreenChange', this.fullscreenChangeBind);
  365. delete this.fullscreenChangeBind;
  366. }
  367. }, {
  368. key: 'isFullscreen',
  369. get: function get() {
  370. return !!(document.fullscreenElement || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement);
  371. }
  372. }]);
  373. return PDFPresentationMode;
  374. }();
  375. exports.PDFPresentationMode = PDFPresentationMode;