You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
richdocuments/js/editor/Editor.js

319 lines
13 KiB
JavaScript

/**
* Copyright (C) 2013 KO GmbH <copyright@kogmbh.com>
*
* @licstart
* The JavaScript code in this page is free software: you can redistribute it
* 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
* may distribute non-source (e.g., minimized or compacted) forms of
* that code without the copy of the GNU GPL normally required by
* section 4, provided you include this license notice and a URL
* through which recipients can access the Corresponding Source.
*
* As a special exception to the AGPL, any HTML file which merely makes function
* calls to this code, and for that purpose includes it by reference shall be
* deemed a separate work for copyright law purposes. In addition, the copyright
* holders of this code give you permission to combine this code with free
* 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.
* @licend
* @source: http://www.webodf.org/
* @source: http://gitorious.org/webodf/webodf/
*/
/*global runtime, define, document, odf, ops, window, gui, alert, saveAs, Blob */
define("webodf/editor/Editor", [
"dojo/i18n!webodf/editor/nls/myResources",
"webodf/editor/EditorSession",
"webodf/editor/MemberList",
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"webodf/editor/widgets"],
function (myResources,
EditorSession,
MemberList,
BorderContainer,
ContentPane,
loadWidgets) {
"use strict";
/**
* @constructor
* @param {{networked:boolean=,
* memberid:!string,
* loadCallback:function()=,
* saveCallback:function()=,
* cursorAddedCallback:function(!string)=,
* cursorRemovedCallback:function(!string)=,
* registerCallbackForShutdown:function(!function())= }} args
* @param {!ops.Server=} server
* @param {!ServerFactory=} serverFactory
*/
function Editor(args, server, serverFactory) {
var self = this,
// Private
memberid = args.memberid,
session,
editorSession,
memberList,
networked = args.networked === true,
opRouter,
memberModel,
loadOdtFile = args.loadCallback,
saveOdtFile = args.saveCallback,
cursorAddedHandler = args.cursorAddedCallback,
cursorRemovedHandler = args.cursorRemovedCallback,
registerCallbackForShutdown = args.registerCallbackForShutdown,
documentUrl,
odfCanvas;
function translator(key, context) {
if (undefined === myResources[key]) {
return "translation missing: " + key;
}
return myResources[key];
}
/**
* prepare all gui elements and load the given document.
* after loading is completed, the given callback is called.
* the caller still has to call editorSession.startEditing
* which will insert the the cursor.
*
* @param {!string} initialDocumentUrl
* @param {!function()} editorReadyCallback
*/
function initGuiAndDoc(initialDocumentUrl, editorReadyCallback) {
var odfElement, mainContainer,
editorPane, memberListPane,
inviteButton,
viewOptions = {
editInfoMarkersInitiallyVisible: networked,
caretAvatarsInitiallyVisible: networked,
caretBlinksOnRangeSelect: true
},
memberListDiv = document.getElementById('memberList');
if (networked) {
runtime.assert(memberListDiv, "missing memberList div in HTML");
}
runtime.loadClass('odf.OdfCanvas');
// we might need it later
documentUrl = initialDocumentUrl;
runtime.assert(documentUrl, "document should be defined here.");
runtime.assert(memberid !== undefined, "memberid should be defined here.");
odfElement = document.getElementById("canvas");
runtime.assert(odfElement, "initGuiAndDoc failed to get odf canvas from html");
odfCanvas = new odf.OdfCanvas(odfElement);
// make the canvas accessible to users of editor.js
self.odfCanvas = odfCanvas;
document.translator = translator;
function translateContent(node) {
var i,
element,
tag,
placeholder,
translatable = node.querySelectorAll("*[text-i18n]");
for (i = 0; i < translatable.length; i += 1) {
element = translatable[i];
tag = element.localName;
placeholder = element.getAttribute('text-i18n');
if (tag === "label"
|| tag === "span"
|| /h\d/i.test(tag)) {
element.textContent = document.translator(placeholder);
}
}
}
document.translateContent = translateContent;
odfCanvas.addListener("statereadychange", function () {
if (!editorReadyCallback) {
// already called once, restart session and return
// undo manager is not yet integrated with collaboration
if (! server) {
editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager());
}
editorSession.startEditing();
return;
}
// Allow annotations
odfCanvas.enableAnnotations(true);
session = new ops.Session(odfCanvas);
editorSession = new EditorSession(session, memberid, {
viewOptions: viewOptions
});
// undo manager is not yet integrated with collaboration
if (! server) {
editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager());
}
if (memberListDiv) {
memberList = new MemberList(editorSession, memberListDiv);
}
if (registerCallbackForShutdown) {
registerCallbackForShutdown(editorSession.endEditing);
}
loadWidgets(editorSession, loadOdtFile, saveOdtFile);
editorReadyCallback();
editorReadyCallback = null;
});
odfCanvas.load(initialDocumentUrl);
odfCanvas.setEditable(false);
// App Widgets
mainContainer = new BorderContainer({}, 'mainContainer');
editorPane = new ContentPane({
region: 'center'
}, 'editor');
mainContainer.addChild(editorPane);
if (networked && memberListDiv) {
memberListPane = new ContentPane({
region: 'right',
title: translator("members")
}, 'members');
mainContainer.addChild(memberListPane);
}
mainContainer.startup();
if (window.inviteButtonProxy) {
inviteButton = document.getElementById('inviteButton');
if (inviteButton) {
inviteButton.innerText = translator("inviteMembers");
inviteButton.style.display = "block";
inviteButton.onclick = window.inviteButtonProxy.clicked;
}
}
}
/**
* create the editor, load the starting document,
* call editorReadyCallback once everything is done.
*
* @param {!string} docUrl
* @param {!function()} editorReadyCallback
* @return {undefined}
*/
self.initAndLoadDocument = function (docUrl, editorReadyCallback) {
initGuiAndDoc(docUrl, function () {
editorReadyCallback(editorSession);
});
};
/**
* Shutdown running editing (polling-timer),
* cleanup.
*/
self.shutdown = function (successfullShutdownCallback) {
editorSession.endEditing();
opRouter.shutdown(function() {
memberModel.shutdown();
successfullShutdownCallback();
});
};
/**
* Load a document in an editor that has already been initialized.
*/
self.loadDocument = function (docUrl) {
self.shutdown();
odfCanvas.load(docUrl);
};
/**
* @param {?function()} callback
* @return {undefined}
*/
self.saveDocument = function (filename, callback) {
function onsuccess(data) {
var mimebase = "application/vnd.oasis.opendocument.",
mimetype = mimebase + "text",
blob;
filename = filename || "doc.odt";
if (filename.substr(-4) === ".odp") {
mimetype = mimebase + "presentation";
} else if (filename.substr(-4) === ".ods") {
mimetype = mimebase + "spreadsheet";
}
blob = new Blob([data.buffer], {type: mimetype});
saveAs(blob, filename);
}
function onerror(error) {
alert(error);
}
var doc = odfCanvas.odfContainer();
doc.createByteArray(onsuccess, onerror);
};
/**
* create the editor, load the starting document of an
* editing-session, request a replay of previous operations, call
* editorReadyCallback once everything is done.
*
* @param {!string} sessionId
* @param {?function()} editorReadyCallback
*/
self.loadSession = function (sessionId, editorReadyCallback) {
initGuiAndDoc(server.getGenesisUrl(sessionId), function () {
// get router and member model
opRouter = opRouter || serverFactory.createOperationRouter(sessionId, memberid, server);
session.setOperationRouter(opRouter);
memberModel = memberModel || serverFactory.createMemberModel(sessionId, server);
session.setMemberModel(memberModel);
opRouter.requestReplay(function done() {
var odtDocument = session.getOdtDocument();
if (cursorAddedHandler) {
odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, function (cursor) {
cursorAddedHandler(cursor.getMemberId());
});
}
if (cursorRemovedHandler) {
odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, function (memberId) {
cursorRemovedHandler(memberId);
});
}
editorReadyCallback(editorSession);
});
});
};
// access to member model
self.getMemberModel = function () {
return memberModel;
};
}
return Editor;
});
// vim:expandtab