demo-viewer.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /**
  2. * @license
  3. * Copyright (C) 2014 KO GmbH <copyright@kogmbh.com>
  4. *
  5. * @licstart
  6. * The JavaScript code in this page is free software: you can redistribute it
  7. * and/or modify it under the terms of the GNU Affero General Public License
  8. * (GNU AGPL) as published by the Free Software Foundation, either version 3 of
  9. * the License, or (at your option) any later version.
  10. *
  11. * The code is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this code. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. * @licend
  20. * @source: http://www.webodf.org/
  21. * @source: https://github.com/kogmbh/WebODF/
  22. */
  23. // This code was created by pieces of Viewer.JS and can surely be reduced some more.
  24. // Idea is to not use Viewer.JS here, but to have custom code, for these reasons:
  25. // * the features of WebODF library should be shown, not Viewer.JS
  26. // * should also show as side-effect that custom UI is simple to do
  27. // * have another way the WebODF API is used
  28. function ODFViewer(url) {
  29. "use strict";
  30. // that should probably be provided by webodf
  31. function nsResolver(prefix) {
  32. var ns = {
  33. 'draw' : "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0",
  34. 'presentation' : "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0",
  35. 'text' : "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
  36. 'office' : "urn:oasis:names:tc:opendocument:xmlns:office:1.0"
  37. };
  38. return ns[prefix] || console.log('prefix [' + prefix + '] unknown.');
  39. }
  40. var self = this,
  41. odfCanvas = null,
  42. odfElement = null,
  43. root = null,
  44. documentType = null,
  45. kScrollbarPadding = 40,
  46. kDefaultScale = 'auto',
  47. initialized = false,
  48. isSlideshow = false,
  49. viewerElement = document.getElementById('viewer'),
  50. canvasContainer = document.getElementById('canvasContainer'),
  51. overlayNavigator = document.getElementById('overlayNavigator'),
  52. pageSwitcher = document.getElementById('toolbarLeft'),
  53. scaleSelector = document.getElementById('scaleSelect'),
  54. toolbarRight = document.getElementById('toolbarRight'),
  55. pages = [],
  56. currentPage = null,
  57. touchTimer;
  58. this.isPresentation = function () {
  59. return documentType === 'presentation';
  60. };
  61. this.getWidth = function () {
  62. return odfElement.clientWidth;
  63. };
  64. this.getHeight = function () {
  65. return odfElement.clientHeight;
  66. };
  67. this.fitToWidth = function (width) {
  68. odfCanvas.fitToWidth(width);
  69. };
  70. this.fitToHeight = function (height) {
  71. odfCanvas.fitToHeight(height);
  72. };
  73. this.fitToPage = function (width, height) {
  74. odfCanvas.fitToContainingElement(width, height);
  75. };
  76. this.fitSmart = function (width) {
  77. odfCanvas.fitSmart(width);
  78. };
  79. // return a list of tuples (pagename, pagenode)
  80. function getPages() {
  81. var pageNodes = Array.prototype.slice.call(root.getElementsByTagNameNS(nsResolver('draw'), 'page')),
  82. pages = [],
  83. i,
  84. tuple;
  85. for (i = 0; i < pageNodes.length; i += 1) {
  86. tuple = [
  87. pageNodes[i].getAttribute('draw:name'),
  88. pageNodes[i]
  89. ];
  90. pages.push(tuple);
  91. }
  92. return pages;
  93. };
  94. function selectScaleOption(value) {
  95. // Retrieve the options from the zoom level <select> element
  96. var options = scaleSelector.options,
  97. option,
  98. predefinedValueFound = false,
  99. i;
  100. for (i = 0; i < options.length; i += 1) {
  101. option = options[i];
  102. if (option.value !== value) {
  103. option.selected = false;
  104. continue;
  105. }
  106. option.selected = true;
  107. predefinedValueFound = true;
  108. }
  109. return predefinedValueFound;
  110. }
  111. function setScale(val, resetAutoSettings, noScroll) {
  112. if (val === self.getZoomLevel()) {
  113. return;
  114. }
  115. self.setZoomLevel(val);
  116. var event = document.createEvent('UIEvents');
  117. event.initUIEvent('scalechange', false, false, window, 0);
  118. event.scale = val;
  119. event.resetAutoSettings = resetAutoSettings;
  120. window.dispatchEvent(event);
  121. }
  122. function parseScale(value, resetAutoSettings, noScroll) {
  123. var scale,
  124. maxWidth,
  125. maxHeight;
  126. if (value === 'custom') {
  127. scale = parseFloat(document.getElementById('customScaleOption').textContent) / 100;
  128. } else {
  129. scale = parseFloat(value);
  130. }
  131. if (scale) {
  132. setScale(scale, true, noScroll);
  133. return;
  134. }
  135. maxWidth = canvasContainer.clientWidth - kScrollbarPadding;
  136. maxHeight = canvasContainer.clientHeight - kScrollbarPadding;
  137. switch (value) {
  138. case 'page-actual':
  139. setScale(1, resetAutoSettings, noScroll);
  140. break;
  141. case 'page-width':
  142. self.fitToWidth(maxWidth);
  143. break;
  144. case 'page-height':
  145. self.fitToHeight(maxHeight);
  146. break;
  147. case 'page-fit':
  148. self.fitToPage(maxWidth, maxHeight);
  149. break;
  150. case 'auto':
  151. if (self.isPresentation()) {
  152. self.fitToPage(maxWidth + kScrollbarPadding, maxHeight + kScrollbarPadding);
  153. } else {
  154. self.fitSmart(maxWidth);
  155. }
  156. break;
  157. }
  158. selectScaleOption(value);
  159. }
  160. /**
  161. * Shows the 'n'th page. If n is larger than the page count,
  162. * shows the last page. If n is less than 1, shows the first page.
  163. * @return {undefined}
  164. */
  165. this.showPage = function (n) {
  166. if (n <= 0) {
  167. n = 1;
  168. } else if (n > pages.length) {
  169. n = pages.length;
  170. }
  171. odfCanvas.showPage(n);
  172. currentPage = n;
  173. document.getElementById('pageNumber').value = currentPage;
  174. };
  175. /**
  176. * Shows the next page. If there is no subsequent page, does nothing.
  177. * @return {undefined}
  178. */
  179. this.showNextPage = function () {
  180. self.showPage(currentPage + 1);
  181. };
  182. /**
  183. * Shows the previous page. If there is no previous page, does nothing.
  184. * @return {undefined}
  185. */
  186. this.showPreviousPage = function () {
  187. self.showPage(currentPage - 1);
  188. };
  189. /**
  190. * Attempts to 'download' the file.
  191. * @return {undefined}
  192. */
  193. this.download = function () {
  194. var documentUrl = url.split('#')[0];
  195. documentUrl += '#viewer.action=download';
  196. window.open(documentUrl, '_parent');
  197. };
  198. /**
  199. * Gets the zoom level of the document
  200. * @return {!number}
  201. */
  202. this.getZoomLevel = function () {
  203. return odfCanvas.getZoomLevel();
  204. };
  205. /**
  206. * Set the zoom level of the document
  207. * @param {!number} value
  208. * @return {undefined}
  209. */
  210. this.setZoomLevel = function (value) {
  211. odfCanvas.setZoomLevel(value);
  212. };
  213. function showOverlayNavigator() {
  214. if (isSlideshow) {
  215. overlayNavigator.className = 'touched';
  216. window.clearTimeout(touchTimer);
  217. touchTimer = window.setTimeout(function () {
  218. overlayNavigator.className = '';
  219. }, 2000);
  220. }
  221. }
  222. function init() {
  223. var session,
  224. sessionController,
  225. sessionView,
  226. odtDocument,
  227. shadowCursor,
  228. selectionViewManager,
  229. caretManager,
  230. localMemberId = 'localuser',
  231. hyperlinkTooltipView,
  232. eventManager;
  233. odfElement = document.getElementById('canvas');
  234. odfCanvas = new odf.OdfCanvas(odfElement);
  235. odfCanvas.load(url);
  236. odfCanvas.addListener('statereadychange', function () {
  237. root = odfCanvas.odfContainer().rootElement;
  238. initialized = true;
  239. documentType = odfCanvas.odfContainer().getDocumentType(root);
  240. if (documentType === 'text') {
  241. odfCanvas.enableAnnotations(true, false);
  242. session = new ops.Session(odfCanvas);
  243. odtDocument = session.getOdtDocument();
  244. shadowCursor = new gui.ShadowCursor(odtDocument);
  245. sessionController = new gui.SessionController(session, localMemberId, shadowCursor, {});
  246. eventManager = sessionController.getEventManager();
  247. caretManager = new gui.CaretManager(sessionController, odfCanvas.getViewport());
  248. selectionViewManager = new gui.SelectionViewManager(gui.SvgSelectionView);
  249. sessionView = new gui.SessionView({
  250. caretAvatarsInitiallyVisible: false
  251. }, localMemberId, session, sessionController.getSessionConstraints(), caretManager, selectionViewManager);
  252. selectionViewManager.registerCursor(shadowCursor);
  253. hyperlinkTooltipView = new gui.HyperlinkTooltipView(odfCanvas,
  254. sessionController.getHyperlinkClickHandler().getModifier);
  255. eventManager.subscribe("mousemove", hyperlinkTooltipView.showTooltip);
  256. eventManager.subscribe("mouseout", hyperlinkTooltipView.hideTooltip);
  257. var op = new ops.OpAddMember();
  258. op.init({
  259. memberid: localMemberId,
  260. setProperties: {
  261. fillName: runtime.tr("Unknown Author"),
  262. color: "blue"
  263. }
  264. });
  265. session.enqueue([op]);
  266. sessionController.insertLocalCursor();
  267. }
  268. isSlideshow = self.isPresentation();
  269. if (isSlideshow) {
  270. // No padding for slideshows
  271. canvasContainer.style.padding = 0;
  272. // Show page nav controls only for presentations
  273. pageSwitcher.style.visibility = 'visible';
  274. }
  275. initialized = true;
  276. pages = getPages();
  277. document.getElementById('numPages').innerHTML = 'of ' + pages.length;
  278. self.showPage(1);
  279. // Set default scale
  280. parseScale(kDefaultScale);
  281. });
  282. document.getElementById('download').addEventListener('click', function () {
  283. self.download();
  284. });
  285. document.getElementById('previous').addEventListener('click', function () {
  286. self.showPreviousPage();
  287. });
  288. document.getElementById('next').addEventListener('click', function () {
  289. self.showNextPage();
  290. });
  291. document.getElementById('previousPage').addEventListener('click', function () {
  292. self.showPreviousPage();
  293. });
  294. document.getElementById('nextPage').addEventListener('click', function () {
  295. self.showNextPage();
  296. });
  297. document.getElementById('pageNumber').addEventListener('change', function () {
  298. self.showPage(this.value);
  299. });
  300. document.getElementById('scaleSelect').addEventListener('change', function () {
  301. parseScale(this.value);
  302. });
  303. canvasContainer.addEventListener('click', showOverlayNavigator);
  304. overlayNavigator.addEventListener('click', showOverlayNavigator);
  305. window.addEventListener('scalechange', function (evt) {
  306. var customScaleOption = document.getElementById('customScaleOption'),
  307. predefinedValueFound = selectScaleOption(String(evt.scale));
  308. customScaleOption.selected = false;
  309. if (!predefinedValueFound) {
  310. customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
  311. customScaleOption.selected = true;
  312. }
  313. }, true);
  314. window.addEventListener('resize', function (evt) {
  315. if (initialized &&
  316. (document.getElementById('pageWidthOption').selected ||
  317. document.getElementById('pageAutoOption').selected)) {
  318. parseScale(document.getElementById('scaleSelect').value);
  319. }
  320. showOverlayNavigator();
  321. });
  322. window.addEventListener('keydown', function (evt) {
  323. var key = evt.keyCode,
  324. shiftKey = evt.shiftKey;
  325. switch (key) {
  326. case 33: // pageUp
  327. case 38: // up
  328. case 37: // left
  329. self.showPreviousPage();
  330. break;
  331. case 34: // pageDown
  332. case 40: // down
  333. case 39: // right
  334. self.showNextPage();
  335. break;
  336. case 32: // space
  337. shiftKey ? self.showPreviousPage() : self.showNextPage();
  338. break;
  339. }
  340. });
  341. }
  342. init();
  343. }
  344. function loadDocument(url) {
  345. new ODFViewer(url);
  346. }