2
0

pdf_presentation_mode.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  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(_ref) {
  33. var _this = this;
  34. var container = _ref.container,
  35. _ref$viewer = _ref.viewer,
  36. viewer = _ref$viewer === undefined ? null : _ref$viewer,
  37. pdfViewer = _ref.pdfViewer,
  38. eventBus = _ref.eventBus,
  39. _ref$contextMenuItems = _ref.contextMenuItems,
  40. contextMenuItems = _ref$contextMenuItems === undefined ? null : _ref$contextMenuItems;
  41. _classCallCheck(this, PDFPresentationMode);
  42. this.container = container;
  43. this.viewer = viewer || container.firstElementChild;
  44. this.pdfViewer = pdfViewer;
  45. this.eventBus = eventBus;
  46. this.active = false;
  47. this.args = null;
  48. this.contextMenuOpen = false;
  49. this.mouseScrollTimeStamp = 0;
  50. this.mouseScrollDelta = 0;
  51. this.touchSwipeState = null;
  52. if (contextMenuItems) {
  53. contextMenuItems.contextFirstPage.addEventListener('click', function () {
  54. _this.contextMenuOpen = false;
  55. _this.eventBus.dispatch('firstpage');
  56. });
  57. contextMenuItems.contextLastPage.addEventListener('click', function () {
  58. _this.contextMenuOpen = false;
  59. _this.eventBus.dispatch('lastpage');
  60. });
  61. contextMenuItems.contextPageRotateCw.addEventListener('click', function () {
  62. _this.contextMenuOpen = false;
  63. _this.eventBus.dispatch('rotatecw');
  64. });
  65. contextMenuItems.contextPageRotateCcw.addEventListener('click', function () {
  66. _this.contextMenuOpen = false;
  67. _this.eventBus.dispatch('rotateccw');
  68. });
  69. }
  70. }
  71. _createClass(PDFPresentationMode, [{
  72. key: 'request',
  73. value: function request() {
  74. if (this.switchInProgress || this.active || !this.viewer.hasChildNodes()) {
  75. return false;
  76. }
  77. this._addFullscreenChangeListeners();
  78. this._setSwitchInProgress();
  79. this._notifyStateChange();
  80. if (this.container.requestFullscreen) {
  81. this.container.requestFullscreen();
  82. } else if (this.container.mozRequestFullScreen) {
  83. this.container.mozRequestFullScreen();
  84. } else if (this.container.webkitRequestFullscreen) {
  85. this.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  86. } else if (this.container.msRequestFullscreen) {
  87. this.container.msRequestFullscreen();
  88. } else {
  89. return false;
  90. }
  91. this.args = {
  92. page: this.pdfViewer.currentPageNumber,
  93. previousScale: this.pdfViewer.currentScaleValue
  94. };
  95. return true;
  96. }
  97. }, {
  98. key: '_mouseWheel',
  99. value: function _mouseWheel(evt) {
  100. if (!this.active) {
  101. return;
  102. }
  103. evt.preventDefault();
  104. var delta = (0, _ui_utils.normalizeWheelEventDelta)(evt);
  105. var currentTime = new Date().getTime();
  106. var storedTime = this.mouseScrollTimeStamp;
  107. if (currentTime > storedTime && currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
  108. return;
  109. }
  110. if (this.mouseScrollDelta > 0 && delta < 0 || this.mouseScrollDelta < 0 && delta > 0) {
  111. this._resetMouseScrollState();
  112. }
  113. this.mouseScrollDelta += delta;
  114. if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) {
  115. var totalDelta = this.mouseScrollDelta;
  116. this._resetMouseScrollState();
  117. var success = totalDelta > 0 ? this._goToPreviousPage() : this._goToNextPage();
  118. if (success) {
  119. this.mouseScrollTimeStamp = currentTime;
  120. }
  121. }
  122. }
  123. }, {
  124. key: '_goToPreviousPage',
  125. value: function _goToPreviousPage() {
  126. var page = this.pdfViewer.currentPageNumber;
  127. if (page <= 1) {
  128. return false;
  129. }
  130. this.pdfViewer.currentPageNumber = page - 1;
  131. return true;
  132. }
  133. }, {
  134. key: '_goToNextPage',
  135. value: function _goToNextPage() {
  136. var page = this.pdfViewer.currentPageNumber;
  137. if (page >= this.pdfViewer.pagesCount) {
  138. return false;
  139. }
  140. this.pdfViewer.currentPageNumber = page + 1;
  141. return true;
  142. }
  143. }, {
  144. key: '_notifyStateChange',
  145. value: function _notifyStateChange() {
  146. this.eventBus.dispatch('presentationmodechanged', {
  147. source: this,
  148. active: this.active,
  149. switchInProgress: !!this.switchInProgress
  150. });
  151. }
  152. }, {
  153. key: '_setSwitchInProgress',
  154. value: function _setSwitchInProgress() {
  155. var _this2 = this;
  156. if (this.switchInProgress) {
  157. clearTimeout(this.switchInProgress);
  158. }
  159. this.switchInProgress = setTimeout(function () {
  160. _this2._removeFullscreenChangeListeners();
  161. delete _this2.switchInProgress;
  162. _this2._notifyStateChange();
  163. }, DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS);
  164. }
  165. }, {
  166. key: '_resetSwitchInProgress',
  167. value: function _resetSwitchInProgress() {
  168. if (this.switchInProgress) {
  169. clearTimeout(this.switchInProgress);
  170. delete this.switchInProgress;
  171. }
  172. }
  173. }, {
  174. key: '_enter',
  175. value: function _enter() {
  176. var _this3 = this;
  177. this.active = true;
  178. this._resetSwitchInProgress();
  179. this._notifyStateChange();
  180. this.container.classList.add(ACTIVE_SELECTOR);
  181. setTimeout(function () {
  182. _this3.pdfViewer.currentPageNumber = _this3.args.page;
  183. _this3.pdfViewer.currentScaleValue = 'page-fit';
  184. }, 0);
  185. this._addWindowListeners();
  186. this._showControls();
  187. this.contextMenuOpen = false;
  188. this.container.setAttribute('contextmenu', 'viewerContextMenu');
  189. window.getSelection().removeAllRanges();
  190. }
  191. }, {
  192. key: '_exit',
  193. value: function _exit() {
  194. var _this4 = this;
  195. var page = this.pdfViewer.currentPageNumber;
  196. this.container.classList.remove(ACTIVE_SELECTOR);
  197. setTimeout(function () {
  198. _this4.active = false;
  199. _this4._removeFullscreenChangeListeners();
  200. _this4._notifyStateChange();
  201. _this4.pdfViewer.currentScaleValue = _this4.args.previousScale;
  202. _this4.pdfViewer.currentPageNumber = page;
  203. _this4.args = null;
  204. }, 0);
  205. this._removeWindowListeners();
  206. this._hideControls();
  207. this._resetMouseScrollState();
  208. this.container.removeAttribute('contextmenu');
  209. this.contextMenuOpen = false;
  210. }
  211. }, {
  212. key: '_mouseDown',
  213. value: function _mouseDown(evt) {
  214. if (this.contextMenuOpen) {
  215. this.contextMenuOpen = false;
  216. evt.preventDefault();
  217. return;
  218. }
  219. if (evt.button === 0) {
  220. var isInternalLink = evt.target.href && evt.target.classList.contains('internalLink');
  221. if (!isInternalLink) {
  222. evt.preventDefault();
  223. if (evt.shiftKey) {
  224. this._goToPreviousPage();
  225. } else {
  226. this._goToNextPage();
  227. }
  228. }
  229. }
  230. }
  231. }, {
  232. key: '_contextMenu',
  233. value: function _contextMenu() {
  234. this.contextMenuOpen = true;
  235. }
  236. }, {
  237. key: '_showControls',
  238. value: function _showControls() {
  239. var _this5 = this;
  240. if (this.controlsTimeout) {
  241. clearTimeout(this.controlsTimeout);
  242. } else {
  243. this.container.classList.add(CONTROLS_SELECTOR);
  244. }
  245. this.controlsTimeout = setTimeout(function () {
  246. _this5.container.classList.remove(CONTROLS_SELECTOR);
  247. delete _this5.controlsTimeout;
  248. }, DELAY_BEFORE_HIDING_CONTROLS);
  249. }
  250. }, {
  251. key: '_hideControls',
  252. value: function _hideControls() {
  253. if (!this.controlsTimeout) {
  254. return;
  255. }
  256. clearTimeout(this.controlsTimeout);
  257. this.container.classList.remove(CONTROLS_SELECTOR);
  258. delete this.controlsTimeout;
  259. }
  260. }, {
  261. key: '_resetMouseScrollState',
  262. value: function _resetMouseScrollState() {
  263. this.mouseScrollTimeStamp = 0;
  264. this.mouseScrollDelta = 0;
  265. }
  266. }, {
  267. key: '_touchSwipe',
  268. value: function _touchSwipe(evt) {
  269. if (!this.active) {
  270. return;
  271. }
  272. if (evt.touches.length > 1) {
  273. this.touchSwipeState = null;
  274. return;
  275. }
  276. switch (evt.type) {
  277. case 'touchstart':
  278. this.touchSwipeState = {
  279. startX: evt.touches[0].pageX,
  280. startY: evt.touches[0].pageY,
  281. endX: evt.touches[0].pageX,
  282. endY: evt.touches[0].pageY
  283. };
  284. break;
  285. case 'touchmove':
  286. if (this.touchSwipeState === null) {
  287. return;
  288. }
  289. this.touchSwipeState.endX = evt.touches[0].pageX;
  290. this.touchSwipeState.endY = evt.touches[0].pageY;
  291. evt.preventDefault();
  292. break;
  293. case 'touchend':
  294. if (this.touchSwipeState === null) {
  295. return;
  296. }
  297. var delta = 0;
  298. var dx = this.touchSwipeState.endX - this.touchSwipeState.startX;
  299. var dy = this.touchSwipeState.endY - this.touchSwipeState.startY;
  300. var absAngle = Math.abs(Math.atan2(dy, dx));
  301. if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD && (absAngle <= SWIPE_ANGLE_THRESHOLD || absAngle >= Math.PI - SWIPE_ANGLE_THRESHOLD)) {
  302. delta = dx;
  303. } else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD && Math.abs(absAngle - Math.PI / 2) <= SWIPE_ANGLE_THRESHOLD) {
  304. delta = dy;
  305. }
  306. if (delta > 0) {
  307. this._goToPreviousPage();
  308. } else if (delta < 0) {
  309. this._goToNextPage();
  310. }
  311. break;
  312. }
  313. }
  314. }, {
  315. key: '_addWindowListeners',
  316. value: function _addWindowListeners() {
  317. this.showControlsBind = this._showControls.bind(this);
  318. this.mouseDownBind = this._mouseDown.bind(this);
  319. this.mouseWheelBind = this._mouseWheel.bind(this);
  320. this.resetMouseScrollStateBind = this._resetMouseScrollState.bind(this);
  321. this.contextMenuBind = this._contextMenu.bind(this);
  322. this.touchSwipeBind = this._touchSwipe.bind(this);
  323. window.addEventListener('mousemove', this.showControlsBind);
  324. window.addEventListener('mousedown', this.mouseDownBind);
  325. window.addEventListener('wheel', this.mouseWheelBind);
  326. window.addEventListener('keydown', this.resetMouseScrollStateBind);
  327. window.addEventListener('contextmenu', this.contextMenuBind);
  328. window.addEventListener('touchstart', this.touchSwipeBind);
  329. window.addEventListener('touchmove', this.touchSwipeBind);
  330. window.addEventListener('touchend', this.touchSwipeBind);
  331. }
  332. }, {
  333. key: '_removeWindowListeners',
  334. value: function _removeWindowListeners() {
  335. window.removeEventListener('mousemove', this.showControlsBind);
  336. window.removeEventListener('mousedown', this.mouseDownBind);
  337. window.removeEventListener('wheel', this.mouseWheelBind);
  338. window.removeEventListener('keydown', this.resetMouseScrollStateBind);
  339. window.removeEventListener('contextmenu', this.contextMenuBind);
  340. window.removeEventListener('touchstart', this.touchSwipeBind);
  341. window.removeEventListener('touchmove', this.touchSwipeBind);
  342. window.removeEventListener('touchend', this.touchSwipeBind);
  343. delete this.showControlsBind;
  344. delete this.mouseDownBind;
  345. delete this.mouseWheelBind;
  346. delete this.resetMouseScrollStateBind;
  347. delete this.contextMenuBind;
  348. delete this.touchSwipeBind;
  349. }
  350. }, {
  351. key: '_fullscreenChange',
  352. value: function _fullscreenChange() {
  353. if (this.isFullscreen) {
  354. this._enter();
  355. } else {
  356. this._exit();
  357. }
  358. }
  359. }, {
  360. key: '_addFullscreenChangeListeners',
  361. value: function _addFullscreenChangeListeners() {
  362. this.fullscreenChangeBind = this._fullscreenChange.bind(this);
  363. window.addEventListener('fullscreenchange', this.fullscreenChangeBind);
  364. window.addEventListener('mozfullscreenchange', this.fullscreenChangeBind);
  365. window.addEventListener('webkitfullscreenchange', this.fullscreenChangeBind);
  366. window.addEventListener('MSFullscreenChange', this.fullscreenChangeBind);
  367. }
  368. }, {
  369. key: '_removeFullscreenChangeListeners',
  370. value: function _removeFullscreenChangeListeners() {
  371. window.removeEventListener('fullscreenchange', this.fullscreenChangeBind);
  372. window.removeEventListener('mozfullscreenchange', this.fullscreenChangeBind);
  373. window.removeEventListener('webkitfullscreenchange', this.fullscreenChangeBind);
  374. window.removeEventListener('MSFullscreenChange', this.fullscreenChangeBind);
  375. delete this.fullscreenChangeBind;
  376. }
  377. }, {
  378. key: 'isFullscreen',
  379. get: function get() {
  380. return !!(document.fullscreenElement || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement);
  381. }
  382. }]);
  383. return PDFPresentationMode;
  384. }();
  385. exports.PDFPresentationMode = PDFPresentationMode;