Refactor odfeditor plugin so it works without temp files (#4307)
- Update WebODF to 0.5.4 (with compressed responses support) - Don't use temporary files for better security and multi-server scenarious support
This commit is contained in:
parent
79e07cc1d6
commit
6bbad6f15e
9 changed files with 1225 additions and 2066 deletions
|
@ -1,43 +1,67 @@
|
||||||
/**
|
/**
|
||||||
* @license
|
|
||||||
* Copyright (C) 2012 KO GmbH <copyright@kogmbh.com>
|
* Copyright (C) 2012 KO GmbH <copyright@kogmbh.com>
|
||||||
*
|
*
|
||||||
* @licstart
|
* @licstart
|
||||||
* The JavaScript code in this page is free software: you can redistribute it
|
* This file is part of WebODF.
|
||||||
* and/or modify it under the terms of the GNU Affero General Public License
|
|
||||||
* (GNU AGPL) as published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version. The code is distributed
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU AGPL for more details.
|
|
||||||
*
|
*
|
||||||
* As additional permission under GNU AGPL version 3 section 7, you
|
* WebODF is free software: you can redistribute it and/or modify it
|
||||||
* may distribute non-source (e.g., minimized or compacted) forms of
|
* under the terms of the GNU Affero General Public License (GNU AGPL)
|
||||||
* that code without the copy of the GNU GPL normally required by
|
* as published by the Free Software Foundation, either version 3 of
|
||||||
* section 4, provided you include this license notice and a URL
|
* the License, or (at your option) any later version.
|
||||||
* through which recipients can access the Corresponding Source.
|
|
||||||
*
|
*
|
||||||
* As a special exception to the AGPL, any HTML file which merely makes function
|
* WebODF is distributed in the hope that it will be useful, but
|
||||||
* calls to this code, and for that purpose includes it by reference shall be
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* deemed a separate work for copyright law purposes. In addition, the copyright
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* holders of this code give you permission to combine this code with free
|
* GNU Affero General Public License for more details.
|
||||||
* software libraries that are released under the GNU LGPL. You may copy and
|
|
||||||
* distribute such a system following the terms of the GNU AGPL for this code
|
|
||||||
* and the LGPL for the libraries. If you modify this code, you may extend this
|
|
||||||
* exception to your version of the code, but you are not obligated to do so.
|
|
||||||
* If you do not wish to do so, delete this exception statement from your
|
|
||||||
* version.
|
|
||||||
*
|
*
|
||||||
* This license applies to this entire compilation.
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with WebODF. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* @licend
|
* @licend
|
||||||
|
*
|
||||||
* @source: http://www.webodf.org/
|
* @source: http://www.webodf.org/
|
||||||
* @source: http://gitorious.org/webodf/webodf/
|
* @source: https://github.com/kogmbh/WebODF/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*global runtime, document, odf, console*/
|
/*global runtime, document, odf, gui, console, webodf*/
|
||||||
|
|
||||||
function ODFViewerPlugin() {
|
function ODFViewerPlugin() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
function init(callback) {
|
||||||
|
/*
|
||||||
|
var lib = document.createElement('script'),
|
||||||
|
pluginCSS;
|
||||||
|
|
||||||
|
lib.async = false;
|
||||||
|
lib.src = './webodf.js';
|
||||||
|
lib.type = 'text/javascript';
|
||||||
|
lib.onload = function () {
|
||||||
|
*/
|
||||||
|
runtime.loadClass('gui.HyperlinkClickHandler');
|
||||||
|
runtime.loadClass('odf.OdfCanvas');
|
||||||
|
runtime.loadClass('ops.Session');
|
||||||
|
runtime.loadClass('gui.CaretManager');
|
||||||
|
runtime.loadClass("gui.HyperlinkTooltipView");
|
||||||
|
runtime.loadClass('gui.SessionController');
|
||||||
|
runtime.loadClass('gui.SvgSelectionView');
|
||||||
|
runtime.loadClass('gui.SelectionViewManager');
|
||||||
|
runtime.loadClass('gui.ShadowCursor');
|
||||||
|
runtime.loadClass('gui.SessionView');
|
||||||
|
|
||||||
|
callback();
|
||||||
|
/*
|
||||||
|
};
|
||||||
|
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(lib);
|
||||||
|
|
||||||
|
pluginCSS = document.createElement('link');
|
||||||
|
pluginCSS.setAttribute("rel", "stylesheet");
|
||||||
|
pluginCSS.setAttribute("type", "text/css");
|
||||||
|
pluginCSS.setAttribute("href", "./ODFViewerPlugin.css");
|
||||||
|
document.head.appendChild(pluginCSS);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
// that should probably be provided by webodf
|
// that should probably be provided by webodf
|
||||||
function nsResolver(prefix) {
|
function nsResolver(prefix) {
|
||||||
var ns = {
|
var ns = {
|
||||||
|
@ -50,6 +74,8 @@ function ODFViewerPlugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this,
|
var self = this,
|
||||||
|
pluginName = "WebODF",
|
||||||
|
pluginURL = "http://webodf.org",
|
||||||
odfCanvas = null,
|
odfCanvas = null,
|
||||||
odfElement = null,
|
odfElement = null,
|
||||||
initialized = false,
|
initialized = false,
|
||||||
|
@ -59,18 +85,61 @@ function ODFViewerPlugin() {
|
||||||
currentPage = null;
|
currentPage = null;
|
||||||
|
|
||||||
this.initialize = function (viewerElement, documentUrl) {
|
this.initialize = function (viewerElement, documentUrl) {
|
||||||
odfElement = document.getElementById('canvas');
|
// If the URL has a fragment (#...), try to load the file it represents
|
||||||
odfCanvas = new odf.OdfCanvas(odfElement);
|
init(function () {
|
||||||
odfCanvas.load(documentUrl);
|
var session,
|
||||||
|
sessionController,
|
||||||
|
sessionView,
|
||||||
|
odtDocument,
|
||||||
|
shadowCursor,
|
||||||
|
selectionViewManager,
|
||||||
|
caretManager,
|
||||||
|
localMemberId = 'localuser',
|
||||||
|
hyperlinkTooltipView,
|
||||||
|
eventManager;
|
||||||
|
|
||||||
odfCanvas.addListener('statereadychange', function () {
|
odfElement = document.getElementById('canvas');
|
||||||
root = odfCanvas.odfContainer().rootElement;
|
odfCanvas = new odf.OdfCanvas(odfElement);
|
||||||
initialized = true;
|
odfCanvas.load(documentUrl);
|
||||||
documentType = odfCanvas.odfContainer().getDocumentType(root);
|
|
||||||
if (documentType === 'text' && odfCanvas.enableAnnotations) {
|
odfCanvas.addListener('statereadychange', function () {
|
||||||
odfCanvas.enableAnnotations(true);
|
root = odfCanvas.odfContainer().rootElement;
|
||||||
}
|
initialized = true;
|
||||||
self.onLoad();
|
documentType = odfCanvas.odfContainer().getDocumentType(root);
|
||||||
|
|
||||||
|
if (documentType === 'text') {
|
||||||
|
odfCanvas.enableAnnotations(true, false);
|
||||||
|
|
||||||
|
session = new ops.Session(odfCanvas);
|
||||||
|
odtDocument = session.getOdtDocument();
|
||||||
|
shadowCursor = new gui.ShadowCursor(odtDocument);
|
||||||
|
sessionController = new gui.SessionController(session, localMemberId, shadowCursor, {});
|
||||||
|
eventManager = sessionController.getEventManager();
|
||||||
|
caretManager = new gui.CaretManager(sessionController, odfCanvas.getViewport());
|
||||||
|
selectionViewManager = new gui.SelectionViewManager(gui.SvgSelectionView);
|
||||||
|
sessionView = new gui.SessionView({
|
||||||
|
caretAvatarsInitiallyVisible: false
|
||||||
|
}, localMemberId, session, sessionController.getSessionConstraints(), caretManager, selectionViewManager);
|
||||||
|
selectionViewManager.registerCursor(shadowCursor);
|
||||||
|
hyperlinkTooltipView = new gui.HyperlinkTooltipView(odfCanvas,
|
||||||
|
sessionController.getHyperlinkClickHandler().getModifier);
|
||||||
|
eventManager.subscribe("mousemove", hyperlinkTooltipView.showTooltip);
|
||||||
|
eventManager.subscribe("mouseout", hyperlinkTooltipView.hideTooltip);
|
||||||
|
|
||||||
|
var op = new ops.OpAddMember();
|
||||||
|
op.init({
|
||||||
|
memberid: localMemberId,
|
||||||
|
setProperties: {
|
||||||
|
fillName: runtime.tr("Unknown Author"),
|
||||||
|
color: "blue"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
session.enqueue([op]);
|
||||||
|
sessionController.insertLocalCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onLoad();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -133,4 +202,23 @@ function ODFViewerPlugin() {
|
||||||
odfCanvas.showPage(n);
|
odfCanvas.showPage(n);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.getPluginName = function () {
|
||||||
|
return pluginName;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getPluginVersion = function () {
|
||||||
|
var version;
|
||||||
|
|
||||||
|
if (String(typeof webodf) !== "undefined") {
|
||||||
|
version = webodf.Version;
|
||||||
|
} else {
|
||||||
|
version = "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
return version;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getPluginURL = function () {
|
||||||
|
return pluginURL;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,5 @@ by Tobias Hintze. See http://webodf.org for more information.
|
||||||
|
|
||||||
INSTALLATION
|
INSTALLATION
|
||||||
------------
|
------------
|
||||||
Make the the folder 'files' in this directory writeable for the webserver.
|
|
||||||
It is used to temporarily store attachment files. Also make sure in the
|
|
||||||
webserver configuraton that this directory is not browsable. For Apache
|
|
||||||
webservers the included .htaccess file should already do the job.
|
|
||||||
|
|
||||||
Add 'odfviewer' to the list of plugins in the config/main.inc.php file
|
Add 'odfviewer' to the list of plugins in the config/main.inc.php file
|
||||||
of your Roundcube installation.
|
of your Roundcube installation.
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"description": "Open Document Viewer plugin",
|
"description": "Open Document Viewer plugin",
|
||||||
"homepage": "http://git.kolab.org/roundcubemail-plugins-kolab/",
|
"homepage": "http://git.kolab.org/roundcubemail-plugins-kolab/",
|
||||||
"license": "AGPLv3",
|
"license": "AGPLv3",
|
||||||
"version": "3.2.3",
|
"version": "3.3.0",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Thomas Bruederli",
|
"name": "Thomas Bruederli",
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<IfModule mod_deflate.c>
|
|
||||||
SetOutputFilter NONE
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
Options -Indexes
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
<html dir="ltr" lang="en-US">
|
<html dir="ltr" lang="en-US">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"/>
|
||||||
<title>Roundcube WebODF Viewer</title>
|
<title>Roundcube WebODF Viewer</title>
|
||||||
<link rel="stylesheet" type="text/css" href="%%DOCROOT%%viewer.css"/>
|
<link rel="stylesheet" type="text/css" href="%%viewer.css%%"/>
|
||||||
<script type="text/javascript" src="%%DOCROOT%%viewer.js" charset="utf-8"></script>
|
<script type="text/javascript" src="%%viewer.js%%" charset="utf-8"></script>
|
||||||
<script type="text/javascript" src="%%DOCROOT%%ODFViewerPlugin.js" charset="utf-8"></script>
|
<script type="text/javascript" src="%%ODFViewerPlugin.js%%" charset="utf-8"></script>
|
||||||
<script type="text/javascript" src="%%DOCROOT%%webodf.js" charset="utf-8"></script>
|
<script type="text/javascript" src="%%webodf.js%%" charset="utf-8"></script>
|
||||||
<script type="text/javascript" charset="utf-8">
|
<script type="text/javascript" charset="utf-8">
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,74 +35,74 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function init() {
|
window.onload = function () {
|
||||||
viewer = new Viewer(new ODFViewerPlugin(), '%%DOCURL%%');
|
var viewer = new Viewer(new ODFViewerPlugin(), %%PARAMS%%);
|
||||||
}
|
};
|
||||||
window.setTimeout(init, 0);
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="viewer">
|
<div id="viewer">
|
||||||
<div id="titlebar">
|
<div id="titlebar">
|
||||||
<div id="documentName"></div>
|
<div id="documentName"></div>
|
||||||
<div id="toolbarRight">
|
<div id="toolbarRight">
|
||||||
<button id="presentation" class="toolbarButton presentation" title="Presentation"></button>
|
<button id="presentation" class="toolbarButton presentation" title="Presentation"></button>
|
||||||
<button id="fullscreen" class="toolbarButton fullscreen" title="Fullscreen"></button>
|
<button id="fullscreen" class="toolbarButton fullscreen" title="Fullscreen"></button>
|
||||||
<button id="download" class="toolbarButton download" title="Download"></button>
|
<button id="download" class="toolbarButton download" title="Download"></button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="toolbarContainer">
|
|
||||||
<div id="toolbar">
|
|
||||||
<div id="toolbarLeft">
|
|
||||||
<div id="navButtons" class="splitToolbarButton">
|
|
||||||
<button id="previous" class="toolbarButton pageUp" title="Previous Page"></button>
|
|
||||||
<div class="splitToolbarButtonSeparator"></div>
|
|
||||||
<button id="next" class="toolbarButton pageDown" title="Next Page"></button>
|
|
||||||
</div>
|
|
||||||
<label id="pageNumberLabel" class="toolbarLabel" for="pageNumber">Page:</label>
|
|
||||||
<input type="number" id="pageNumber" class="toolbarField pageNumber"></input>
|
|
||||||
<span id="numPages" class="toolbarLabel"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="toolbarMiddleContainer" class="outerCenter">
|
</div>
|
||||||
<div id="toolbarMiddle" class="innerCenter">
|
<div id="toolbarContainer">
|
||||||
<div id = 'zoomButtons' class="splitToolbarButton">
|
<div id="toolbar">
|
||||||
<button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out"></button>
|
<div id="toolbarLeft">
|
||||||
|
<div id="navButtons" class="splitToolbarButton">
|
||||||
|
<button id="previous" class="toolbarButton pageUp" title="Previous Page"></button>
|
||||||
<div class="splitToolbarButtonSeparator"></div>
|
<div class="splitToolbarButtonSeparator"></div>
|
||||||
<button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In"></button>
|
<button id="next" class="toolbarButton pageDown" title="Next Page"></button>
|
||||||
</div>
|
</div>
|
||||||
<span id="scaleSelectContainer" class="dropdownToolbarButton">
|
<label id="pageNumberLabel" class="toolbarLabel" for="pageNumber">Page:</label>
|
||||||
<select id="scaleSelect" title="Zoom" oncontextmenu="return false;">
|
<input type="number" id="pageNumber" class="toolbarField pageNumber"/>
|
||||||
<option id="pageAutoOption" value="auto" selected>Automatic</option>
|
<span id="numPages" class="toolbarLabel"></span>
|
||||||
<option id="pageActualOption" value="page-actual">Actual Size</option>
|
</div>
|
||||||
<option id="pageWidthOption" value="page-width">Full Width</option>
|
<div id="toolbarMiddleContainer" class="outerCenter">
|
||||||
<option id="customScaleOption" value="custom"></option>
|
<div id="toolbarMiddle" class="innerCenter">
|
||||||
<option value="0.5">50%</option>
|
<div id = 'zoomButtons' class="splitToolbarButton">
|
||||||
<option value="0.75">75%</option>
|
<button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out"></button>
|
||||||
<option value="1">100%</option>
|
<div class="splitToolbarButtonSeparator"></div>
|
||||||
<option value="1.25">125%</option>
|
<button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In"></button>
|
||||||
<option value="1.5">150%</option>
|
</div>
|
||||||
<option value="2">200%</option>
|
<span id="scaleSelectContainer" class="dropdownToolbarButton">
|
||||||
</select>
|
<select id="scaleSelect" title="Zoom" oncontextmenu="return false;">
|
||||||
</span>
|
<option id="pageAutoOption" value="auto" selected>Automatic</option>
|
||||||
<div id="sliderContainer">
|
<option id="pageActualOption" value="page-actual">Actual Size</option>
|
||||||
<div id="slider"></div>
|
<option id="pageWidthOption" value="page-width">Full Width</option>
|
||||||
|
<option id="customScaleOption" value="custom"> </option>
|
||||||
|
<option value="0.5">50%</option>
|
||||||
|
<option value="0.75">75%</option>
|
||||||
|
<option value="1">100%</option>
|
||||||
|
<option value="1.25">125%</option>
|
||||||
|
<option value="1.5">150%</option>
|
||||||
|
<option value="2">200%</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<div id="sliderContainer">
|
||||||
|
<div id="slider"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="canvasContainer">
|
||||||
|
<div id="canvas"></div>
|
||||||
|
</div>
|
||||||
|
<div id="overlayNavigator">
|
||||||
|
<div id="previousPage"></div>
|
||||||
|
<div id="nextPage"></div>
|
||||||
|
</div>
|
||||||
|
<div id="overlayCloseButton">
|
||||||
|
✖
|
||||||
|
</div>
|
||||||
|
<div id="dialogOverlay"></div>
|
||||||
|
<div id="blanked"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="canvasContainer">
|
|
||||||
<div id="canvas"></div>
|
|
||||||
</div>
|
|
||||||
<div id="overlayNavigator">
|
|
||||||
<div id="previousPage"></div>
|
|
||||||
<div id="nextPage"></div>
|
|
||||||
</div>
|
|
||||||
<div id="overlayCloseButton">
|
|
||||||
✖
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -26,132 +26,84 @@
|
||||||
*/
|
*/
|
||||||
class odfviewer extends rcube_plugin
|
class odfviewer extends rcube_plugin
|
||||||
{
|
{
|
||||||
public $task = 'mail|calendar|tasks|logout';
|
public $task = 'mail|calendar|tasks';
|
||||||
|
|
||||||
private $tempdir = 'plugins/odfviewer/files/';
|
private $odf_mimetypes = array(
|
||||||
private $tempbase = 'plugins/odfviewer/files/';
|
'application/vnd.oasis.opendocument.chart',
|
||||||
|
'application/vnd.oasis.opendocument.chart-template',
|
||||||
|
'application/vnd.oasis.opendocument.formula',
|
||||||
|
'application/vnd.oasis.opendocument.formula-template',
|
||||||
|
'application/vnd.oasis.opendocument.graphics',
|
||||||
|
'application/vnd.oasis.opendocument.graphics-template',
|
||||||
|
'application/vnd.oasis.opendocument.presentation',
|
||||||
|
'application/vnd.oasis.opendocument.presentation-template',
|
||||||
|
'application/vnd.oasis.opendocument.text',
|
||||||
|
'application/vnd.oasis.opendocument.text-master',
|
||||||
|
'application/vnd.oasis.opendocument.text-template',
|
||||||
|
'application/vnd.oasis.opendocument.spreadsheet',
|
||||||
|
'application/vnd.oasis.opendocument.spreadsheet-template',
|
||||||
|
);
|
||||||
|
|
||||||
private $odf_mimetypes = array(
|
function init()
|
||||||
'application/vnd.oasis.opendocument.chart',
|
{
|
||||||
'application/vnd.oasis.opendocument.chart-template',
|
// webODF only supports IE9 or higher
|
||||||
'application/vnd.oasis.opendocument.formula',
|
$ua = new rcube_browser;
|
||||||
'application/vnd.oasis.opendocument.formula-template',
|
if ($ua->ie && $ua->ver < 9) {
|
||||||
'application/vnd.oasis.opendocument.graphics',
|
return;
|
||||||
'application/vnd.oasis.opendocument.graphics-template',
|
}
|
||||||
'application/vnd.oasis.opendocument.presentation',
|
|
||||||
'application/vnd.oasis.opendocument.presentation-template',
|
|
||||||
'application/vnd.oasis.opendocument.text',
|
|
||||||
'application/vnd.oasis.opendocument.text-master',
|
|
||||||
'application/vnd.oasis.opendocument.text-template',
|
|
||||||
'application/vnd.oasis.opendocument.spreadsheet',
|
|
||||||
'application/vnd.oasis.opendocument.spreadsheet-template',
|
|
||||||
);
|
|
||||||
|
|
||||||
function init()
|
// extend list of mimetypes that should open in preview
|
||||||
{
|
|
||||||
$this->tempdir = $this->home . '/files/';
|
|
||||||
$this->tempbase = $this->urlbase . 'files/';
|
|
||||||
|
|
||||||
// webODF only supports IE9 or higher
|
|
||||||
$ua = new rcube_browser;
|
|
||||||
if ($ua->ie && $ua->ver < 9)
|
|
||||||
return;
|
|
||||||
// extend list of mimetypes that should open in preview
|
|
||||||
$rcmail = rcube::get_instance();
|
|
||||||
if ($rcmail->action == 'preview' || $rcmail->action == 'show' || $rcmail->task == 'calendar' || $rcmail->task == 'tasks') {
|
|
||||||
$mimetypes = (array)$rcmail->config->get('client_mimetypes');
|
|
||||||
$rcmail->config->set('client_mimetypes', array_merge($mimetypes, $this->odf_mimetypes));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->add_hook('message_part_get', array($this, 'get_part'));
|
|
||||||
$this->add_hook('session_destroy', array($this, 'session_cleanup'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for message attachment download
|
|
||||||
*/
|
|
||||||
function get_part($args)
|
|
||||||
{
|
|
||||||
if (!$args['download'] && $args['mimetype'] && in_array($args['mimetype'], $this->odf_mimetypes)) {
|
|
||||||
if (empty($_GET['_load'])) {
|
|
||||||
$rcmail = rcube::get_instance();
|
$rcmail = rcube::get_instance();
|
||||||
$exts = rcube_mime::get_mime_extensions($args['mimetype']);
|
if ($rcmail->action == 'preview' || $rcmail->action == 'show' || $rcmail->task == 'calendar' || $rcmail->task == 'tasks') {
|
||||||
$suffix = $exts ? '.'.$exts[0] : '.odt';
|
$mimetypes = (array)$rcmail->config->get('client_mimetypes');
|
||||||
$fn = md5(session_id() . $_SERVER['REQUEST_URI']) . $suffix;
|
$rcmail->config->set('client_mimetypes', array_merge($mimetypes, $this->odf_mimetypes));
|
||||||
|
|
||||||
// FIXME: copy file to disk because only apache can send the file correctly
|
|
||||||
$tempfn = $this->tempdir . $fn;
|
|
||||||
if (!file_exists($tempfn)) {
|
|
||||||
if ($args['body']) {
|
|
||||||
file_put_contents($tempfn, $args['body']);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$fp = fopen($tempfn, 'w');
|
|
||||||
$imap = rcube::get_instance()->get_storage();
|
|
||||||
$imap->get_message_part($args['uid'], $args['id'], $args['part'], false, $fp);
|
|
||||||
fclose($fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// remember tempfiles in session to clean up on logout
|
|
||||||
$_SESSION['odfviewer']['tempfiles'][] = $fn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// send webODF viewer page
|
$this->add_hook('message_part_get', array($this, 'get_part'));
|
||||||
$html = file_get_contents($this->home . '/odf.html');
|
}
|
||||||
header("Content-Type: text/html; charset=" . RCMAIL_CHARSET);
|
|
||||||
echo strtr($html, array(
|
/**
|
||||||
'%%DOCROOT%%' => $rcmail->output->asset_url($this->urlbase),
|
* Handler for message attachment download
|
||||||
'%%DOCURL%%' => $rcmail->output->asset_url($this->tempbase . $fn), # $_SERVER['REQUEST_URI'].'&_load=1',
|
*/
|
||||||
));
|
function get_part($args)
|
||||||
$args['abort'] = true;
|
{
|
||||||
}
|
if (!$args['download'] && $args['mimetype'] && in_array($args['mimetype'], $this->odf_mimetypes)) {
|
||||||
/*
|
$rcmail = rcube::get_instance();
|
||||||
else {
|
$params = array(
|
||||||
if ($_SERVER['REQUEST_METHOD'] == 'HEAD') {
|
'documentUrl' => $_SERVER['REQUEST_URI'] . '&_download=1',
|
||||||
header("Content-Length: " . max(10, $args['part']->size)); # content-length has to be present
|
'filename' => $args['part']->filename ?: 'file.odt',
|
||||||
$args['body'] = ' '; # send empty body
|
'type' => $args['mimetype'],
|
||||||
return $args;
|
);
|
||||||
|
|
||||||
|
// send webODF viewer page
|
||||||
|
$html = file_get_contents($this->home . '/odf.html');
|
||||||
|
header("Content-Type: text/html; charset=" . RCMAIL_CHARSET);
|
||||||
|
echo strtr($html, array(
|
||||||
|
'%%PARAMS%%' => rcube_output::json_serialize($params),
|
||||||
|
'%%viewer.css%%' => $this->asset_path('viewer.css'),
|
||||||
|
'%%viewer.js%%' => $this->asset_path('viewer.js'),
|
||||||
|
'%%ODFViewerPlugin.js%%' => $this->asset_path('ODFViewerPlugin.js'),
|
||||||
|
'%%webodf.js%%' => $this->asset_path('webodf.js'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$args['abort'] = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
*/
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $args;
|
private function asset_path($path)
|
||||||
}
|
{
|
||||||
|
$rcmail = rcube::get_instance();
|
||||||
|
$assets_dir = $rcmail->config->get('assets_dir');
|
||||||
|
|
||||||
/**
|
$mtime = filemtime($this->home . '/' . $path);
|
||||||
* Remove temp files opened during this session
|
if (!$mtime && $assets_dir) {
|
||||||
*/
|
$mtime = filemtime($assets_dir . '/plugins/odfviewer/' . $path);
|
||||||
function session_cleanup()
|
}
|
||||||
{
|
|
||||||
foreach ((array)$_SESSION['odfviewer']['tempfiles'] as $fn) {
|
$path = $this->urlbase . $path . ($mtime ? '?s=' . $mtime : '');
|
||||||
@unlink($this->tempdir . $fn);
|
|
||||||
|
return $rcmail->output->asset_url($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// also trigger general garbage collection because not everybody logs out properly
|
|
||||||
$this->gc_cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Garbage collector function for temp files.
|
|
||||||
* Remove temp files older than two days
|
|
||||||
*/
|
|
||||||
function gc_cleanup()
|
|
||||||
{
|
|
||||||
$tmp = unslashify($this->tempdir);
|
|
||||||
$expire = mktime() - 172800; // expire in 48 hours
|
|
||||||
|
|
||||||
if ($dir = opendir($tmp)) {
|
|
||||||
while (($fname = readdir($dir)) !== false) {
|
|
||||||
if ($fname[0] == '.')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (filemtime($tmp.'/'.$fname) < $expire)
|
|
||||||
@unlink($tmp.'/'.$fname);
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir($dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,41 +1,50 @@
|
||||||
/**
|
/**
|
||||||
* @license
|
* Copyright (C) 2012-2015 KO GmbH <copyright@kogmbh.com>
|
||||||
* Copyright (C) 2013 KO GmbH <copyright@kogmbh.com>
|
|
||||||
*
|
*
|
||||||
* @licstart
|
* @licstart
|
||||||
* The JavaScript code in this page is free software: you can redistribute it
|
* This file is part of WebODF.
|
||||||
* and/or modify it under the terms of the GNU Affero General Public License
|
|
||||||
* (GNU AGPL) as published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version. The code is distributed
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU AGPL for more details.
|
|
||||||
*
|
*
|
||||||
* As additional permission under GNU AGPL version 3 section 7, you
|
* WebODF is free software: you can redistribute it and/or modify it
|
||||||
* may distribute non-source (e.g., minimized or compacted) forms of
|
* under the terms of the GNU Affero General Public License (GNU AGPL)
|
||||||
* that code without the copy of the GNU GPL normally required by
|
* as published by the Free Software Foundation, either version 3 of
|
||||||
* section 4, provided you include this license notice and a URL
|
* the License, or (at your option) any later version.
|
||||||
* through which recipients can access the Corresponding Source.
|
|
||||||
*
|
*
|
||||||
* As a special exception to the AGPL, any HTML file which merely makes function
|
* WebODF is distributed in the hope that it will be useful, but
|
||||||
* calls to this code, and for that purpose includes it by reference shall be
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* deemed a separate work for copyright law purposes. In addition, the copyright
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* holders of this code give you permission to combine this code with free
|
* GNU Affero General Public License for more details.
|
||||||
* software libraries that are released under the GNU LGPL. You may copy and
|
|
||||||
* distribute such a system following the terms of the GNU AGPL for this code
|
|
||||||
* and the LGPL for the libraries. If you modify this code, you may extend this
|
|
||||||
* exception to your version of the code, but you are not obligated to do so.
|
|
||||||
* If you do not wish to do so, delete this exception statement from your
|
|
||||||
* version.
|
|
||||||
*
|
*
|
||||||
* This license applies to this entire compilation.
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with WebODF. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* @licend
|
* @licend
|
||||||
|
*
|
||||||
* @source: http://www.webodf.org/
|
* @source: http://www.webodf.org/
|
||||||
* @source: http://gitorious.org/webodf/webodf/
|
* @source: https://github.com/kogmbh/WebODF/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is a derivative from a part of Mozilla's PDF.js project. The
|
||||||
|
* original license header follows.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Copyright 2012 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*global document, window*/
|
/*global document, window*/
|
||||||
|
|
||||||
function Viewer(viewerPlugin, docurl) {
|
function Viewer(viewerPlugin, parameters) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var self = this,
|
var self = this,
|
||||||
|
@ -45,25 +54,88 @@ function Viewer(viewerPlugin, docurl) {
|
||||||
kDefaultScaleDelta = 1.1,
|
kDefaultScaleDelta = 1.1,
|
||||||
kDefaultScale = 'auto',
|
kDefaultScale = 'auto',
|
||||||
presentationMode = false,
|
presentationMode = false,
|
||||||
|
isFullScreen = false,
|
||||||
initialized = false,
|
initialized = false,
|
||||||
isSlideshow = false,
|
isSlideshow = false,
|
||||||
url,
|
url,
|
||||||
viewerElement,
|
viewerElement = document.getElementById('viewer'),
|
||||||
canvasContainer = document.getElementById('canvasContainer'),
|
canvasContainer = document.getElementById('canvasContainer'),
|
||||||
overlayNavigator = document.getElementById('overlayNavigator'),
|
overlayNavigator = document.getElementById('overlayNavigator'),
|
||||||
|
titlebar = document.getElementById('titlebar'),
|
||||||
|
toolbar = document.getElementById('toolbarContainer'),
|
||||||
pageSwitcher = document.getElementById('toolbarLeft'),
|
pageSwitcher = document.getElementById('toolbarLeft'),
|
||||||
zoomWidget = document.getElementById('toolbarMiddleContainer'),
|
zoomWidget = document.getElementById('toolbarMiddleContainer'),
|
||||||
scaleSelector = document.getElementById('scaleSelect'),
|
scaleSelector = document.getElementById('scaleSelect'),
|
||||||
filename,
|
dialogOverlay = document.getElementById('dialogOverlay'),
|
||||||
|
toolbarRight = document.getElementById('toolbarRight'),
|
||||||
|
aboutDialog,
|
||||||
pages = [],
|
pages = [],
|
||||||
currentPage,
|
currentPage,
|
||||||
scaleChangeTimer,
|
scaleChangeTimer,
|
||||||
touchTimer;
|
touchTimer,
|
||||||
|
toolbarTouchTimer,
|
||||||
|
/**@const*/
|
||||||
|
UI_FADE_DURATION = 5000;
|
||||||
|
|
||||||
function isFullScreen() {
|
function isBlankedOut() {
|
||||||
// Note that the browser fullscreen (triggered by short keys) might
|
return (blanked.style.display === 'block');
|
||||||
// be considered different from content fullscreen when expecting a boolean
|
}
|
||||||
return document.isFullScreen || document.mozFullScreen || document.webkitIsFullScreen;
|
|
||||||
|
function initializeAboutInformation() {
|
||||||
|
var aboutDialogCentererTable, aboutDialogCentererCell, aboutButton, pluginName, pluginVersion, pluginURL;
|
||||||
|
|
||||||
|
if (viewerPlugin) {
|
||||||
|
pluginName = viewerPlugin.getPluginName();
|
||||||
|
pluginVersion = viewerPlugin.getPluginVersion();
|
||||||
|
pluginURL = viewerPlugin.getPluginURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create dialog
|
||||||
|
aboutDialogCentererTable = document.createElement('div');
|
||||||
|
aboutDialogCentererTable.id = "aboutDialogCentererTable";
|
||||||
|
aboutDialogCentererCell = document.createElement('div');
|
||||||
|
aboutDialogCentererCell.id = "aboutDialogCentererCell";
|
||||||
|
aboutDialog = document.createElement('div');
|
||||||
|
aboutDialog.id = "aboutDialog";
|
||||||
|
aboutDialog.innerHTML =
|
||||||
|
"<h1>ViewerJS</h1>" +
|
||||||
|
"<p>Open Source document viewer for webpages, built with HTML and JavaScript.</p>" +
|
||||||
|
"<p>Learn more and get your own copy on the <a href=\"http://viewerjs.org/\" target=\"_blank\">ViewerJS website</a>.</p>" +
|
||||||
|
(viewerPlugin ? ("<p>Using the <a href = \""+ pluginURL + "\" target=\"_blank\">" + pluginName + "</a> " +
|
||||||
|
"(<span id = \"pluginVersion\">" + pluginVersion + "</span>) " +
|
||||||
|
"plugin to show you this document.</p>")
|
||||||
|
: "") +
|
||||||
|
"<p>Supported by <a href=\"http://nlnet.nl\" target=\"_blank\"><br><img src=\"images\/nlnet.png\" width=\"160\" height=\"60\" alt=\"NLnet Foundation\"></a></p>" +
|
||||||
|
"<p>Made by <a href=\"http://kogmbh.com\" target=\"_blank\"><br><img src=\"images\/kogmbh.png\" width=\"172\" height=\"40\" alt=\"KO GmbH\"></a></p>" +
|
||||||
|
"<button id = \"aboutDialogCloseButton\" class = \"toolbarButton textButton\">Close</button>";
|
||||||
|
dialogOverlay.appendChild(aboutDialogCentererTable);
|
||||||
|
aboutDialogCentererTable.appendChild(aboutDialogCentererCell);
|
||||||
|
aboutDialogCentererCell.appendChild(aboutDialog);
|
||||||
|
|
||||||
|
// Create button to open dialog that says "ViewerJS"
|
||||||
|
aboutButton = document.createElement('button');
|
||||||
|
aboutButton.id = "about";
|
||||||
|
aboutButton.className = "toolbarButton textButton about";
|
||||||
|
aboutButton.title = "About";
|
||||||
|
aboutButton.innerHTML = "ViewerJS"
|
||||||
|
toolbarRight.appendChild(aboutButton);
|
||||||
|
|
||||||
|
// Attach events to the above
|
||||||
|
aboutButton.addEventListener('click', function () {
|
||||||
|
showAboutDialog();
|
||||||
|
});
|
||||||
|
document.getElementById('aboutDialogCloseButton').addEventListener('click', function () {
|
||||||
|
hideAboutDialog();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAboutDialog() {
|
||||||
|
dialogOverlay.style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideAboutDialog() {
|
||||||
|
dialogOverlay.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectScaleOption(value) {
|
function selectScaleOption(value) {
|
||||||
|
@ -171,18 +243,38 @@ function Viewer(viewerPlugin, docurl) {
|
||||||
delayedRefresh(300);
|
delayedRefresh(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readZoomParameter(zoom) {
|
||||||
|
var validZoomStrings = ["auto", "page-actual", "page-width"],
|
||||||
|
number;
|
||||||
|
|
||||||
this.initialize = function (url) {
|
if (validZoomStrings.indexOf(zoom) !== -1) {
|
||||||
viewerElement = document.getElementById('viewer');
|
return zoom;
|
||||||
filename = url.replace(/^.*[\\\/]/, '');
|
}
|
||||||
document.title = filename;
|
number = parseFloat(zoom);
|
||||||
document.getElementById('documentName').innerHTML = document.title;
|
if (number && kMinScale <= number && number <= kMaxScale) {
|
||||||
|
return zoom;
|
||||||
|
}
|
||||||
|
return kDefaultScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initialize = function () {
|
||||||
|
var initialScale,
|
||||||
|
element;
|
||||||
|
|
||||||
|
initialScale = readZoomParameter(parameters.zoom);
|
||||||
|
|
||||||
|
url = parameters.documentUrl;
|
||||||
|
document.title = parameters.filename;
|
||||||
|
var documentName = document.getElementById('documentName');
|
||||||
|
documentName.innerHTML = "";
|
||||||
|
documentName.appendChild(documentName.ownerDocument.createTextNode(parameters.filename));
|
||||||
|
|
||||||
viewerPlugin.onLoad = function () {
|
viewerPlugin.onLoad = function () {
|
||||||
|
// document.getElementById('pluginVersion').innerHTML = viewerPlugin.getPluginVersion();
|
||||||
isSlideshow = viewerPlugin.isSlideshow();
|
isSlideshow = viewerPlugin.isSlideshow();
|
||||||
if (isSlideshow) {
|
if (isSlideshow) {
|
||||||
// No padding for slideshows
|
// Slideshow pages should be centered
|
||||||
canvasContainer.style.padding = 0;
|
canvasContainer.classList.add("slideshow");
|
||||||
// Show page nav controls only for presentations
|
// Show page nav controls only for presentations
|
||||||
pageSwitcher.style.visibility = 'visible';
|
pageSwitcher.style.visibility = 'visible';
|
||||||
} else {
|
} else {
|
||||||
|
@ -201,7 +293,7 @@ function Viewer(viewerPlugin, docurl) {
|
||||||
self.showPage(1);
|
self.showPage(1);
|
||||||
|
|
||||||
// Set default scale
|
// Set default scale
|
||||||
parseScale(kDefaultScale);
|
parseScale(initialScale);
|
||||||
|
|
||||||
canvasContainer.onscroll = onScroll;
|
canvasContainer.onscroll = onScroll;
|
||||||
delayedRefresh();
|
delayedRefresh();
|
||||||
|
@ -260,21 +352,31 @@ function Viewer(viewerPlugin, docurl) {
|
||||||
*/
|
*/
|
||||||
this.toggleFullScreen = function () {
|
this.toggleFullScreen = function () {
|
||||||
var elem = viewerElement;
|
var elem = viewerElement;
|
||||||
if (!isFullScreen()) {
|
if (!isFullScreen) {
|
||||||
if (elem.requestFullScreen) {
|
if (elem.requestFullscreen) {
|
||||||
elem.requestFullScreen();
|
elem.requestFullscreen();
|
||||||
} else if (elem.mozRequestFullScreen) {
|
} else if (elem.mozRequestFullScreen) {
|
||||||
elem.mozRequestFullScreen();
|
elem.mozRequestFullScreen();
|
||||||
|
} else if (elem.webkitRequestFullscreen) {
|
||||||
|
elem.webkitRequestFullscreen();
|
||||||
} else if (elem.webkitRequestFullScreen) {
|
} else if (elem.webkitRequestFullScreen) {
|
||||||
elem.webkitRequestFullScreen();
|
elem.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||||
|
} else if (elem.msRequestFullscreen) {
|
||||||
|
elem.msRequestFullscreen();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (document.cancelFullScreen) {
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
} else if (document.cancelFullScreen) {
|
||||||
document.cancelFullScreen();
|
document.cancelFullScreen();
|
||||||
} else if (document.mozCancelFullScreen) {
|
} else if (document.mozCancelFullScreen) {
|
||||||
document.mozCancelFullScreen();
|
document.mozCancelFullScreen();
|
||||||
|
} else if (document.webkitExitFullscreen) {
|
||||||
|
document.webkitExitFullscreen();
|
||||||
} else if (document.webkitCancelFullScreen) {
|
} else if (document.webkitCancelFullScreen) {
|
||||||
document.webkitCancelFullScreen();
|
document.webkitCancelFullScreen();
|
||||||
|
} else if (document.msExitFullscreen) {
|
||||||
|
document.msExitFullscreen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -284,14 +386,12 @@ function Viewer(viewerPlugin, docurl) {
|
||||||
* Presentation mode involves fullscreen + hidden UI controls
|
* Presentation mode involves fullscreen + hidden UI controls
|
||||||
*/
|
*/
|
||||||
this.togglePresentationMode = function () {
|
this.togglePresentationMode = function () {
|
||||||
var titlebar = document.getElementById('titlebar'),
|
var overlayCloseButton = document.getElementById('overlayCloseButton');
|
||||||
toolbar = document.getElementById('toolbarContainer'),
|
|
||||||
overlayCloseButton = document.getElementById('overlayCloseButton');
|
|
||||||
|
|
||||||
if (!presentationMode) {
|
if (!presentationMode) {
|
||||||
titlebar.style.display = toolbar.style.display = 'none';
|
titlebar.style.display = toolbar.style.display = 'none';
|
||||||
overlayCloseButton.style.display = 'block';
|
overlayCloseButton.style.display = 'block';
|
||||||
canvasContainer.className = 'presentationMode';
|
canvasContainer.classList.add('presentationMode');
|
||||||
isSlideshow = true;
|
isSlideshow = true;
|
||||||
canvasContainer.onmousedown = function (event) {
|
canvasContainer.onmousedown = function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -309,9 +409,12 @@ function Viewer(viewerPlugin, docurl) {
|
||||||
};
|
};
|
||||||
parseScale('page-fit');
|
parseScale('page-fit');
|
||||||
} else {
|
} else {
|
||||||
|
if (isBlankedOut()) {
|
||||||
|
leaveBlankOut();
|
||||||
|
}
|
||||||
titlebar.style.display = toolbar.style.display = 'block';
|
titlebar.style.display = toolbar.style.display = 'block';
|
||||||
overlayCloseButton.style.display = 'none';
|
overlayCloseButton.style.display = 'none';
|
||||||
canvasContainer.className = '';
|
canvasContainer.classList.remove('presentationMode');
|
||||||
canvasContainer.onmouseup = function () {};
|
canvasContainer.onmouseup = function () {};
|
||||||
canvasContainer.oncontextmenu = function () {};
|
canvasContainer.oncontextmenu = function () {};
|
||||||
canvasContainer.onmousedown = function () {};
|
canvasContainer.onmousedown = function () {};
|
||||||
|
@ -362,123 +465,213 @@ function Viewer(viewerPlugin, docurl) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function cancelPresentationMode() {
|
function cancelPresentationMode() {
|
||||||
if (presentationMode && !isFullScreen()) {
|
if (presentationMode && !isFullScreen) {
|
||||||
self.togglePresentationMode();
|
self.togglePresentationMode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleFullScreenChange() {
|
||||||
|
isFullScreen = !isFullScreen;
|
||||||
|
cancelPresentationMode();
|
||||||
|
}
|
||||||
|
|
||||||
function showOverlayNavigator() {
|
function showOverlayNavigator() {
|
||||||
if (isSlideshow) {
|
if (isSlideshow) {
|
||||||
overlayNavigator.className = 'touched';
|
overlayNavigator.className = 'viewer-touched';
|
||||||
window.clearTimeout(touchTimer);
|
window.clearTimeout(touchTimer);
|
||||||
touchTimer = window.setTimeout(function () {
|
touchTimer = window.setTimeout(function () {
|
||||||
overlayNavigator.className = '';
|
overlayNavigator.className = '';
|
||||||
}, 2000);
|
}, UI_FADE_DURATION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function init(docurl) {
|
/**
|
||||||
|
* @param {!boolean} timed Fade after a while
|
||||||
|
*/
|
||||||
|
function showToolbars() {
|
||||||
|
titlebar.classList.add('viewer-touched');
|
||||||
|
toolbar.classList.add('viewer-touched');
|
||||||
|
window.clearTimeout(toolbarTouchTimer);
|
||||||
|
toolbarTouchTimer = window.setTimeout(function () {
|
||||||
|
hideToolbars();
|
||||||
|
}, UI_FADE_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
self.initialize(docurl);
|
function hideToolbars() {
|
||||||
|
titlebar.classList.remove('viewer-touched');
|
||||||
|
toolbar.classList.remove('viewer-touched');
|
||||||
|
}
|
||||||
|
|
||||||
if (!(document.cancelFullScreen || document.mozCancelFullScreen || document.webkitCancelFullScreen)) {
|
function toggleToolbars() {
|
||||||
document.getElementById('fullscreen').style.visibility = 'hidden';
|
if (titlebar.classList.contains('viewer-touched')) {
|
||||||
|
hideToolbars();
|
||||||
|
} else {
|
||||||
|
showToolbars();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('overlayCloseButton').addEventListener('click', self.toggleFullScreen);
|
function blankOut(value) {
|
||||||
document.getElementById('fullscreen').addEventListener('click', self.toggleFullScreen);
|
blanked.style.display = 'block';
|
||||||
document.getElementById('presentation').addEventListener('click', function () {
|
blanked.style.backgroundColor = value;
|
||||||
if (!isFullScreen()) {
|
hideToolbars();
|
||||||
self.toggleFullScreen();
|
}
|
||||||
|
|
||||||
|
function leaveBlankOut() {
|
||||||
|
blanked.style.display = 'none';
|
||||||
|
toggleToolbars();
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
|
||||||
|
// initializeAboutInformation();
|
||||||
|
if (viewerPlugin) {
|
||||||
|
|
||||||
|
self.initialize();
|
||||||
|
|
||||||
|
if (!(document.exitFullscreen || document.cancelFullScreen || document.mozCancelFullScreen || document.webkitExitFullscreen || document.webkitCancelFullScreen || document.msExitFullscreen)) {
|
||||||
|
document.getElementById('fullscreen').style.visibility = 'hidden';
|
||||||
|
document.getElementById('presentation').style.visibility = 'hidden';
|
||||||
}
|
}
|
||||||
self.togglePresentationMode();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('fullscreenchange', cancelPresentationMode);
|
document.getElementById('overlayCloseButton').addEventListener('click', self.toggleFullScreen);
|
||||||
document.addEventListener('webkitfullscreenchange', cancelPresentationMode);
|
document.getElementById('fullscreen').addEventListener('click', self.toggleFullScreen);
|
||||||
document.addEventListener('mozfullscreenchange', cancelPresentationMode);
|
document.getElementById('presentation').addEventListener('click', function () {
|
||||||
|
if (!isFullScreen) {
|
||||||
|
self.toggleFullScreen();
|
||||||
|
}
|
||||||
|
self.togglePresentationMode();
|
||||||
|
});
|
||||||
|
|
||||||
document.getElementById('download').addEventListener('click', function () {
|
document.addEventListener('fullscreenchange', handleFullScreenChange);
|
||||||
self.download();
|
document.addEventListener('webkitfullscreenchange', handleFullScreenChange);
|
||||||
});
|
document.addEventListener('mozfullscreenchange', handleFullScreenChange);
|
||||||
|
document.addEventListener('MSFullscreenChange', handleFullScreenChange);
|
||||||
|
|
||||||
document.getElementById('zoomOut').addEventListener('click', function () {
|
document.getElementById('download').addEventListener('click', function () {
|
||||||
self.zoomOut();
|
self.download();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('zoomIn').addEventListener('click', function () {
|
document.getElementById('zoomOut').addEventListener('click', function () {
|
||||||
self.zoomIn();
|
self.zoomOut();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('previous').addEventListener('click', function () {
|
document.getElementById('zoomIn').addEventListener('click', function () {
|
||||||
self.showPreviousPage();
|
self.zoomIn();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('next').addEventListener('click', function () {
|
document.getElementById('previous').addEventListener('click', function () {
|
||||||
self.showNextPage();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('previousPage').addEventListener('click', function () {
|
|
||||||
self.showPreviousPage();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('nextPage').addEventListener('click', function () {
|
|
||||||
self.showNextPage();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('pageNumber').addEventListener('change', function () {
|
|
||||||
self.showPage(this.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('scaleSelect').addEventListener('change', function () {
|
|
||||||
parseScale(this.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
canvasContainer.addEventListener('click', showOverlayNavigator);
|
|
||||||
overlayNavigator.addEventListener('click', showOverlayNavigator);
|
|
||||||
|
|
||||||
window.addEventListener('scalechange', function (evt) {
|
|
||||||
var customScaleOption = document.getElementById('customScaleOption'),
|
|
||||||
predefinedValueFound = selectScaleOption(String(evt.scale));
|
|
||||||
|
|
||||||
customScaleOption.selected = false;
|
|
||||||
|
|
||||||
if (!predefinedValueFound) {
|
|
||||||
customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
|
|
||||||
customScaleOption.selected = true;
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
window.addEventListener('resize', function (evt) {
|
|
||||||
if (initialized &&
|
|
||||||
(document.getElementById('pageWidthOption').selected ||
|
|
||||||
document.getElementById('pageAutoOption').selected)) {
|
|
||||||
parseScale(document.getElementById('scaleSelect').value);
|
|
||||||
}
|
|
||||||
showOverlayNavigator();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('keydown', function (evt) {
|
|
||||||
var key = evt.keyCode,
|
|
||||||
shiftKey = evt.shiftKey;
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case 33: // pageUp
|
|
||||||
case 38: // up
|
|
||||||
case 37: // left
|
|
||||||
self.showPreviousPage();
|
self.showPreviousPage();
|
||||||
break;
|
});
|
||||||
case 34: // pageDown
|
|
||||||
case 40: // down
|
document.getElementById('next').addEventListener('click', function () {
|
||||||
case 39: // right
|
|
||||||
self.showNextPage();
|
self.showNextPage();
|
||||||
break;
|
});
|
||||||
case 32: // space
|
|
||||||
shiftKey ? self.showPreviousPage() : self.showNextPage();
|
document.getElementById('previousPage').addEventListener('click', function () {
|
||||||
break;
|
self.showPreviousPage();
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
document.getElementById('nextPage').addEventListener('click', function () {
|
||||||
|
self.showNextPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('pageNumber').addEventListener('change', function () {
|
||||||
|
self.showPage(this.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('scaleSelect').addEventListener('change', function () {
|
||||||
|
parseScale(this.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
canvasContainer.addEventListener('click', showOverlayNavigator);
|
||||||
|
overlayNavigator.addEventListener('click', showOverlayNavigator);
|
||||||
|
canvasContainer.addEventListener('click', toggleToolbars);
|
||||||
|
titlebar.addEventListener('click', showToolbars);
|
||||||
|
toolbar.addEventListener('click', showToolbars);
|
||||||
|
|
||||||
|
window.addEventListener('scalechange', function (evt) {
|
||||||
|
var customScaleOption = document.getElementById('customScaleOption'),
|
||||||
|
predefinedValueFound = selectScaleOption(String(evt.scale));
|
||||||
|
|
||||||
|
customScaleOption.selected = false;
|
||||||
|
|
||||||
|
if (!predefinedValueFound) {
|
||||||
|
customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
|
||||||
|
customScaleOption.selected = true;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
window.addEventListener('resize', function (evt) {
|
||||||
|
if (initialized &&
|
||||||
|
(document.getElementById('pageWidthOption').selected ||
|
||||||
|
document.getElementById('pageAutoOption').selected)) {
|
||||||
|
parseScale(document.getElementById('scaleSelect').value);
|
||||||
|
}
|
||||||
|
showOverlayNavigator();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('keydown', function (evt) {
|
||||||
|
var key = evt.keyCode,
|
||||||
|
shiftKey = evt.shiftKey;
|
||||||
|
|
||||||
|
// blanked-out mode?
|
||||||
|
if (isBlankedOut()) {
|
||||||
|
switch (key) {
|
||||||
|
case 16: // Shift
|
||||||
|
case 17: // Ctrl
|
||||||
|
case 18: // Alt
|
||||||
|
case 91: // LeftMeta
|
||||||
|
case 93: // RightMeta
|
||||||
|
case 224: // MetaInMozilla
|
||||||
|
case 225: // AltGr
|
||||||
|
// ignore modifier keys alone
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
leaveBlankOut();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (key) {
|
||||||
|
case 8: // backspace
|
||||||
|
case 33: // pageUp
|
||||||
|
case 37: // left arrow
|
||||||
|
case 38: // up arrow
|
||||||
|
case 80: // key 'p'
|
||||||
|
self.showPreviousPage();
|
||||||
|
break;
|
||||||
|
case 13: // enter
|
||||||
|
case 34: // pageDown
|
||||||
|
case 39: // right arrow
|
||||||
|
case 40: // down arrow
|
||||||
|
case 78: // key 'n'
|
||||||
|
self.showNextPage();
|
||||||
|
break;
|
||||||
|
case 32: // space
|
||||||
|
shiftKey ? self.showPreviousPage() : self.showNextPage();
|
||||||
|
break;
|
||||||
|
case 66: // key 'b' blanks screen (to black) or returns to the document
|
||||||
|
case 190: // and so does the key '.' (dot)
|
||||||
|
if (presentationMode) {
|
||||||
|
blankOut('#000');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 87: // key 'w' blanks page (to white) or returns to the document
|
||||||
|
case 188: // and so does the key ',' (comma)
|
||||||
|
if (presentationMode) {
|
||||||
|
blankOut('#FFF');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 36: // key 'Home' goes to first page
|
||||||
|
self.showPage(0);
|
||||||
|
break;
|
||||||
|
case 35: // key 'End' goes to last page
|
||||||
|
self.showPage(pages.length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(docurl);
|
init();
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Add table
Reference in a new issue