diff --git a/src/styles/components/_presentation.scss b/src/styles/components/_presentation.scss
index 00728fe2..140d3f13 100644
--- a/src/styles/components/_presentation.scss
+++ b/src/styles/components/_presentation.scss
@@ -78,6 +78,16 @@
display: block;
margin: 0 auto;
}
+ .odfcanvas {
+ user-select: none;
+ cursor: default;
+ }
+ .odfcontainer {
+ overflow: hidden;
+ display: none;
+ padding: 0;
+ margin: 0;
+ }
}
.presentation .overlaybar {
diff --git a/static/js/directives/directives.js b/static/js/directives/directives.js
index ef9bbb0a..e2798a8a 100644
--- a/static/js/directives/directives.js
+++ b/static/js/directives/directives.js
@@ -39,7 +39,8 @@ define([
'directives/contactrequest',
'directives/defaultdialog',
'directives/pdfcanvas',
- 'directives/presentation'], function(_, onEnter, onEscape, statusMessage, buddyList, buddyPicture, settings, chat, audioVideo, usability, audioLevel, fileInfo, screenshare, roomBar, socialShare, page, contactRequest, defaultDialog, pdfcanvas, presentation) {
+ 'directives/odfcanvas',
+ 'directives/presentation'], function(_, onEnter, onEscape, statusMessage, buddyList, buddyPicture, settings, chat, audioVideo, usability, audioLevel, fileInfo, screenshare, roomBar, socialShare, page, contactRequest, defaultDialog, pdfcanvas, odfcanvas, presentation) {
var directives = {
onEnter: onEnter,
@@ -60,6 +61,7 @@ define([
contactRequest: contactRequest,
defaultDialog: defaultDialog,
pdfcanvas: pdfcanvas,
+ odfcanvas: odfcanvas,
presentation: presentation
};
diff --git a/static/js/directives/odfcanvas.js b/static/js/directives/odfcanvas.js
new file mode 100644
index 00000000..a9f1882b
--- /dev/null
+++ b/static/js/directives/odfcanvas.js
@@ -0,0 +1,244 @@
+/*
+ * Spreed WebRTC.
+ * Copyright (C) 2013-2014 struktur AG
+ *
+ * This file is part of Spreed WebRTC.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+define(['require', 'underscore', 'jquery'], function(require, _, $) {
+
+ return ["$window", "$compile", "translation", "safeApply", function($window, $compile, translation, safeApply) {
+
+ var webodf = null;
+
+ var nsResolver = function(prefix) {
+ var ns = {
+ 'draw': "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0",
+ 'presentation': "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0",
+ 'text': "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
+ 'office': "urn:oasis:names:tc:opendocument:xmlns:office:1.0"
+ };
+ return ns[prefix] || console.log('prefix [' + prefix + '] unknown.');
+ }
+
+ var ODFCanvas_read = function(path, offset, length, callback) {
+ if (typeof path === "string") {
+ webodf.runtime.orig_read.call(webodf.runtime, path, offset, length, callback);
+ return;
+ }
+
+ var fp = path.file || path;
+ if (typeof URL !== "undefined" && URL.createObjectURL) {
+ var url = URL.createObjectURL(fp);
+ webodf.runtime.orig_read.call(webodf.runtime, url, offset, length, callback);
+ return;
+ }
+
+ console.error("TODO(fancycode): implement read for", path);
+ };
+
+ var ODFCanvas_getFileSize = function(path, callback) {
+ if (typeof path === "string") {
+ webodf.runtime.orig_getFileSize.call(webodf.runtime, path, callback);
+ return;
+ }
+
+ var size = path.size || path.info.size;
+ if (typeof size !== "undefined") {
+ callback(size);
+ return;
+ }
+
+ console.error("TODO(fancycode): implement getFileSize for", path);
+ callback(-1);
+ };
+
+ var controller = ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
+
+ var ODFCanvas = function(scope, container, canvasDom) {
+ this.scope = scope;
+ this.container = container;
+ this.canvasDom = canvasDom;
+ this.canvas = null;
+ this.odfcontainer = null;
+ this.maxPageNumber = -1;
+ this.currentPageNumber = -1;
+ this.pendingPageNumber = null;
+ };
+
+ ODFCanvas.prototype._close = function() {
+ if (this.canvas) {
+ this.canvas.destroy(function() {
+ // ignore callback
+ });
+ this.canvas = null;
+ }
+ this.odfcontainer = null;
+ this.maxPageNumber = -1;
+ this.currentPageNumber = -1;
+ this.pendingPageNumber = null;
+ };
+
+ ODFCanvas.prototype.close = function() {
+ this._close();
+ };
+
+ ODFCanvas.prototype.open = function(presentation) {
+ this.scope.$emit("presentationOpening", presentation);
+ presentation.open(_.bind(function(source) {
+ console.log("Loading ODF from", source);
+ this._openFile(source);
+ }, this));
+ };
+
+ ODFCanvas.prototype._doOpenFile = function(source) {
+ this.scope.$emit("presentationLoading", source);
+ this.container.hide();
+ this.canvas = new webodf.odf.OdfCanvas(this.canvasDom[0]);
+ this.canvas.load(source);
+ this.canvas.addListener("statereadychange", _.bind(function(odfcontainer) {
+ this.scope.$apply(_.bind(function(scope) {
+ this.odfcontainer = odfcontainer;
+ // pages only supported for presentations
+ var pages = odfcontainer.rootElement.getElementsByTagNameNS(nsResolver('draw'), 'page');
+ this.maxPageNumber = Math.max(1, pages.length);
+ this.currentPageNumber = -1;
+ console.log("ODF loaded", odfcontainer);
+ var odfDoc = {
+ numPages: this.maxPageNumber
+ };
+ scope.$emit("presentationLoaded", source, odfDoc);
+ if (this.pendingPageNumber !== null) {
+ this._showPage(this.pendingPageNumber);
+ this.pendingPageNumber = null;
+ }
+ }, this));
+ }, this));
+ };
+
+ ODFCanvas.prototype._openFile = function(source) {
+ if (webodf === null) {
+ // load webodf.js lazily
+ require(['webodf'], _.bind(function(webodf_) {
+ console.log("Using webodf.js " + webodf_.webodf.Version);
+
+ webodf = webodf_;
+
+ // monkey-patch IO functions
+ webodf.runtime.orig_read = webodf.runtime.read;
+ webodf.runtime.read = ODFCanvas_read;
+ webodf.runtime.orig_getFileSize = webodf.runtime.getFileSize;
+ webodf.runtime.getFileSize = ODFCanvas_getFileSize;
+
+ this.scope.$apply(_.bind(function(scope) {
+ this._doOpenFile(source);
+ }, this));
+ }, this));
+ } else {
+ this._doOpenFile(source);
+ }
+ };
+
+ ODFCanvas.prototype._showPage = function(page) {
+ if (page === this.currentPageNumber) {
+ return;
+ }
+
+ console.log("Showing page", page, "/", this.maxPageNumber);
+ this.scope.$emit("presentationPageLoading", page);
+ this.scope.$emit("presentationPageLoaded", page, null);
+ // actual rendering of first page must happen after the
+ // previously hidden DOM elements are visible again to
+ // make initial scaling work, so defer it
+ _.defer(_.bind(function() {
+ this.scope.$apply(_.bind(function() {
+ if (this.currentPageNumber === -1) {
+ this.container.show();
+ this.redrawPage();
+ }
+ this.currentPageNumber = page;
+ this.canvas.showPage(page);
+ this.scope.$emit("presentationPageRendering", page);
+ this.scope.$emit("presentationPageRendered", page, this.maxPageNumber);
+ }, this));
+ }, this));
+ };
+
+ ODFCanvas.prototype.redrawPage = function() {
+ if (this.canvas) {
+ this.canvas.fitSmart(this.container.width(), this.container.height());
+ }
+ };
+
+ ODFCanvas.prototype.showPage = function(page) {
+ if (page >= 1 && page <= this.maxPageNumber) {
+ if (!this.canvas) {
+ this.pendingPageNumber = page;
+ } else {
+ this._showPage(page);
+ }
+ }
+ };
+
+ var container = $($element);
+ var canvas = $($element).find(".odfcanvas");
+ var odfCanvas = new ODFCanvas($scope, container, canvas);
+
+ $scope.$watch("currentPresentation", function(presentation, previousPresentation) {
+ if (presentation) {
+ safeApply($scope, function(scope) {
+ odfCanvas.open(presentation);
+ });
+ } else {
+ if (previousPresentation) {
+ previousPresentation.close();
+ }
+ odfCanvas.close();
+ }
+ });
+
+ $scope.$on("$destroy", function() {
+ odfCanvas.close();
+ odfCanvas = null;
+ });
+
+ $scope.$watch("currentPageNumber", function(page, oldValue) {
+ if (page === oldValue) {
+ // no change
+ return;
+ }
+
+ odfCanvas.showPage(page);
+ });
+
+ $($window).on("resize", function() {
+ $scope.$apply(function(scope) {
+ odfCanvas.redrawPage();
+ });
+ });
+
+ }];
+
+ return {
+ restrict: 'E',
+ replace: true,
+ template: '
',
+ controller: controller
+ };
+
+ }];
+
+});
diff --git a/static/js/directives/presentation.js b/static/js/directives/presentation.js
index 5816bfcd..95616236 100644
--- a/static/js/directives/presentation.js
+++ b/static/js/directives/presentation.js
@@ -24,7 +24,17 @@ define(['jquery', 'underscore', 'text!partials/presentation.html', 'bigscreen'],
var SUPPORTED_TYPES = {
// rendered by pdfcanvas directive
- "application/pdf": "pdf"
+ "application/pdf": "pdf",
+ // rendered by odfcanvas directive
+ // TODO(fancycode): check which formats really work, allow all odf for now
+ "application/vnd.oasis.opendocument.text": "odf",
+ "application/vnd.oasis.opendocument.spreadsheet": "odf",
+ "application/vnd.oasis.opendocument.presentation": "odf",
+ "application/vnd.oasis.opendocument.graphics": "odf",
+ "application/vnd.oasis.opendocument.chart": "odf",
+ "application/vnd.oasis.opendocument.formula": "odf",
+ "application/vnd.oasis.opendocument.image": "odf",
+ "application/vnd.oasis.opendocument.text-master": "odf"
};
var BasePresentation = function(scope, fileInfo, token) {
diff --git a/static/partials/presentation.html b/static/partials/presentation.html
index bef74ead..07288e61 100644
--- a/static/partials/presentation.html
+++ b/static/partials/presentation.html
@@ -17,6 +17,7 @@