initial WIP - amd clash-issues webodf/dojo/owncloud
parent
c3bd22366c
commit
37f1c94d8f
@ -0,0 +1,422 @@
|
|||||||
|
/**
|
||||||
|
* 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/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bootstrap the editor in different ways.
|
||||||
|
* this file is meant to be included from HTML and used
|
||||||
|
* by users who do not want to know much about the inner
|
||||||
|
* complexity.
|
||||||
|
* so we need to make it really easy.
|
||||||
|
*
|
||||||
|
* including this file will result in the namespace/object
|
||||||
|
* "webodfEditor" to be available from the HTML side.
|
||||||
|
* calling webodfEditor.boot() will start the editor.
|
||||||
|
* the method can also take some parameters to specify
|
||||||
|
* behaviour. see documentation of that method.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global runtime, require, document, alert, gui, window, SessionList, SessionListView, FileReader, Uint8Array */
|
||||||
|
|
||||||
|
// define the namespace/object we want to provide
|
||||||
|
// this is the first line of API, the user gets.
|
||||||
|
var webodfEditor = (function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var editorInstance = null,
|
||||||
|
booting = false,
|
||||||
|
loadedFilename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wait for a network connection through nowjs to establish.
|
||||||
|
* call the callback when done, when finally failed, or
|
||||||
|
* when a timeout reached.
|
||||||
|
* the parameter to the callback is a string with the possible
|
||||||
|
* values:
|
||||||
|
* "unavailable", "timeout", "ready"
|
||||||
|
*
|
||||||
|
* @param {!function(!string)} callback
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
function waitForNetwork(callback) {
|
||||||
|
var net = runtime.getNetwork(), accumulated_waiting_time = 0;
|
||||||
|
function later_cb() {
|
||||||
|
if (net.networkStatus === "unavailable") {
|
||||||
|
runtime.log("connection to server unavailable.");
|
||||||
|
callback("unavailable");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (net.networkStatus !== "ready") {
|
||||||
|
if (accumulated_waiting_time > 8000) {
|
||||||
|
// game over
|
||||||
|
runtime.log("connection to server timed out.");
|
||||||
|
callback("timeout");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
accumulated_waiting_time += 100;
|
||||||
|
runtime.getWindow().setTimeout(later_cb, 100);
|
||||||
|
} else {
|
||||||
|
runtime.log("connection to collaboration server established.");
|
||||||
|
callback("ready");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
later_cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extract document url from the url-fragment
|
||||||
|
*
|
||||||
|
* @return {?string}
|
||||||
|
*/
|
||||||
|
function guessDocUrl() {
|
||||||
|
var pos, docUrl = String(document.location);
|
||||||
|
// If the URL has a fragment (#...), try to load the file it represents
|
||||||
|
pos = docUrl.indexOf('#');
|
||||||
|
if (pos !== -1) {
|
||||||
|
docUrl = docUrl.substr(pos + 1);
|
||||||
|
} else {
|
||||||
|
docUrl = "welcome.odt";
|
||||||
|
}
|
||||||
|
return docUrl || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileSelectHandler(evt) {
|
||||||
|
var file, files, reader;
|
||||||
|
files = (evt.target && evt.target.files) ||
|
||||||
|
(evt.dataTransfer && evt.dataTransfer.files);
|
||||||
|
function onloadend() {
|
||||||
|
if (reader.readyState === 2) {
|
||||||
|
runtime.registerFile(file.name, reader.result);
|
||||||
|
loadedFilename = file.name;
|
||||||
|
editorInstance.loadDocument(file.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (files && files.length === 1) {
|
||||||
|
file = files[0];
|
||||||
|
reader = new FileReader();
|
||||||
|
reader.onloadend = onloadend;
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
} else {
|
||||||
|
alert("File could not be opened in this browser.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enhanceRuntime() {
|
||||||
|
var openedFiles = {},
|
||||||
|
read = runtime.read,
|
||||||
|
getFileSize = runtime.getFileSize;
|
||||||
|
runtime.read = function (path, offset, length, callback) {
|
||||||
|
var array;
|
||||||
|
if (openedFiles.hasOwnProperty(path)) {
|
||||||
|
array = new Uint8Array(openedFiles[path], offset, length);
|
||||||
|
callback(undefined, array);
|
||||||
|
} else {
|
||||||
|
return read(path, offset, length, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
runtime.getFileSize = function (path, callback) {
|
||||||
|
if (openedFiles.hasOwnProperty(path)) {
|
||||||
|
return callback(openedFiles[path].byteLength);
|
||||||
|
} else {
|
||||||
|
return getFileSize(path, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
runtime.registerFile = function (path, data) {
|
||||||
|
openedFiles[path] = data;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFileLoadForm() {
|
||||||
|
var form = document.createElement("form"),
|
||||||
|
input = document.createElement("input");
|
||||||
|
form.appendChild(input);
|
||||||
|
form.style.display = "none";
|
||||||
|
input.id = "fileloader";
|
||||||
|
input.setAttribute("type", "file");
|
||||||
|
input.addEventListener("change", fileSelectHandler, false);
|
||||||
|
document.body.appendChild(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
function load() {
|
||||||
|
var form = document.getElementById("fileloader");
|
||||||
|
if (!form) {
|
||||||
|
enhanceRuntime();
|
||||||
|
createFileLoadForm();
|
||||||
|
form = document.getElementById("fileloader");
|
||||||
|
}
|
||||||
|
form.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
editorInstance.saveDocument(loadedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a new editor instance, and start the editor with
|
||||||
|
* the given document.
|
||||||
|
*
|
||||||
|
* @param {!string} docUrl
|
||||||
|
* @param {?Object} editorOptions
|
||||||
|
* @param {?function(!Object)} editorReadyCallback
|
||||||
|
*/
|
||||||
|
function createLocalEditor(docUrl, editorOptions, editorReadyCallback) {
|
||||||
|
var pos;
|
||||||
|
booting = true;
|
||||||
|
editorOptions = editorOptions || {};
|
||||||
|
editorOptions.memberid = "localuser";
|
||||||
|
editorOptions.loadCallback = load;
|
||||||
|
editorOptions.saveCallback = save;
|
||||||
|
|
||||||
|
runtime.assert(docUrl, "docUrl needs to be specified");
|
||||||
|
runtime.assert(editorInstance === null, "cannot boot with instanciated editor");
|
||||||
|
|
||||||
|
document.getElementById("mainContainer").style.display = "";
|
||||||
|
|
||||||
|
document.dojoAnchor.require({}, ["webodf/editor/Editor"],
|
||||||
|
function (Editor) {
|
||||||
|
runtime.assert(Editor!==undefined, "requiring does not work.");
|
||||||
|
editorInstance = new Editor(editorOptions);
|
||||||
|
editorInstance.initAndLoadDocument(docUrl, function (editorSession) {
|
||||||
|
editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager());
|
||||||
|
editorSession.startEditing();
|
||||||
|
editorReadyCallback(editorInstance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* assume the network connection is established, create a new editor instance,
|
||||||
|
* and start the editor on the network.
|
||||||
|
*
|
||||||
|
* @param {!string} sessionId
|
||||||
|
* @param {!string} userid
|
||||||
|
* @param {?string} token
|
||||||
|
* @param {?Object} editorOptions
|
||||||
|
* @param {?function(!Object)} editorReadyCallback
|
||||||
|
*/
|
||||||
|
function createNetworkedEditor(sessionId, userid, token, editorOptions, editorReadyCallback) {
|
||||||
|
|
||||||
|
runtime.assert(sessionId, "sessionId needs to be specified");
|
||||||
|
runtime.assert(editorInstance === null, "cannot boot with instanciated editor");
|
||||||
|
|
||||||
|
editorOptions = editorOptions || {};
|
||||||
|
editorOptions.memberid = userid + "___" + Date.now();
|
||||||
|
editorOptions.networked = true;
|
||||||
|
editorOptions.networkSecurityToken = token;
|
||||||
|
|
||||||
|
require({ }, ["webodf/editor/Editor"],
|
||||||
|
function (Editor) {
|
||||||
|
// TODO: the networkSecurityToken needs to be retrieved via now.login
|
||||||
|
// (but this is to be implemented later)
|
||||||
|
editorInstance = new Editor(editorOptions);
|
||||||
|
|
||||||
|
// load the document and get called back when it's live
|
||||||
|
editorInstance.loadSession(sessionId, function (editorSession) {
|
||||||
|
editorSession.startEditing();
|
||||||
|
editorReadyCallback(editorInstance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start the login process by offering a login/password prompt.
|
||||||
|
* the login is validated via nowjs namespace.
|
||||||
|
* on success a list of sessions is offered.
|
||||||
|
* when the user selects a session the callback is called
|
||||||
|
* with the sessionId as parameter
|
||||||
|
*
|
||||||
|
* @param {!function(!string, !string, ?string)} callback
|
||||||
|
* @returns {undefined}
|
||||||
|
*/
|
||||||
|
function startLoginProcess(callback) {
|
||||||
|
var userid, token,
|
||||||
|
net = runtime.getNetwork();
|
||||||
|
|
||||||
|
booting = true;
|
||||||
|
|
||||||
|
runtime.assert(editorInstance === null, "cannot boot with instanciated editor");
|
||||||
|
|
||||||
|
function enterSession(selectedSessionId) {
|
||||||
|
document.getElementById("sessionListContainer").style.display = "none";
|
||||||
|
document.getElementById("mainContainer").style.display = "";
|
||||||
|
|
||||||
|
callback(selectedSessionId, userid, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSessions() {
|
||||||
|
var sessionListDiv = document.getElementById("sessionList"),
|
||||||
|
sessionList = new SessionList(net),
|
||||||
|
sessionListView = new SessionListView(sessionList, sessionListDiv, enterSession);
|
||||||
|
|
||||||
|
// hide login view
|
||||||
|
document.getElementById("loginContainer").style.display = "none";
|
||||||
|
|
||||||
|
// show session list
|
||||||
|
document.getElementById("sessionListContainer").style.display = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function loginSuccess(userData) {
|
||||||
|
runtime.log("connected:" + userData.full_name);
|
||||||
|
userid = userData.uid;
|
||||||
|
token = userData.securityToken || null;
|
||||||
|
|
||||||
|
showSessions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loginFail(result) {
|
||||||
|
alert("Login failed: " + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLoginSubmit() {
|
||||||
|
net.login(document.loginForm.login.value, document.loginForm.password.value, loginSuccess, loginFail);
|
||||||
|
|
||||||
|
// block the submit button, we already dealt with the input
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bring up the login form
|
||||||
|
document.loginForm.Submit.onclick = onLoginSubmit;
|
||||||
|
document.getElementById("loginContainer").style.display = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make a guess about the document (# in URL)
|
||||||
|
* also guess about local/collaborative (depending on nowjs)
|
||||||
|
*
|
||||||
|
* @param {?Object} args
|
||||||
|
*
|
||||||
|
* args:
|
||||||
|
*
|
||||||
|
* collaborative: if set to true: connect to the network and start a
|
||||||
|
* collaborative editor. in that case the document url
|
||||||
|
* is ignored. and user needs to select a session.
|
||||||
|
*
|
||||||
|
* if set to the string "auto": it will try the above
|
||||||
|
* but fall back to non-collaborative mode [default]
|
||||||
|
*
|
||||||
|
* docUrl: if given it is used as the url to the document to load
|
||||||
|
*
|
||||||
|
* callback: callback to be called as soon as the document is loaded
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function boot(args) {
|
||||||
|
var editorOptions = {}, loginProcedure = startLoginProcess;
|
||||||
|
runtime.assert(!booting, "editor creation already in progress");
|
||||||
|
|
||||||
|
args = args || {};
|
||||||
|
|
||||||
|
if (args.collaborative === undefined) {
|
||||||
|
args.collaborative = "auto";
|
||||||
|
} else {
|
||||||
|
args.collaborative = String(args.collaborative).toLowerCase();
|
||||||
|
}
|
||||||
|
if (args.docUrl === undefined) {
|
||||||
|
args.docUrl = guessDocUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.saveCallback) {
|
||||||
|
editorOptions.saveCallback = args.saveCallback;
|
||||||
|
}
|
||||||
|
if (args.cursorAddedCallback) {
|
||||||
|
editorOptions.cursorAddedCallback = args.cursorAddedCallback;
|
||||||
|
}
|
||||||
|
if (args.cursorRemovedCallback) {
|
||||||
|
editorOptions.cursorRemovedCallback = args.cursorRemovedCallback;
|
||||||
|
}
|
||||||
|
if (args.registerCallbackForShutdown) {
|
||||||
|
editorOptions.registerCallbackForShutdown = args.registerCallbackForShutdown;
|
||||||
|
} else {
|
||||||
|
editorOptions.registerCallbackForShutdown = function (callback) {
|
||||||
|
window.onunload = callback;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.loginProcedure) {
|
||||||
|
loginProcedure = args.loginProcedure;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the editor with network
|
||||||
|
function handleNetworkedSituation() {
|
||||||
|
loginProcedure(function (sessionId, userid, token) {
|
||||||
|
createNetworkedEditor(sessionId, userid, token, editorOptions, function (ed) {
|
||||||
|
if (args.callback) {
|
||||||
|
args.callback(ed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the editor without network
|
||||||
|
function handleNonNetworkedSituation() {
|
||||||
|
createLocalEditor(args.docUrl, editorOptions, function (editor) {
|
||||||
|
if (args.callback) {
|
||||||
|
args.callback(editor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.collaborative === "auto") {
|
||||||
|
runtime.log("detecting network...");
|
||||||
|
waitForNetwork(function (state) {
|
||||||
|
if (state === "ready") {
|
||||||
|
runtime.log("... network available.");
|
||||||
|
handleNetworkedSituation();
|
||||||
|
} else {
|
||||||
|
runtime.log("... no network available (" + state + ").");
|
||||||
|
handleNonNetworkedSituation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ((args.collaborative === "true") ||
|
||||||
|
(args.collaborative === "1") ||
|
||||||
|
(args.collaborative === "yes")) {
|
||||||
|
runtime.log("starting collaborative editor.");
|
||||||
|
waitForNetwork(function (state) {
|
||||||
|
if (state === "ready") {
|
||||||
|
handleNetworkedSituation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
runtime.log("starting local editor.");
|
||||||
|
handleNonNetworkedSituation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// exposed API
|
||||||
|
return { boot: boot };
|
||||||
|
}());
|
||||||
|
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,307 @@
|
|||||||
|
/**
|
||||||
|
* 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/UserList",
|
||||||
|
"dijit/layout/BorderContainer",
|
||||||
|
"dijit/layout/ContentPane",
|
||||||
|
"webodf/editor/widgets"],
|
||||||
|
|
||||||
|
function (myResources,
|
||||||
|
EditorSession,
|
||||||
|
UserList,
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
function Editor(args) {
|
||||||
|
|
||||||
|
var self = this,
|
||||||
|
// Private
|
||||||
|
userid,
|
||||||
|
memberid = args.memberid,
|
||||||
|
session,
|
||||||
|
editorSession,
|
||||||
|
userList,
|
||||||
|
networked = args.networked === true,
|
||||||
|
opRouter,
|
||||||
|
userModel,
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.currentDirectory = function () {
|
||||||
|
return "../../webodf/lib";
|
||||||
|
};
|
||||||
|
runtime.libraryPaths = function () {
|
||||||
|
return [ runtime.currentDirectory() ];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, peoplePane,
|
||||||
|
inviteButton,
|
||||||
|
viewOptions = {
|
||||||
|
editInfoMarkersInitiallyVisible: networked,
|
||||||
|
caretAvatarsInitiallyVisible: networked
|
||||||
|
},
|
||||||
|
peopleListDiv = document.getElementById('peopleList');
|
||||||
|
|
||||||
|
if (networked) {
|
||||||
|
runtime.assert(peopleListDiv, "missing peopleList 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
|
||||||
|
editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager());
|
||||||
|
editorSession.startEditing();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!memberid) {
|
||||||
|
// legacy - memberid should be passed in the constructor
|
||||||
|
memberid = (userid || 'undefined') + "___" + Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
session = new ops.Session(odfCanvas);
|
||||||
|
editorSession = new EditorSession(session, memberid, {
|
||||||
|
viewOptions: viewOptions
|
||||||
|
});
|
||||||
|
|
||||||
|
if (peopleListDiv) {
|
||||||
|
userList = new UserList(editorSession, peopleListDiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 && peopleListDiv) {
|
||||||
|
peoplePane = new ContentPane({
|
||||||
|
region: 'right',
|
||||||
|
title: translator("people")
|
||||||
|
}, 'people');
|
||||||
|
mainContainer.addChild(peoplePane);
|
||||||
|
}
|
||||||
|
|
||||||
|
mainContainer.startup();
|
||||||
|
|
||||||
|
if (window.inviteButtonProxy) {
|
||||||
|
inviteButton = document.getElementById('inviteButton');
|
||||||
|
if (inviteButton) {
|
||||||
|
inviteButton.innerText = translator("invitePeople");
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a document in an editor that has already been initialized.
|
||||||
|
*/
|
||||||
|
self.loadDocument = function (docUrl) {
|
||||||
|
editorSession.endEditing();
|
||||||
|
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("/session/" + sessionId + "/genesis", function () {
|
||||||
|
// use the nowjs op-router when connected
|
||||||
|
opRouter = opRouter || new ops.NowjsOperationRouter(sessionId, memberid);
|
||||||
|
session.setOperationRouter(opRouter);
|
||||||
|
|
||||||
|
userModel = userModel || new ops.NowjsUserModel();
|
||||||
|
session.setUserModel(userModel);
|
||||||
|
|
||||||
|
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 user model
|
||||||
|
self.getUserModel = function () {
|
||||||
|
return userModel;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Editor;
|
||||||
|
});
|
||||||
|
|
||||||
|
// vim:expandtab
|
@ -0,0 +1,492 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright (C) 2012 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 define,runtime,gui,ops,document */
|
||||||
|
define("webodf/editor/EditorSession", [
|
||||||
|
"dojo/text!resources/fonts/fonts.css"
|
||||||
|
], function (fontsCSS) { // fontsCSS is retrieved as a string, using dojo's text retrieval AMD plugin
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
runtime.libraryPaths = function () {
|
||||||
|
return [ "../../webodf/lib" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
runtime.loadClass("ops.OdtDocument");
|
||||||
|
runtime.loadClass("ops.Session");
|
||||||
|
runtime.loadClass("ops.NowjsOperationRouter");
|
||||||
|
runtime.loadClass("ops.NowjsUserModel");
|
||||||
|
runtime.loadClass("odf.OdfCanvas");
|
||||||
|
runtime.loadClass("gui.CaretFactory");
|
||||||
|
runtime.loadClass("gui.Caret");
|
||||||
|
runtime.loadClass("gui.SessionController");
|
||||||
|
runtime.loadClass("gui.SessionView");
|
||||||
|
runtime.loadClass("gui.TrivialUndoManager");
|
||||||
|
runtime.loadClass("core.EventNotifier");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a new editor session attached to an existing operation session
|
||||||
|
* @param {!ops.Session} session
|
||||||
|
* @param {!string} memberid
|
||||||
|
* @param {{viewOptions:gui.SessionViewOptions}} config
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
var EditorSession = function EditorSession(session, memberid, config) {
|
||||||
|
var self = this,
|
||||||
|
currentParagraphNode = null,
|
||||||
|
currentNamedStyleName = null,
|
||||||
|
currentStyleName = null,
|
||||||
|
odtDocument = session.getOdtDocument(),
|
||||||
|
textns = "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
|
||||||
|
formatting = odtDocument.getFormatting(),
|
||||||
|
eventNotifier = new core.EventNotifier([
|
||||||
|
EditorSession.signalUserAdded,
|
||||||
|
EditorSession.signalUserRemoved,
|
||||||
|
EditorSession.signalCursorMoved,
|
||||||
|
EditorSession.signalParagraphChanged,
|
||||||
|
EditorSession.signalStyleCreated,
|
||||||
|
EditorSession.signalStyleDeleted,
|
||||||
|
EditorSession.signalParagraphStyleModified,
|
||||||
|
EditorSession.signalUndoStackChanged]);
|
||||||
|
|
||||||
|
|
||||||
|
this.sessionController = new gui.SessionController(session, memberid);
|
||||||
|
this.sessionView = new gui.SessionView(config.viewOptions, session, new gui.CaretFactory(self.sessionController));
|
||||||
|
this.availableFonts = [];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @return {Array.{!string}}
|
||||||
|
*/
|
||||||
|
function getAvailableFonts() {
|
||||||
|
var availableFonts, regex, matches;
|
||||||
|
|
||||||
|
availableFonts = {};
|
||||||
|
|
||||||
|
regex = /font-family *: *(?:\'([^']*)\'|\"([^"]*)\")/gm;
|
||||||
|
matches = regex.exec(fontsCSS);
|
||||||
|
|
||||||
|
while (matches) {
|
||||||
|
availableFonts[matches[1] || matches[2]] = 1;
|
||||||
|
matches = regex.exec(fontsCSS);
|
||||||
|
}
|
||||||
|
availableFonts = Object.keys(availableFonts);
|
||||||
|
|
||||||
|
return availableFonts;
|
||||||
|
}
|
||||||
|
this.availableFonts = getAvailableFonts();
|
||||||
|
|
||||||
|
function checkParagraphStyleName() {
|
||||||
|
var newStyleName,
|
||||||
|
newNamedStyleName;
|
||||||
|
|
||||||
|
newStyleName = currentParagraphNode.getAttributeNS(textns, 'style-name');
|
||||||
|
if (newStyleName !== currentStyleName) {
|
||||||
|
currentStyleName = newStyleName;
|
||||||
|
// check if named style is still the same
|
||||||
|
newNamedStyleName = formatting.getFirstNamedParentStyleNameOrSelf(newStyleName);
|
||||||
|
if (!newNamedStyleName) {
|
||||||
|
// TODO: how to handle default styles?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// a named style
|
||||||
|
if (newNamedStyleName !== currentNamedStyleName) {
|
||||||
|
currentNamedStyleName = newNamedStyleName;
|
||||||
|
self.emit(EditorSession.signalParagraphChanged, {
|
||||||
|
type: 'style',
|
||||||
|
node: currentParagraphNode,
|
||||||
|
styleName: currentNamedStyleName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates a NCName from the passed string
|
||||||
|
* @param {!string} name
|
||||||
|
* @return {!string}
|
||||||
|
*/
|
||||||
|
function createNCName(name) {
|
||||||
|
var letter,
|
||||||
|
result = "";
|
||||||
|
|
||||||
|
// encode
|
||||||
|
for (var i = 0; i < name.length; i++) {
|
||||||
|
letter = name[i];
|
||||||
|
// simple approach, can be improved to not skip other allowed chars
|
||||||
|
if (letter.match(/[a-zA-Z0-9.-_]/) !== null) {
|
||||||
|
result += letter;
|
||||||
|
} else {
|
||||||
|
result += "_" + letter.charCodeAt(0).toString(16) + "_";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ensure leading char is from proper range
|
||||||
|
if (result.match(/^[a-zA-Z_]/) === null) {
|
||||||
|
result = "_" + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function uniqueParagraphStyleNCName(name) {
|
||||||
|
var result,
|
||||||
|
i = 0,
|
||||||
|
ncMemberId = createNCName(memberid),
|
||||||
|
ncName = createNCName(name);
|
||||||
|
|
||||||
|
// create default paragraph style
|
||||||
|
// memberid is used to avoid id conflicts with ids created by other users
|
||||||
|
result = ncName + "_" + ncMemberId;
|
||||||
|
// then loop until result is really unique
|
||||||
|
while(formatting.hasParagraphStyle(result)) {
|
||||||
|
result = ncName + "_" + i + "_" + ncMemberId;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function trackCursor(cursor) {
|
||||||
|
var node;
|
||||||
|
|
||||||
|
node = odtDocument.getParagraphElement(cursor.getNode());
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentParagraphNode = node;
|
||||||
|
checkParagraphStyleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
function trackCurrentParagraph(info) {
|
||||||
|
if (info.paragraphElement !== currentParagraphNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkParagraphStyleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom signals, that make sense in the Editor context. We do not want to expose webodf's ops signals to random bits of the editor UI.
|
||||||
|
odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, function (cursor) {
|
||||||
|
self.emit(EditorSession.signalUserAdded, cursor.getMemberId());
|
||||||
|
trackCursor(cursor);
|
||||||
|
});
|
||||||
|
|
||||||
|
odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, function (memberId) {
|
||||||
|
self.emit(EditorSession.signalUserRemoved, memberId);
|
||||||
|
});
|
||||||
|
|
||||||
|
odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, function (cursor) {
|
||||||
|
// Emit 'cursorMoved' only when *I* am moving the cursor, not the other users
|
||||||
|
if (cursor.getMemberId() === memberid) {
|
||||||
|
self.emit(EditorSession.signalCursorMoved, cursor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
odtDocument.subscribe(ops.OdtDocument.signalStyleCreated, function (newStyleName) {
|
||||||
|
self.emit(EditorSession.signalStyleCreated, newStyleName);
|
||||||
|
});
|
||||||
|
|
||||||
|
odtDocument.subscribe(ops.OdtDocument.signalStyleDeleted, function (styleName) {
|
||||||
|
self.emit(EditorSession.signalStyleDeleted, styleName);
|
||||||
|
});
|
||||||
|
|
||||||
|
odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, function (styleName) {
|
||||||
|
self.emit(EditorSession.signalParagraphStyleModified, styleName);
|
||||||
|
});
|
||||||
|
|
||||||
|
odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, trackCurrentParagraph);
|
||||||
|
|
||||||
|
this.startEditing = function () {
|
||||||
|
self.sessionController.startEditing();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.endEditing = function () {
|
||||||
|
self.sessionController.endEditing();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call all subscribers for the given event with the specified argument
|
||||||
|
* @param {!string} eventid
|
||||||
|
* @param {Object} args
|
||||||
|
*/
|
||||||
|
this.emit = function (eventid, args) {
|
||||||
|
eventNotifier.emit(eventid, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to a given event with a callback
|
||||||
|
* @param {!string} eventid
|
||||||
|
* @param {!Function} cb
|
||||||
|
*/
|
||||||
|
this.subscribe = function (eventid, cb) {
|
||||||
|
eventNotifier.subscribe(eventid, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getUserDetailsAndUpdates = function (memberId, subscriber) {
|
||||||
|
return session.getUserModel().getUserDetailsAndUpdates(memberId, subscriber);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.unsubscribeUserDetailsUpdates = function (memberId, subscriber) {
|
||||||
|
return session.getUserModel().unsubscribeUserDetailsUpdates(memberId, subscriber);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getCursorPosition = function () {
|
||||||
|
return odtDocument.getCursorPosition(memberid);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getCursorSelection = function() {
|
||||||
|
return odtDocument.getCursorSelection(memberid);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getOdfCanvas = function () {
|
||||||
|
return odtDocument.getOdfCanvas();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getCurrentParagraph = function () {
|
||||||
|
return currentParagraphNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getAvailableParagraphStyles = function () {
|
||||||
|
return formatting.getAvailableParagraphStyles();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getCurrentSelectionStyle = function() {
|
||||||
|
var cursor = odtDocument.getCursor(memberid),
|
||||||
|
selectedRange;
|
||||||
|
// no own cursor yet/currently added?
|
||||||
|
if (!cursor) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
selectedRange = cursor.getSelectedRange();
|
||||||
|
if (selectedRange.collapsed) {
|
||||||
|
return [formatting.getAppliedStylesForElement(cursor.getNode())];
|
||||||
|
}
|
||||||
|
return formatting.getAppliedStyles(selectedRange);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getCurrentParagraphStyle = function () {
|
||||||
|
return currentNamedStyleName;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.formatSelection = function(value) {
|
||||||
|
var op = new ops.OpApplyStyle(),
|
||||||
|
selection = self.getCursorSelection();
|
||||||
|
op.init({
|
||||||
|
memberid: memberid,
|
||||||
|
position: selection.position,
|
||||||
|
length: selection.length,
|
||||||
|
info: value
|
||||||
|
});
|
||||||
|
session.enqueue(op);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setCurrentParagraphStyle = function (value) {
|
||||||
|
var op;
|
||||||
|
if (currentNamedStyleName !== value) {
|
||||||
|
op = new ops.OpSetParagraphStyle();
|
||||||
|
op.init({
|
||||||
|
memberid: memberid,
|
||||||
|
position: self.getCursorPosition(),
|
||||||
|
styleName: value
|
||||||
|
});
|
||||||
|
session.enqueue(op);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.insertTable = function(initialRows, initialColumns, tableStyleName, tableColumnStyleName, tableCellStyleMatrix) {
|
||||||
|
var op = new ops.OpInsertTable();
|
||||||
|
op.init({
|
||||||
|
memberid: memberid,
|
||||||
|
position: self.getCursorPosition(),
|
||||||
|
initialRows: initialRows,
|
||||||
|
initialColumns: initialColumns,
|
||||||
|
tableStyleName: tableStyleName,
|
||||||
|
tableColumnStyleName: tableColumnStyleName,
|
||||||
|
tableCellStyleMatrix: tableCellStyleMatrix
|
||||||
|
});
|
||||||
|
session.enqueue(op);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getParagraphStyleElement = function (styleName) {
|
||||||
|
return odtDocument.getParagraphStyleElement(styleName);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if the style is used anywhere in the document
|
||||||
|
* @param {!Element} styleElement
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
this.isStyleUsed = function (styleElement) {
|
||||||
|
return formatting.isStyleUsed(styleElement);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getParagraphStyleAttributes = function (styleName) {
|
||||||
|
return odtDocument.getParagraphStyleAttributes(styleName);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and enqueues a paragraph-style cloning operation.
|
||||||
|
* Returns the created id for the new style.
|
||||||
|
* @param {!string} styleName id of the style to update
|
||||||
|
* @param {!{paragraphProperties,textProperties}} setProperties properties which are set
|
||||||
|
* @param {!{paragraphPropertyNames,textPropertyNames}=} removedProperties properties which are removed
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
this.updateParagraphStyle = function (styleName, setProperties, removedProperties) {
|
||||||
|
var op;
|
||||||
|
op = new ops.OpUpdateParagraphStyle();
|
||||||
|
op.init({
|
||||||
|
memberid: memberid,
|
||||||
|
styleName: styleName,
|
||||||
|
setProperties: setProperties,
|
||||||
|
removedProperties: (!removedProperties) ? {} : removedProperties
|
||||||
|
});
|
||||||
|
session.enqueue(op);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and enqueues a paragraph-style cloning operation.
|
||||||
|
* Returns the created id for the new style.
|
||||||
|
* @param {!string} styleName id of the style to clone
|
||||||
|
* @param {!string} newStyleDisplayName display name of the new style
|
||||||
|
* @return {!string}
|
||||||
|
*/
|
||||||
|
this.cloneParagraphStyle = function (styleName, newStyleDisplayName) {
|
||||||
|
var newStyleName = uniqueParagraphStyleNCName(newStyleDisplayName),
|
||||||
|
op;
|
||||||
|
|
||||||
|
op = new ops.OpCloneParagraphStyle();
|
||||||
|
op.init({
|
||||||
|
memberid: memberid,
|
||||||
|
styleName: styleName,
|
||||||
|
newStyleName: newStyleName,
|
||||||
|
newStyleDisplayName: newStyleDisplayName
|
||||||
|
});
|
||||||
|
session.enqueue(op);
|
||||||
|
|
||||||
|
return newStyleName;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.deleteStyle = function (styleName) {
|
||||||
|
var op;
|
||||||
|
op = new ops.OpDeleteParagraphStyle();
|
||||||
|
op.init({
|
||||||
|
memberid: memberid,
|
||||||
|
styleName: styleName
|
||||||
|
});
|
||||||
|
session.enqueue(op);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of the declared fonts in the ODF document,
|
||||||
|
* with 'duplicates' like Arial1, Arial2, etc removed. The alphabetically
|
||||||
|
* first font name for any given family is kept.
|
||||||
|
* The elements of the array are objects containing the font's name and
|
||||||
|
* the family.
|
||||||
|
* @return {Array.{Object}}
|
||||||
|
*/
|
||||||
|
this.getDeclaredFonts = function () {
|
||||||
|
var fontMap = formatting.getFontMap(),
|
||||||
|
usedFamilies = [],
|
||||||
|
array = [],
|
||||||
|
sortedNames,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
i;
|
||||||
|
|
||||||
|
// Sort all the keys in the font map alphabetically
|
||||||
|
sortedNames = Object.keys(fontMap);
|
||||||
|
sortedNames.sort();
|
||||||
|
|
||||||
|
for (i = 0; i < sortedNames.length; i += 1) {
|
||||||
|
key = sortedNames[i];
|
||||||
|
value = fontMap[key];
|
||||||
|
|
||||||
|
// Use the font declaration only if the family is not already used.
|
||||||
|
// Therefore we are able to discard the alphabetic successors of the first
|
||||||
|
// font name.
|
||||||
|
if (usedFamilies.indexOf(value) === -1) {
|
||||||
|
array.push({
|
||||||
|
name: key,
|
||||||
|
family: value
|
||||||
|
});
|
||||||
|
if (value) {
|
||||||
|
usedFamilies.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
|
||||||
|
function undoStackModified(e) {
|
||||||
|
self.emit(EditorSession.signalUndoStackChanged, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hasUndoManager = function() {
|
||||||
|
return Boolean(self.sessionController.getUndoManager());
|
||||||
|
};
|
||||||
|
|
||||||
|
this.undo = function() {
|
||||||
|
var undoManager = self.sessionController.getUndoManager();
|
||||||
|
undoManager.moveBackward(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.redo = function() {
|
||||||
|
var undoManager = self.sessionController.getUndoManager();
|
||||||
|
undoManager.moveForward(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.subscribe(EditorSession.signalCursorMoved, trackCursor);
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
var head = document.getElementsByTagName('head')[0],
|
||||||
|
fontStyles = document.createElement('style');
|
||||||
|
fontStyles.type = 'text/css';
|
||||||
|
fontStyles.media = 'screen, print, handheld, projection';
|
||||||
|
fontStyles.appendChild(document.createTextNode(fontsCSS));
|
||||||
|
head.appendChild(fontStyles);
|
||||||
|
odtDocument.subscribe(ops.OdtDocument.signalUndoStackChanged, undoStackModified);
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**@const*/EditorSession.signalUserAdded = "userAdded";
|
||||||
|
/**@const*/EditorSession.signalUserRemoved = "userRemoved";
|
||||||
|
/**@const*/EditorSession.signalCursorMoved = "cursorMoved";
|
||||||
|
/**@const*/EditorSession.signalParagraphChanged = "paragraphChanged";
|
||||||
|
/**@const*/EditorSession.signalStyleCreated = "styleCreated";
|
||||||
|
/**@const*/EditorSession.signalStyleDeleted = "styleDeleted";
|
||||||
|
/**@const*/EditorSession.signalParagraphStyleModified = "paragraphStyleModified";
|
||||||
|
/**@const*/EditorSession.signalUndoStackChanged = "signalUndoStackChanged";
|
||||||
|
|
||||||
|
return EditorSession;
|
||||||
|
});
|
@ -0,0 +1,216 @@
|
|||||||
|
/* FileSaver.js
|
||||||
|
* A saveAs() FileSaver implementation.
|
||||||
|
* 2013-01-23
|
||||||
|
*
|
||||||
|
* By Eli Grey, http://eligrey.com
|
||||||
|
* License: X11/MIT
|
||||||
|
* See LICENSE.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global self */
|
||||||
|
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
|
||||||
|
plusplus: true */
|
||||||
|
|
||||||
|
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
|
||||||
|
|
||||||
|
var saveAs = saveAs
|
||||||
|
|| (navigator.msSaveBlob && navigator.msSaveBlob.bind(navigator))
|
||||||
|
|| (function(view) {
|
||||||
|
"use strict";
|
||||||
|
var
|
||||||
|
doc = view.document
|
||||||
|
// only get URL when necessary in case BlobBuilder.js hasn't overridden it yet
|
||||||
|
, get_URL = function() {
|
||||||
|
return view.URL || view.webkitURL || view;
|
||||||
|
}
|
||||||
|
, URL = view.URL || view.webkitURL || view
|
||||||
|
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
|
||||||
|
, can_use_save_link = "download" in save_link
|
||||||
|
, click = function(node) {
|
||||||
|
var event = doc.createEvent("MouseEvents");
|
||||||
|
event.initMouseEvent(
|
||||||
|
"click", true, false, view, 0, 0, 0, 0, 0
|
||||||
|
, false, false, false, false, 0, null
|
||||||
|
);
|
||||||
|
node.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
, webkit_req_fs = view.webkitRequestFileSystem
|
||||||
|
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
|
||||||
|
, throw_outside = function (ex) {
|
||||||
|
(view.setImmediate || view.setTimeout)(function() {
|
||||||
|
throw ex;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
, force_saveable_type = "application/octet-stream"
|
||||||
|
, fs_min_size = 0
|
||||||
|
, deletion_queue = []
|
||||||
|
, process_deletion_queue = function() {
|
||||||
|
var i = deletion_queue.length;
|
||||||
|
while (i--) {
|
||||||
|
var file = deletion_queue[i];
|
||||||
|
if (typeof file === "string") { // file is an object URL
|
||||||
|
URL.revokeObjectURL(file);
|
||||||
|
} else { // file is a File
|
||||||
|
file.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deletion_queue.length = 0; // clear queue
|
||||||
|
}
|
||||||
|
, dispatch = function(filesaver, event_types, event) {
|
||||||
|
event_types = [].concat(event_types);
|
||||||
|
var i = event_types.length;
|
||||||
|
while (i--) {
|
||||||
|
var listener = filesaver["on" + event_types[i]];
|
||||||
|
if (typeof listener === "function") {
|
||||||
|
try {
|
||||||
|
listener.call(filesaver, event || filesaver);
|
||||||
|
} catch (ex) {
|
||||||
|
throw_outside(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, FileSaver = function(blob, name) {
|
||||||
|
// First try a.download, then web filesystem, then object URLs
|
||||||
|
var
|
||||||
|
filesaver = this
|
||||||
|
, type = blob.type
|
||||||
|
, blob_changed = false
|
||||||
|
, object_url
|
||||||
|
, target_view
|
||||||
|
, get_object_url = function() {
|
||||||
|
var object_url = get_URL().createObjectURL(blob);
|
||||||
|
deletion_queue.push(object_url);
|
||||||
|
return object_url;
|
||||||
|
}
|
||||||
|
, dispatch_all = function() {
|
||||||
|
dispatch(filesaver, "writestart progress write writeend".split(" "));
|
||||||
|
}
|
||||||
|
// on any filesys errors revert to saving with object URLs
|
||||||
|
, fs_error = function() {
|
||||||
|
// don't create more object URLs than needed
|
||||||
|
if (blob_changed || !object_url) {
|
||||||
|
object_url = get_object_url(blob);
|
||||||
|
}
|
||||||
|
if (target_view) {
|
||||||
|
target_view.location.href = object_url;
|
||||||
|
} else {
|
||||||
|
window.open(object_url, "_blank");
|
||||||
|
}
|
||||||
|
filesaver.readyState = filesaver.DONE;
|
||||||
|
dispatch_all();
|
||||||
|
}
|
||||||
|
, abortable = function(func) {
|
||||||
|
return function() {
|
||||||
|
if (filesaver.readyState !== filesaver.DONE) {
|
||||||
|
return func.apply(this, arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
, create_if_not_found = {create: true, exclusive: false}
|
||||||
|
, slice
|
||||||
|
;
|
||||||
|
filesaver.readyState = filesaver.INIT;
|
||||||
|
if (!name) {
|
||||||
|
name = "download";
|
||||||
|
}
|
||||||
|
if (can_use_save_link) {
|
||||||
|
object_url = get_object_url(blob);
|
||||||
|
save_link.href = object_url;
|
||||||
|
save_link.download = name;
|
||||||
|
click(save_link);
|
||||||
|
filesaver.readyState = filesaver.DONE;
|
||||||
|
dispatch_all();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Object and web filesystem URLs have a problem saving in Google Chrome when
|
||||||
|
// viewed in a tab, so I force save with application/octet-stream
|
||||||
|
// http://code.google.com/p/chromium/issues/detail?id=91158
|
||||||
|
if (view.chrome && type && type !== force_saveable_type) {
|
||||||
|
slice = blob.slice || blob.webkitSlice;
|
||||||
|
blob = slice.call(blob, 0, blob.size, force_saveable_type);
|
||||||
|
blob_changed = true;
|
||||||
|
}
|
||||||
|
// Since I can't be sure that the guessed media type will trigger a download
|
||||||
|
// in WebKit, I append .download to the filename.
|
||||||
|
// https://bugs.webkit.org/show_bug.cgi?id=65440
|
||||||
|
if (webkit_req_fs && name !== "download") {
|
||||||
|
name += ".download";
|
||||||
|
}
|
||||||
|
if (type === force_saveable_type || webkit_req_fs) {
|
||||||
|
target_view = view;
|
||||||
|
}
|
||||||
|
if (!req_fs) {
|
||||||
|
fs_error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs_min_size += blob.size;
|
||||||
|
req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
|
||||||
|
fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
|
||||||
|
var save = function() {
|
||||||
|
dir.getFile(name, create_if_not_found, abortable(function(file) {
|
||||||
|
file.createWriter(abortable(function(writer) {
|
||||||
|
writer.onwriteend = function(event) {
|
||||||
|
target_view.location.href = file.toURL();
|
||||||
|
deletion_queue.push(file);
|
||||||
|
filesaver.readyState = filesaver.DONE;
|
||||||
|
dispatch(filesaver, "writeend", event);
|
||||||
|
};
|
||||||
|
writer.onerror = function() {
|
||||||
|
var error = writer.error;
|
||||||
|
if (error.code !== error.ABORT_ERR) {
|
||||||
|
fs_error();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"writestart progress write abort".split(" ").forEach(function(event) {
|
||||||
|
writer["on" + event] = filesaver["on" + event];
|
||||||
|
});
|
||||||
|
writer.write(blob);
|
||||||
|
filesaver.abort = function() {
|
||||||
|
writer.abort();
|
||||||
|
filesaver.readyState = filesaver.DONE;
|
||||||
|
};
|
||||||
|
filesaver.readyState = filesaver.WRITING;
|
||||||
|
}), fs_error);
|
||||||
|
}), fs_error);
|
||||||
|
};
|
||||||
|
dir.getFile(name, {create: false}, abortable(function(file) {
|
||||||
|
// delete file if it already exists
|
||||||
|
file.remove();
|
||||||
|
save();
|
||||||
|
}), abortable(function(ex) {
|
||||||
|
if (ex.code === ex.NOT_FOUND_ERR) {
|
||||||
|
save();
|
||||||
|
} else {
|
||||||
|
fs_error();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}), fs_error);
|
||||||
|
}), fs_error);
|
||||||
|
}
|
||||||
|
, FS_proto = FileSaver.prototype
|
||||||
|
, saveAs = function(blob, name) {
|
||||||
|
return new FileSaver(blob, name);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
FS_proto.abort = function() {
|
||||||
|
var filesaver = this;
|
||||||
|
filesaver.readyState = filesaver.DONE;
|
||||||
|
dispatch(filesaver, "abort");
|
||||||
|
};
|
||||||
|
FS_proto.readyState = FS_proto.INIT = 0;
|
||||||
|
FS_proto.WRITING = 1;
|
||||||
|
FS_proto.DONE = 2;
|
||||||
|
|
||||||
|
FS_proto.error =
|
||||||
|
FS_proto.onwritestart =
|
||||||
|
FS_proto.onprogress =
|
||||||
|
FS_proto.onwrite =
|
||||||
|
FS_proto.onabort =
|
||||||
|
FS_proto.onerror =
|
||||||
|
FS_proto.onwriteend =
|
||||||
|
null;
|
||||||
|
|
||||||
|
view.addEventListener("unload", process_deletion_queue, false);
|
||||||
|
return saveAs;
|
||||||
|
}(self));
|
@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2012 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 ops, runtime */
|
||||||
|
|
||||||
|
function SessionList(net) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var cachedSessionData = {},
|
||||||
|
subscribers = [];
|
||||||
|
|
||||||
|
function onSessionData(sessionData) {
|
||||||
|
var i,
|
||||||
|
isNew = ! cachedSessionData.hasOwnProperty(sessionData.id);
|
||||||
|
|
||||||
|
// cache
|
||||||
|
cachedSessionData[sessionData.id] = sessionData;
|
||||||
|
runtime.log("get session data for:"+sessionData.title+", is new:"+isNew);
|
||||||
|
|
||||||
|
for (i = 0; i < subscribers.length; i += 1) {
|
||||||
|
if (isNew) {
|
||||||
|
subscribers[i].onCreated(sessionData);
|
||||||
|
} else {
|
||||||
|
subscribers[i].onUpdated(sessionData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSessionRemoved(sessionId) {
|
||||||
|
var i;
|
||||||
|
|
||||||
|
if (cachedSessionData.hasOwnProperty(sessionId)) {
|
||||||
|
delete cachedSessionData[sessionId];
|
||||||
|
|
||||||
|
for (i = 0; i < subscribers.length; i += 1) {
|
||||||
|
subscribers[i].onRemoved(sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getSessions = function (subscriber) {
|
||||||
|
var i,
|
||||||
|
sessionList = [];
|
||||||
|
|
||||||
|
if (subscriber) {
|
||||||
|
subscribers.push(subscriber);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in cachedSessionData) {
|
||||||
|
if (cachedSessionData.hasOwnProperty(i)) {
|
||||||
|
sessionList.push(cachedSessionData[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessionList;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.unsubscribe = function (subscriber) {
|
||||||
|
var i;
|
||||||
|
|
||||||
|
for (i=0; i<subscribers.length; i+=1) {
|
||||||
|
if (subscribers[i] === subscriber) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.assert((i < subscribers.length),
|
||||||
|
"tried to unsubscribe when not subscribed.");
|
||||||
|
|
||||||
|
subscribers.splice(i,1);
|
||||||
|
};
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
net.onSessionAdded = onSessionData;
|
||||||
|
net.onSessionChanged = onSessionData;
|
||||||
|
net.onSessionRemoved = onSessionRemoved;
|
||||||
|
|
||||||
|
net.getSessionList( function(sessionList) {
|
||||||
|
var idx;
|
||||||
|
runtime.log("get sessions on init:"+sessionList.length);
|
||||||
|
for (idx=0; idx<sessionList.length; idx+=1) {
|
||||||
|
onSessionData(sessionList[idx])
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright (C) 2012 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 Node, define, runtime */
|
||||||
|
|
||||||
|
function SessionListView(sessionList, sessionListDiv, cb) {
|
||||||
|
"use strict";
|
||||||
|
var self = this,
|
||||||
|
memberDataChangedHandler;
|
||||||
|
|
||||||
|
function createSessionDescription(sessionDetails) {
|
||||||
|
return sessionDetails.title + " ("+sessionDetails.cursors.length+" members)";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function createSessionViewItem(sessionDetails) {
|
||||||
|
runtime.assert(sessionListDiv, "sessionListDiv unavailable");
|
||||||
|
var doc = sessionListDiv.ownerDocument,
|
||||||
|
htmlns = doc.documentElement.namespaceURI,
|
||||||
|
sessionDiv = doc.createElementNS(htmlns, "div"),
|
||||||
|
fullnameTextNode = doc.createTextNode(createSessionDescription(sessionDetails));
|
||||||
|
|
||||||
|
sessionDiv.appendChild(fullnameTextNode);
|
||||||
|
sessionDiv.sessionId = sessionDetails.id; // TODO: namespace?
|
||||||
|
sessionDiv.onclick = function () {
|
||||||
|
cb(sessionDetails.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
sessionListDiv.appendChild(sessionDiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSessionViewItem(sessionDetails) {
|
||||||
|
var node = sessionListDiv.firstChild;
|
||||||
|
while (node) {
|
||||||
|
if (node.sessionId === sessionDetails.id) {
|
||||||
|
node = node.firstChild;
|
||||||
|
while (node) {
|
||||||
|
if (node.nodeType == Node.TEXT_NODE) {
|
||||||
|
node.data = createSessionDescription(sessionDetails);
|
||||||
|
}
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!string} sessionId
|
||||||
|
*/
|
||||||
|
function removeSessionViewItem(sessionId) {
|
||||||
|
var node = sessionListDiv.firstChild;
|
||||||
|
while (node) {
|
||||||
|
if (node.sessionId === sessionId) {
|
||||||
|
sessionListDiv.removeChild(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
var idx,
|
||||||
|
subscriber = {onCreated: createSessionViewItem, onUpdated: updateSessionViewItem, onRemoved: removeSessionViewItem},
|
||||||
|
sessions = sessionList.getSessions(subscriber);
|
||||||
|
|
||||||
|
// fill session list
|
||||||
|
for (idx = 0; idx < sessions.length; idx += 1) {
|
||||||
|
createSessionViewItem(sessions[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* 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 define,runtime */
|
||||||
|
define("webodf/editor/UserList",
|
||||||
|
["webodf/editor/EditorSession"],
|
||||||
|
|
||||||
|
function (EditorSession) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
return function UserList(editorSession, userListDiv) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
editorSession.subscribe(EditorSession.signalUserAdded, function (memberId) {
|
||||||
|
self.addUser(memberId);
|
||||||
|
});
|
||||||
|
|
||||||
|
editorSession.subscribe(EditorSession.signalUserRemoved, function (memberId) {
|
||||||
|
self.removeUser(memberId);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!string} memberId
|
||||||
|
*/
|
||||||
|
function updateAvatarButton(memberId, userDetails) {
|
||||||
|
var node = userListDiv.firstChild;
|
||||||
|
if (userDetails === null) {
|
||||||
|
// 'null' here means finally unknown user
|
||||||
|
// (and not that the data is still loading)
|
||||||
|
userDetails = {
|
||||||
|
memberid: memberId, fullname: "Unknown",
|
||||||
|
color: "black", imageurl: "avatar-joe.png"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
while (node) {
|
||||||
|
if (node.memberId === memberId) {
|
||||||
|
node = node.firstChild;
|
||||||
|
while (node) {
|
||||||
|
if (node.localName === "img") {
|
||||||
|
// update avatar image
|
||||||
|
node.src = userDetails.imageurl;
|
||||||
|
// update border color
|
||||||
|
node.style.borderColor = userDetails.color;
|
||||||
|
} else if (node.localName === "div") {
|
||||||
|
node.setAttribute('fullname', userDetails.fullname);
|
||||||
|
}
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!string} memberId
|
||||||
|
*/
|
||||||
|
function createAvatarButton(memberId) {
|
||||||
|
runtime.assert(userListDiv, "userListDiv unavailable");
|
||||||
|
var doc = userListDiv.ownerDocument,
|
||||||
|
htmlns = doc.documentElement.namespaceURI,
|
||||||
|
avatarDiv = doc.createElementNS(htmlns, "div"),
|
||||||
|
imageElement = doc.createElement("img"),
|
||||||
|
fullnameNode = doc.createElement("div");
|
||||||
|
|
||||||
|
avatarDiv.className = "userListButton";
|
||||||
|
fullnameNode.className = "userListLabel";
|
||||||
|
avatarDiv.appendChild(imageElement);
|
||||||
|
avatarDiv.appendChild(fullnameNode);
|
||||||
|
avatarDiv.memberId = memberId; // TODO: namespace?
|
||||||
|
|
||||||
|
avatarDiv.onmouseover = function () {
|
||||||
|
//avatar.getCaret().showHandle();
|
||||||
|
};
|
||||||
|
avatarDiv.onmouseout = function () {
|
||||||
|
//avatar.getCaret().hideHandle();
|
||||||
|
};
|
||||||
|
avatarDiv.onclick = function () {
|
||||||
|
var caret = editorSession.sessionView.getCaret(memberId);
|
||||||
|
if (caret) {
|
||||||
|
caret.toggleHandleVisibility();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
userListDiv.appendChild(avatarDiv);
|
||||||
|
|
||||||
|
// preset bogus data
|
||||||
|
// TODO: indicate loading state
|
||||||
|
// (instead of setting the final 'unknown identity' data)
|
||||||
|
updateAvatarButton(memberId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!string} memberId
|
||||||
|
*/
|
||||||
|
function removeAvatarButton(memberId) {
|
||||||
|
var node = userListDiv.firstChild;
|
||||||
|
while (node) {
|
||||||
|
if (node.memberId === memberId) {
|
||||||
|
userListDiv.removeChild(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!string} memberId
|
||||||
|
*/
|
||||||
|
this.addUser = function (memberId) {
|
||||||
|
createAvatarButton(memberId);
|
||||||
|
editorSession.getUserDetailsAndUpdates(memberId, updateAvatarButton);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!string} memberId
|
||||||
|
*/
|
||||||
|
this.removeUser = function (memberId) {
|
||||||
|
editorSession.unsubscribeUserDetailsUpdates(memberId, updateAvatarButton);
|
||||||
|
removeAvatarButton(memberId);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
@ -0,0 +1,421 @@
|
|||||||
|
/**
|
||||||
|
* 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/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bootstrap the editor in different ways.
|
||||||
|
* this file is meant to be included from HTML and used
|
||||||
|
* by users who do not want to know much about the inner
|
||||||
|
* complexity.
|
||||||
|
* so we need to make it really easy.
|
||||||
|
*
|
||||||
|
* including this file will result in the namespace/object
|
||||||
|
* "webodfEditor" to be available from the HTML side.
|
||||||
|
* calling webodfEditor.boot() will start the editor.
|
||||||
|
* the method can also take some parameters to specify
|
||||||
|
* behaviour. see documentation of that method.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global runtime, require, document, alert, gui, window, SessionList, SessionListView, FileReader, Uint8Array */
|
||||||
|
|
||||||
|
// define the namespace/object we want to provide
|
||||||
|
// this is the first line of API, the user gets.
|
||||||
|
var webodfEditor = (function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var editorInstance = null,
|
||||||
|
booting = false,
|
||||||
|
loadedFilename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wait for a network connection through nowjs to establish.
|
||||||
|
* call the callback when done, when finally failed, or
|
||||||
|
* when a timeout reached.
|
||||||
|
* the parameter to the callback is a string with the possible
|
||||||
|
* values:
|
||||||
|
* "unavailable", "timeout", "ready"
|
||||||
|
*
|
||||||
|
* @param {!function(!string)} callback
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
function waitForNetwork(callback) {
|
||||||
|
var net = runtime.getNetwork(), accumulated_waiting_time = 0;
|
||||||
|
function later_cb() {
|
||||||
|
if (net.networkStatus === "unavailable") {
|
||||||
|
runtime.log("connection to server unavailable.");
|
||||||
|
callback("unavailable");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (net.networkStatus !== "ready") {
|
||||||
|
if (accumulated_waiting_time > 8000) {
|
||||||
|
// game over
|
||||||
|
runtime.log("connection to server timed out.");
|
||||||
|
callback("timeout");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
accumulated_waiting_time += 100;
|
||||||
|
runtime.getWindow().setTimeout(later_cb, 100);
|
||||||
|
} else {
|
||||||
|
runtime.log("connection to collaboration server established.");
|
||||||
|
callback("ready");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
later_cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extract document url from the url-fragment
|
||||||
|
*
|
||||||
|
* @return {?string}
|
||||||
|
*/
|
||||||
|
function guessDocUrl() {
|
||||||
|
var pos, docUrl = String(document.location);
|
||||||
|
// If the URL has a fragment (#...), try to load the file it represents
|
||||||
|
pos = docUrl.indexOf('#');
|
||||||
|
if (pos !== -1) {
|
||||||
|
docUrl = docUrl.substr(pos + 1);
|
||||||
|
} else {
|
||||||
|
docUrl = "welcome.odt";
|
||||||
|
}
|
||||||
|
return docUrl || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileSelectHandler(evt) {
|
||||||
|
var file, files, reader;
|
||||||
|
files = (evt.target && evt.target.files) ||
|
||||||
|
(evt.dataTransfer && evt.dataTransfer.files);
|
||||||
|
function onloadend() {
|
||||||
|
if (reader.readyState === 2) {
|
||||||
|
runtime.registerFile(file.name, reader.result);
|
||||||
|
loadedFilename = file.name;
|
||||||
|
editorInstance.loadDocument(file.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (files && files.length === 1) {
|
||||||
|
file = files[0];
|
||||||
|
reader = new FileReader();
|
||||||
|
reader.onloadend = onloadend;
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
} else {
|
||||||
|
alert("File could not be opened in this browser.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enhanceRuntime() {
|
||||||
|
var openedFiles = {},
|
||||||
|
read = runtime.read,
|
||||||
|
getFileSize = runtime.getFileSize;
|
||||||
|
runtime.read = function (path, offset, length, callback) {
|
||||||
|
var array;
|
||||||
|
if (openedFiles.hasOwnProperty(path)) {
|
||||||
|
array = new Uint8Array(openedFiles[path], offset, length);
|
||||||
|
callback(undefined, array);
|
||||||
|
} else {
|
||||||
|
return read(path, offset, length, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
runtime.getFileSize = function (path, callback) {
|
||||||
|
if (openedFiles.hasOwnProperty(path)) {
|
||||||
|
return callback(openedFiles[path].byteLength);
|
||||||
|
} else {
|
||||||
|
return getFileSize(path, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
runtime.registerFile = function (path, data) {
|
||||||
|
openedFiles[path] = data;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFileLoadForm() {
|
||||||
|
var form = document.createElement("form"),
|
||||||
|
input = document.createElement("input");
|
||||||
|
form.appendChild(input);
|
||||||
|
form.style.display = "none";
|
||||||
|
input.id = "fileloader";
|
||||||
|
input.setAttribute("type", "file");
|
||||||
|
input.addEventListener("change", fileSelectHandler, false);
|
||||||
|
document.body.appendChild(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
function load() {
|
||||||
|
var form = document.getElementById("fileloader");
|
||||||
|
if (!form) {
|
||||||
|
enhanceRuntime();
|
||||||
|
createFileLoadForm();
|
||||||
|
form = document.getElementById("fileloader");
|
||||||
|
}
|
||||||
|
form.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
editorInstance.saveDocument(loadedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a new editor instance, and start the editor with
|
||||||
|
* the given document.
|
||||||
|
*
|
||||||
|
* @param {!string} docUrl
|
||||||
|
* @param {?Object} editorOptions
|
||||||
|
* @param {?function(!Object)} editorReadyCallback
|
||||||
|
*/
|
||||||
|
function createLocalEditor(docUrl, editorOptions, editorReadyCallback) {
|
||||||
|
var pos;
|
||||||
|
booting = true;
|
||||||
|
editorOptions = editorOptions || {};
|
||||||
|
editorOptions.memberid = "localuser";
|
||||||
|
editorOptions.loadCallback = load;
|
||||||
|
editorOptions.saveCallback = save;
|
||||||
|
|
||||||
|
runtime.assert(docUrl, "docUrl needs to be specified");
|
||||||
|
runtime.assert(editorInstance === null, "cannot boot with instanciated editor");
|
||||||
|
|
||||||
|
document.getElementById("mainContainer").style.display = "";
|
||||||
|
|
||||||
|
require({ }, ["webodf/editor/Editor"],
|
||||||
|
function (Editor) {
|
||||||
|
editorInstance = new Editor(editorOptions);
|
||||||
|
editorInstance.initAndLoadDocument(docUrl, function (editorSession) {
|
||||||
|
editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager());
|
||||||
|
editorSession.startEditing();
|
||||||
|
editorReadyCallback(editorInstance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* assume the network connection is established, create a new editor instance,
|
||||||
|
* and start the editor on the network.
|
||||||
|
*
|
||||||
|
* @param {!string} sessionId
|
||||||
|
* @param {!string} userid
|
||||||
|
* @param {?string} token
|
||||||
|
* @param {?Object} editorOptions
|
||||||
|
* @param {?function(!Object)} editorReadyCallback
|
||||||
|
*/
|
||||||
|
function createNetworkedEditor(sessionId, userid, token, editorOptions, editorReadyCallback) {
|
||||||
|
|
||||||
|
runtime.assert(sessionId, "sessionId needs to be specified");
|
||||||
|
runtime.assert(editorInstance === null, "cannot boot with instanciated editor");
|
||||||
|
|
||||||
|
editorOptions = editorOptions || {};
|
||||||
|
editorOptions.memberid = userid + "___" + Date.now();
|
||||||
|
editorOptions.networked = true;
|
||||||
|
editorOptions.networkSecurityToken = token;
|
||||||
|
|
||||||
|
require({ }, ["webodf/editor/Editor"],
|
||||||
|
function (Editor) {
|
||||||
|
// TODO: the networkSecurityToken needs to be retrieved via now.login
|
||||||
|
// (but this is to be implemented later)
|
||||||
|
editorInstance = new Editor(editorOptions);
|
||||||
|
|
||||||
|
// load the document and get called back when it's live
|
||||||
|
editorInstance.loadSession(sessionId, function (editorSession) {
|
||||||
|
editorSession.startEditing();
|
||||||
|
editorReadyCallback(editorInstance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start the login process by offering a login/password prompt.
|
||||||
|
* the login is validated via nowjs namespace.
|
||||||
|
* on success a list of sessions is offered.
|
||||||
|
* when the user selects a session the callback is called
|
||||||
|
* with the sessionId as parameter
|
||||||
|
*
|
||||||
|
* @param {!function(!string, !string, ?string)} callback
|
||||||
|
* @returns {undefined}
|
||||||
|
*/
|
||||||
|
function startLoginProcess(callback) {
|
||||||
|
var userid, token,
|
||||||
|
net = runtime.getNetwork();
|
||||||
|
|
||||||
|
booting = true;
|
||||||
|
|
||||||
|
runtime.assert(editorInstance === null, "cannot boot with instanciated editor");
|
||||||
|
|
||||||
|
function enterSession(selectedSessionId) {
|
||||||
|
document.getElementById("sessionListContainer").style.display = "none";
|
||||||
|
document.getElementById("mainContainer").style.display = "";
|
||||||
|
|
||||||
|
callback(selectedSessionId, userid, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSessions() {
|
||||||
|
var sessionListDiv = document.getElementById("sessionList"),
|
||||||
|
sessionList = new SessionList(net),
|
||||||
|
sessionListView = new SessionListView(sessionList, sessionListDiv, enterSession);
|
||||||
|
|
||||||
|
// hide login view
|
||||||
|
document.getElementById("loginContainer").style.display = "none";
|
||||||
|
|
||||||
|
// show session list
|
||||||
|
document.getElementById("sessionListContainer").style.display = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function loginSuccess(userData) {
|
||||||
|
runtime.log("connected:" + userData.full_name);
|
||||||
|
userid = userData.uid;
|
||||||
|
token = userData.securityToken || null;
|
||||||
|
|
||||||
|
showSessions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loginFail(result) {
|
||||||
|
alert("Login failed: " + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLoginSubmit() {
|
||||||
|
net.login(document.loginForm.login.value, document.loginForm.password.value, loginSuccess, loginFail);
|
||||||
|
|
||||||
|
// block the submit button, we already dealt with the input
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bring up the login form
|
||||||
|
document.loginForm.Submit.onclick = onLoginSubmit;
|
||||||
|
document.getElementById("loginContainer").style.display = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make a guess about the document (# in URL)
|
||||||
|
* also guess about local/collaborative (depending on nowjs)
|
||||||
|
*
|
||||||
|
* @param {?Object} args
|
||||||
|
*
|
||||||
|
* args:
|
||||||
|
*
|
||||||
|
* collaborative: if set to true: connect to the network and start a
|
||||||
|
* collaborative editor. in that case the document url
|
||||||
|
* is ignored. and user needs to select a session.
|
||||||
|
*
|
||||||
|
* if set to the string "auto": it will try the above
|
||||||
|
* but fall back to non-collaborative mode [default]
|
||||||
|
*
|
||||||
|
* docUrl: if given it is used as the url to the document to load
|
||||||
|
*
|
||||||
|
* callback: callback to be called as soon as the document is loaded
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function boot(args) {
|
||||||
|
var editorOptions = {}, loginProcedure = startLoginProcess;
|
||||||
|
runtime.assert(!booting, "editor creation already in progress");
|
||||||
|
|
||||||
|
args = args || {};
|
||||||
|
|
||||||
|
if (args.collaborative === undefined) {
|
||||||
|
args.collaborative = "auto";
|
||||||
|
} else {
|
||||||
|
args.collaborative = String(args.collaborative).toLowerCase();
|
||||||
|
}
|
||||||
|
if (args.docUrl === undefined) {
|
||||||
|
args.docUrl = guessDocUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.saveCallback) {
|
||||||
|
editorOptions.saveCallback = args.saveCallback;
|
||||||
|
}
|
||||||
|
if (args.cursorAddedCallback) {
|
||||||
|
editorOptions.cursorAddedCallback = args.cursorAddedCallback;
|
||||||
|
}
|
||||||
|
if (args.cursorRemovedCallback) {
|
||||||
|
editorOptions.cursorRemovedCallback = args.cursorRemovedCallback;
|
||||||
|
}
|
||||||
|
if (args.registerCallbackForShutdown) {
|
||||||
|
editorOptions.registerCallbackForShutdown = args.registerCallbackForShutdown;
|
||||||
|
} else {
|
||||||
|
editorOptions.registerCallbackForShutdown = function (callback) {
|
||||||
|
window.onunload = callback;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.loginProcedure) {
|
||||||
|
loginProcedure = args.loginProcedure;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the editor with network
|
||||||
|
function handleNetworkedSituation() {
|
||||||
|
loginProcedure(function (sessionId, userid, token) {
|
||||||
|
createNetworkedEditor(sessionId, userid, token, editorOptions, function (ed) {
|
||||||
|
if (args.callback) {
|
||||||
|
args.callback(ed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the editor without network
|
||||||
|
function handleNonNetworkedSituation() {
|
||||||
|
createLocalEditor(args.docUrl, editorOptions, function (editor) {
|
||||||
|
if (args.callback) {
|
||||||
|
args.callback(editor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.collaborative === "auto") {
|
||||||
|
runtime.log("detecting network...");
|
||||||
|
waitForNetwork(function (state) {
|
||||||
|
if (state === "ready") {
|
||||||
|
runtime.log("... network available.");
|
||||||
|
handleNetworkedSituation();
|
||||||
|
} else {
|
||||||
|
runtime.log("... no network available (" + state + ").");
|
||||||
|
handleNonNetworkedSituation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ((args.collaborative === "true") ||
|
||||||
|
(args.collaborative === "1") ||
|
||||||
|
(args.collaborative === "yes")) {
|
||||||
|
runtime.log("starting collaborative editor.");
|
||||||
|
waitForNetwork(function (state) {
|
||||||
|
if (state === "ready") {
|
||||||
|
handleNetworkedSituation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
runtime.log("starting local editor.");
|
||||||
|
handleNonNetworkedSituation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// exposed API
|
||||||
|
return { boot: boot };
|
||||||
|
}());
|
||||||
|
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,143 @@
|
|||||||
|
/**
|
||||||
|
* 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 define,document,require */
|
||||||
|
define("webodf/editor/widgets", [
|
||||||
|
"webodf/editor/widgets/simpleStyles",
|
||||||
|
"webodf/editor/widgets/undoRedoMenu",
|
||||||
|
"webodf/editor/widgets/toolbarWidgets/currentStyle",
|
||||||
|
"webodf/editor/widgets/paragraphStylesDialog",
|
||||||
|
"webodf/editor/widgets/zoomSlider"],
|
||||||
|
function (SimpleStyles, UndoRedoMenu, CurrentStyle, ParagraphStylesDialog, ZoomSlider) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
return function loadWidgets(editorSession, loadOdtFile, saveOdtFile) {
|
||||||
|
var translator = document.translator;
|
||||||
|
|
||||||
|
// Menubar
|
||||||
|
require([
|
||||||
|
"dojo/ready",
|
||||||
|
"dijit/MenuItem",
|
||||||
|
"dijit/DropDownMenu",
|
||||||
|
"dijit/form/Button",
|
||||||
|
"dijit/form/DropDownButton",
|
||||||
|
"dijit/Toolbar"
|
||||||
|
], function (ready, MenuItem, DropDownMenu, Button, DropDownButton, Toolbar) {
|
||||||
|
ready(function () {
|
||||||
|
var loadButton, saveButton, dropDownMenu, menuButton, paragraphStylesMenuItem, dialog, toolbar, simpleStyles, currentStyle, zoomSlider,
|
||||||
|
undoRedoMenu;
|
||||||
|
|
||||||
|
dropDownMenu = new DropDownMenu({});
|
||||||
|
paragraphStylesMenuItem = new MenuItem({
|
||||||
|
label: translator("paragraph_DDD")
|
||||||
|
});
|
||||||
|
dropDownMenu.addChild(paragraphStylesMenuItem);
|
||||||
|
|
||||||
|
dialog = new ParagraphStylesDialog(editorSession, function (dialog) {
|
||||||
|
paragraphStylesMenuItem.onClick = function () {
|
||||||
|
dialog.startup();
|
||||||
|
dialog.show();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toolbar
|
||||||
|
toolbar = new Toolbar({}, "toolbar");
|
||||||
|
|
||||||
|
if (editorSession.hasUndoManager()) {
|
||||||
|
undoRedoMenu = new UndoRedoMenu(editorSession, function (widget) {
|
||||||
|
widget.placeAt(toolbar);
|
||||||
|
widget.startup();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple Style Selector [B, I, U, S]
|
||||||
|
simpleStyles = new SimpleStyles(editorSession, function (widget) {
|
||||||
|
widget.placeAt(toolbar);
|
||||||
|
widget.startup();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Paragraph Style Selector
|
||||||
|
currentStyle = new CurrentStyle(editorSession, function (widget) {
|
||||||
|
widget.placeAt(toolbar);
|
||||||
|
widget.startup();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Zoom Level Selector
|
||||||
|
zoomSlider = new ZoomSlider(editorSession, function (widget) {
|
||||||
|
widget.placeAt(toolbar);
|
||||||
|
widget.startup();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loadOdtFile) {
|
||||||
|
loadButton = new Button({
|
||||||
|
label: translator('open'),
|
||||||
|
showLabel: false,
|
||||||
|
iconClass: 'dijitIcon dijitIconFolderOpen',
|
||||||
|
style: {
|
||||||
|
float: 'left'
|
||||||
|
},
|
||||||
|
onClick: function () {
|
||||||
|
loadOdtFile();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
loadButton.placeAt(toolbar);
|
||||||
|
}
|
||||||
|
if (saveOdtFile) {
|
||||||
|
saveButton = new Button({
|
||||||
|
label: translator('save'),
|
||||||
|
showLabel: false,
|
||||||
|
iconClass: 'dijitEditorIcon dijitEditorIconSave',
|
||||||
|
style: {
|
||||||
|
float: 'left'
|
||||||
|
},
|
||||||
|
onClick: function () {
|
||||||
|
saveOdtFile();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
saveButton.placeAt(toolbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
menuButton = new DropDownButton({
|
||||||
|
dropDown: dropDownMenu,
|
||||||
|
label: translator('format'),
|
||||||
|
iconClass: "dijitIconEditTask",
|
||||||
|
style: {
|
||||||
|
float: 'left'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
menuButton.placeAt(toolbar);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,59 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div data-dojo-type="dijit/form/Form" id="alignmentPaneForm" data-dojo-id="alignmentPaneForm">
|
||||||
|
|
||||||
|
<h3 text-i18n="options">Options</h3>
|
||||||
|
|
||||||
|
<input type="radio" data-dojo-type="dijit/form/RadioButton" name="textAlign" id="radioLeft" checked value="left"/>
|
||||||
|
<label text-i18n="left" for="radioLeft">Left</label> <br/>
|
||||||
|
<input type="radio" data-dojo-type="dijit/form/RadioButton" name="textAlign" id="radioCenter" value="center"/>
|
||||||
|
<label text-i18n="center" for="radioCenter">Center</label> <br/>
|
||||||
|
<input type="radio" data-dojo-type="dijit/form/RadioButton" name="textAlign" id="radioRight" value="right"/>
|
||||||
|
<label text-i18n="right" for="radioRight">Right</label> <br/>
|
||||||
|
<input type="radio" data-dojo-type="dijit/form/RadioButton" name="textAlign" id="radioJustified" value="justify"/>
|
||||||
|
<label text-i18n="justified" for="radioJustified">Justified</label> <br/>
|
||||||
|
|
||||||
|
<h3><span text-i18n="spacing">Spacing</span> <small>(mm)</small></h3>
|
||||||
|
|
||||||
|
<div style = "margin-top: 10px;">
|
||||||
|
<div style = "float: right; margin-right: 200px;">
|
||||||
|
<label text-i18n="top" for="topMargin">Top</label>
|
||||||
|
<input data-dojo-type="dijit/form/NumberSpinner" id="topMargin"
|
||||||
|
value="0"
|
||||||
|
data-dojo-props="smallDelta:1, constraints:{min:0,max:100}"
|
||||||
|
name="topMargin"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
<div style = "float: left;">
|
||||||
|
<label text-i18n="left" for="leftMargin">Left</label>
|
||||||
|
<input data-dojo-type="dijit/form/NumberSpinner" id="leftMargin"
|
||||||
|
value="0"
|
||||||
|
data-dojo-props="smallDelta:1, constraints:{min:0,max:100}"
|
||||||
|
name="leftMargin"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style = "float: right; margin-right: 50px;">
|
||||||
|
<label text-i18n="right" for="rightMargin">Right</label>
|
||||||
|
<input data-dojo-type="dijit/form/NumberSpinner" id="rightMargin"
|
||||||
|
value="0"
|
||||||
|
data-dojo-props="smallDelta:1, constraints:{min:0,max:100}"
|
||||||
|
name="rightMargin"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<br/><br>
|
||||||
|
<div style = "float: right; margin-right: 200px;">
|
||||||
|
<label text-i18n="bottom" for="bottomMargin">Bottom</label>
|
||||||
|
<input data-dojo-type="dijit/form/NumberSpinner" id="bottomMargin"
|
||||||
|
value="0"
|
||||||
|
data-dojo-props="smallDelta:1, constraints:{min:0,max:100}"
|
||||||
|
name="bottomMargin"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* 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,core,define,require,document,dijit */
|
||||||
|
|
||||||
|
runtime.loadClass("core.CSSUnits");
|
||||||
|
|
||||||
|
define("webodf/editor/widgets/dialogWidgets/alignmentPane", [], function () {
|
||||||
|
"use strict";
|
||||||
|
var AlignmentPane = function (editorSession, callback) {
|
||||||
|
var self = this,
|
||||||
|
contentPane,
|
||||||
|
form;
|
||||||
|
|
||||||
|
this.widget = function () {
|
||||||
|
return contentPane;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.value = function () {
|
||||||
|
return form.get('value');
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setStyle = function (styleName) {
|
||||||
|
var style = editorSession.getParagraphStyleAttributes(styleName)['style:paragraph-properties'],
|
||||||
|
cssUnits = new core.CSSUnits(),
|
||||||
|
s_topMargin,
|
||||||
|
s_bottomMargin,
|
||||||
|
s_leftMargin,
|
||||||
|
s_rightMargin,
|
||||||
|
s_textAlign;
|
||||||
|
|
||||||
|
if (style !== undefined) {
|
||||||
|
s_topMargin = parseFloat(cssUnits.convertMeasure(style['fo:margin-top'], 'mm'));
|
||||||
|
s_leftMargin = parseFloat(cssUnits.convertMeasure(style['fo:margin-left'], 'mm'));
|
||||||
|
s_rightMargin = parseFloat(cssUnits.convertMeasure(style['fo:margin-right'], 'mm'));
|
||||||
|
s_bottomMargin = parseFloat(cssUnits.convertMeasure(style['fo:margin-bottom'], 'mm'));
|
||||||
|
s_textAlign = style['fo:text-align'];
|
||||||
|
|
||||||
|
form.attr('value', {
|
||||||
|
topMargin: isNaN(s_topMargin) ? 0 : s_topMargin,
|
||||||
|
bottomMargin: isNaN(s_bottomMargin) ? 0 : s_bottomMargin,
|
||||||
|
leftMargin: isNaN(s_leftMargin) ? 0 : s_leftMargin,
|
||||||
|
rightMargin: isNaN(s_rightMargin) ? 0 : s_rightMargin,
|
||||||
|
textAlign: s_textAlign && s_textAlign.length ? s_textAlign : 'left'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
form.attr('value', {
|
||||||
|
topMargin: 0,
|
||||||
|
bottomMargin: 0,
|
||||||
|
leftMargin: 0,
|
||||||
|
rightMargin: 0,
|
||||||
|
textAlign: 'left'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function init(cb) {
|
||||||
|
require([
|
||||||
|
"dojo",
|
||||||
|
"dojo/ready",
|
||||||
|
"dojo/dom-construct",
|
||||||
|
"dijit/layout/ContentPane"],
|
||||||
|
function (dojo, ready, domConstruct, ContentPane) {
|
||||||
|
var editorBase = dojo.config && dojo.config.paths &&
|
||||||
|
dojo.config.paths['webodf/editor'];
|
||||||
|
runtime.assert(editorBase, "webodf/editor path not defined in dojoConfig");
|
||||||
|
ready(function () {
|
||||||
|
contentPane = new ContentPane({
|
||||||
|
title: document.translator("alignment"),
|
||||||
|
href: editorBase+"/widgets/dialogWidgets/alignmentPane.html",
|
||||||
|
preload: true
|
||||||
|
});
|
||||||
|
contentPane.onLoad = function () {
|
||||||
|
form = dijit.byId('alignmentPaneForm');
|
||||||
|
document.translateContent(form.domNode);
|
||||||
|
};
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init(function () {
|
||||||
|
return callback(self);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return AlignmentPane;
|
||||||
|
});
|
@ -0,0 +1,62 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div data-dojo-type="dijit/form/Form" id="fontEffectsPaneForm" data-dojo-id="fontEffectsPaneForm">
|
||||||
|
|
||||||
|
<h3 text-i18n="style">Style</h3>
|
||||||
|
|
||||||
|
<input data-dojo-type="dijit/form/CheckBox" name="textStyle" id="radioBold" value="bold"/>
|
||||||
|
<label text-i18n="bold" for="radioBold">Bold</label> <br/>
|
||||||
|
<input data-dojo-type="dijit/form/CheckBox" name="textStyle" id="radioItalic" value="italic"/>
|
||||||
|
<label text-i18n="italic" for="radioItalic">Italic</label> <br/>
|
||||||
|
<input data-dojo-type="dijit/form/CheckBox" name="textStyle" id="radioUnderline" value="underline"/>
|
||||||
|
<label text-i18n="underline" for="radioUnderline">Underline</label> <br/>
|
||||||
|
|
||||||
|
<h3 text-i18n="font">Font</h3>
|
||||||
|
<br>
|
||||||
|
<div id = 'fontPicker' class="labeledSelect" style = "float: left;">
|
||||||
|
<label text-i18n="family" for="fontName">Family:</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style = "float: left; margin-left: 30px;">
|
||||||
|
<label for="fontSize"><span text-i18n="size">Size</span> (pt) </label>
|
||||||
|
<input data-dojo-type="dijit/form/NumberSpinner" id="fontSize"
|
||||||
|
value="12"
|
||||||
|
data-dojo-props="smallDelta:1, constraints:{min:6,max:96}"
|
||||||
|
name="fontSize"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
<h3 text-i18n="color">Color</h3>
|
||||||
|
<br>
|
||||||
|
<input data-dojo-type="dijit/form/TextBox" name = "color" id = "textColorTB" style="display: none;"/>
|
||||||
|
<div data-dojo-type="dijit/form/DropDownButton">
|
||||||
|
<span><span text-i18n="text" id = "textColor">
|
||||||
|
Text
|
||||||
|
</span></span>
|
||||||
|
<div data-dojo-type="dijit/TooltipDialog">
|
||||||
|
<div data-dojo-type="dojox.widget.ColorPicker" id="textColorPicker"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input data-dojo-type="dijit/form/TextBox" name = "backgroundColor" id = "backgroundColorTB" style="display: none;"/>
|
||||||
|
<div data-dojo-type="dijit/form/DropDownButton">
|
||||||
|
<span><span text-i18n="background" id = "backgroundColor">
|
||||||
|
Background
|
||||||
|
</span></span>
|
||||||
|
<div data-dojo-type="dijit/TooltipDialog">
|
||||||
|
<div data-dojo-type="dojox.widget.ColorPicker" id="backgroundColorPicker"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br /> <br />
|
||||||
|
|
||||||
|
<div class="dialogPreviewBox" style="overflow: hidden;">
|
||||||
|
<p id="previewText" style="margin: 0;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce laoreet diam vestibulum massa malesuada quis dignissim libero blandit. Duis sit amet volutpat nisl.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,193 @@
|
|||||||
|
/**
|
||||||
|
* 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,require,document,dijit */
|
||||||
|
define("webodf/editor/widgets/dialogWidgets/fontEffectsPane", [], function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var FontEffectsPane = function (editorSession, callback) {
|
||||||
|
var self = this,
|
||||||
|
contentPane,
|
||||||
|
form,
|
||||||
|
preview,
|
||||||
|
textColorPicker,
|
||||||
|
backgroundColorPicker,
|
||||||
|
fontPicker;
|
||||||
|
|
||||||
|
this.widget = function () {
|
||||||
|
return contentPane;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.value = function () {
|
||||||
|
var textProperties = form.get('value'),
|
||||||
|
textStyle = textProperties.textStyle;
|
||||||
|
|
||||||
|
textProperties.fontWeight = (textStyle.indexOf('bold') !== -1)
|
||||||
|
? 'bold'
|
||||||
|
: 'normal';
|
||||||
|
textProperties.fontStyle = (textStyle.indexOf('italic') !== -1)
|
||||||
|
? 'italic'
|
||||||
|
: 'normal';
|
||||||
|
textProperties.underline = (textStyle.indexOf('underline') !== -1)
|
||||||
|
? 'solid'
|
||||||
|
: 'none';
|
||||||
|
|
||||||
|
delete textProperties.textStyle;
|
||||||
|
return textProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setStyle = function (styleName) {
|
||||||
|
var style = editorSession.getParagraphStyleAttributes(styleName)['style:text-properties'],
|
||||||
|
s_bold,
|
||||||
|
s_italic,
|
||||||
|
s_underline,
|
||||||
|
s_fontSize,
|
||||||
|
s_fontName,
|
||||||
|
s_color,
|
||||||
|
s_backgroundColor;
|
||||||
|
|
||||||
|
if (style !== undefined) {
|
||||||
|
s_bold = style['fo:font-weight'];
|
||||||
|
s_italic = style['fo:font-style'];
|
||||||
|
s_underline = style['style:text-underline-style'];
|
||||||
|
s_fontSize = parseFloat(style['fo:font-size']);
|
||||||
|
s_fontName = style['style:font-name'];
|
||||||
|
s_color = style['fo:color'];
|
||||||
|
s_backgroundColor = style['fo:background-color'];
|
||||||
|
|
||||||
|
form.attr('value', {
|
||||||
|
fontName: s_fontName && s_fontName.length ? s_fontName : 'Arial',
|
||||||
|
fontSize: isNaN(s_fontSize) ? 12 : s_fontSize,
|
||||||
|
textStyle: [
|
||||||
|
s_bold,
|
||||||
|
s_italic,
|
||||||
|
s_underline === 'solid' ? 'underline' : undefined
|
||||||
|
]
|
||||||
|
});
|
||||||
|
textColorPicker.set('value', s_color && s_color.length ? s_color : '#000000');
|
||||||
|
backgroundColorPicker.set('value', s_backgroundColor && s_backgroundColor.length ? s_backgroundColor : '#ffffff');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// TODO: Use default style here
|
||||||
|
form.attr('value', {
|
||||||
|
fontFamily: 'sans-serif',
|
||||||
|
fontSize: 12,
|
||||||
|
textStyle: []
|
||||||
|
});
|
||||||
|
textColorPicker.set('value', '#000000');
|
||||||
|
backgroundColorPicker.set('value', '#ffffff');
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function init(cb) {
|
||||||
|
require([
|
||||||
|
"dojo",
|
||||||
|
"dojo/ready",
|
||||||
|
"dojo/dom-construct",
|
||||||
|
"dijit/layout/ContentPane",
|
||||||
|
"dojox/widget/ColorPicker",
|
||||||
|
"webodf/editor/widgets/fontPicker"
|
||||||
|
], function (dojo, ready, domConstruct, ContentPane, ColorPicker, FontPicker) {
|
||||||
|
var translator = document.translator,
|
||||||
|
editorBase = dojo.config && dojo.config.paths &&
|
||||||
|
dojo.config.paths['webodf/editor'];
|
||||||
|
runtime.assert(editorBase, "webodf/editor path not defined in dojoConfig");
|
||||||
|
ready(function () {
|
||||||
|
contentPane = new ContentPane({
|
||||||
|
title: translator("fontEffects"),
|
||||||
|
href: editorBase+"/widgets/dialogWidgets/fontEffectsPane.html",
|
||||||
|
preload: true
|
||||||
|
});
|
||||||
|
|
||||||
|
contentPane.onLoad = function () {
|
||||||
|
var textColorTB = dijit.byId('textColorTB'),
|
||||||
|
backgroundColorTB = dijit.byId('backgroundColorTB');
|
||||||
|
|
||||||
|
form = dijit.byId('fontEffectsPaneForm');
|
||||||
|
document.translateContent(form.domNode);
|
||||||
|
|
||||||
|
preview = document.getElementById('previewText');
|
||||||
|
textColorPicker = dijit.byId('textColorPicker');
|
||||||
|
backgroundColorPicker = dijit.byId('backgroundColorPicker');
|
||||||
|
|
||||||
|
// Bind dojox widgets' values to invisible form elements, for easy parsing
|
||||||
|
textColorPicker.onChange = function (value) {
|
||||||
|
textColorTB.set('value', value);
|
||||||
|
};
|
||||||
|
backgroundColorPicker.onChange = function (value) {
|
||||||
|
backgroundColorTB.set('value', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
fontPicker = new FontPicker(editorSession, function (picker) {
|
||||||
|
picker.widget().startup();
|
||||||
|
document.getElementById('fontPicker').appendChild(picker.widget().domNode);
|
||||||
|
picker.widget().name = 'fontName';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Automatically update preview when selections change
|
||||||
|
form.watch('value', function () {
|
||||||
|
if (form.value.textStyle.indexOf('bold') !== -1) {
|
||||||
|
preview.style.fontWeight = 'bold';
|
||||||
|
} else {
|
||||||
|
preview.style.fontWeight = 'normal';
|
||||||
|
}
|
||||||
|
if (form.value.textStyle.indexOf('italic') !== -1) {
|
||||||
|
preview.style.fontStyle = 'italic';
|
||||||
|
} else {
|
||||||
|
preview.style.fontStyle = 'normal';
|
||||||
|
}
|
||||||
|
if (form.value.textStyle.indexOf('underline') !== -1) {
|
||||||
|
preview.style.textDecoration = 'underline';
|
||||||
|
} else {
|
||||||
|
preview.style.textDecoration = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
preview.style.fontSize = form.value.fontSize + 'pt';
|
||||||
|
preview.style.fontFamily = fontPicker.getFamily(form.value.fontName);
|
||||||
|
preview.style.color = form.value.color;
|
||||||
|
preview.style.backgroundColor = form.value.backgroundColor;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init(function () {
|
||||||
|
return callback(self);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return FontEffectsPane;
|
||||||
|
});
|
@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2012 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 define,require,document */
|
||||||
|
define("webodf/editor/widgets/fontPicker", [], function () {
|
||||||
|
"use strict";
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
var FontPicker = function (editorSession, callback) {
|
||||||
|
var self = this,
|
||||||
|
select,
|
||||||
|
editorFonts = [],
|
||||||
|
documentFonts = [],
|
||||||
|
selectionList = [];
|
||||||
|
|
||||||
|
this.widget = function () {
|
||||||
|
return select;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.value = function () {
|
||||||
|
return select.get('value');
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setValue = function (value) {
|
||||||
|
select.set('value', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the font family for a given font name. If unavailable,
|
||||||
|
* return the name itself (e.g. editor fonts won't have a name-family separation
|
||||||
|
* @param {!string} name
|
||||||
|
* @return {!string}
|
||||||
|
*/
|
||||||
|
this.getFamily = function (name) {
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < documentFonts.length; i += 1) {
|
||||||
|
if ((documentFonts[i].name === name) && documentFonts[i].family) {
|
||||||
|
return documentFonts[i].family;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
};
|
||||||
|
// events
|
||||||
|
this.onAdd = null;
|
||||||
|
this.onRemove = null;
|
||||||
|
|
||||||
|
function populateFonts() {
|
||||||
|
var i, name, family;
|
||||||
|
editorFonts = editorSession.availableFonts;
|
||||||
|
documentFonts = editorSession.getDeclaredFonts();
|
||||||
|
|
||||||
|
// First populate the fonts used in the document
|
||||||
|
for (i = 0; i < documentFonts.length; i += 1) {
|
||||||
|
name = documentFonts[i].name;
|
||||||
|
family = documentFonts[i].family || name;
|
||||||
|
selectionList.push({
|
||||||
|
label: '<span style="font-family: ' + family + ';">' + name + '</span>',
|
||||||
|
value: name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (editorFonts.length) {
|
||||||
|
// Then add a separator
|
||||||
|
selectionList.push({
|
||||||
|
type: 'separator'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Lastly populate the fonts provided by the editor
|
||||||
|
for (i = 0; i < editorFonts.length; i += 1) {
|
||||||
|
selectionList.push({
|
||||||
|
label: '<span style="font-family: ' + editorFonts[i] + ';">' + editorFonts[i] + '</span>',
|
||||||
|
value: editorFonts[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
select.removeOption(select.getOptions());
|
||||||
|
select.addOption(selectionList);
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(cb) {
|
||||||
|
require(["dijit/form/Select"], function (Select) {
|
||||||
|
select = new Select({
|
||||||
|
name: 'FontPicker',
|
||||||
|
maxHeight: 200,
|
||||||
|
style: {
|
||||||
|
width: '100px'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
populateFonts();
|
||||||
|
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init(function () {
|
||||||
|
return callback(self);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return FontPicker;
|
||||||
|
});
|
@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2012 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 define,require */
|
||||||
|
define("webodf/editor/widgets/paragraphStyles",
|
||||||
|
["webodf/editor/EditorSession"],
|
||||||
|
|
||||||
|
function (EditorSession) {
|
||||||
|
"use strict";
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
var ParagraphStyles = function (editorSession, callback) {
|
||||||
|
var self = this,
|
||||||
|
select;
|
||||||
|
|
||||||
|
this.widget = function () {
|
||||||
|
return select;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.value = function () {
|
||||||
|
return select.get('value');
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setValue = function (value) {
|
||||||
|
select.set('value', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// events
|
||||||
|
this.onAdd = null;
|
||||||
|
this.onRemove = null;
|
||||||
|
|
||||||
|
function populateStyles() {
|
||||||
|
var i, availableStyles, selectionList;
|
||||||
|
selectionList = [];
|
||||||
|
availableStyles = editorSession.getAvailableParagraphStyles();
|
||||||
|
|
||||||
|
for (i = 0; i < availableStyles.length; i += 1) {
|
||||||
|
selectionList.push({
|
||||||
|
label: availableStyles[i].displayName,
|
||||||
|
value: availableStyles[i].name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
select.removeOption(select.getOptions());
|
||||||
|
select.addOption(selectionList);
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(cb) {
|
||||||
|
require(["dijit/form/Select"], function (Select) {
|
||||||
|
var stylens = "urn:oasis:names:tc:opendocument:xmlns:style:1.0";
|
||||||
|
|
||||||
|
select = new Select({
|
||||||
|
name: 'ParagraphStyles',
|
||||||
|
maxHeight: 200,
|
||||||
|
style: {
|
||||||
|
width: '100px'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
populateStyles();
|
||||||
|
|
||||||
|
editorSession.subscribe(EditorSession.signalStyleCreated, function (newStyleName) {
|
||||||
|
var newStyleElement = editorSession.getParagraphStyleElement(newStyleName);
|
||||||
|
select.addOption({
|
||||||
|
value: newStyleName,
|
||||||
|
label: newStyleElement.getAttributeNS(stylens, 'display-name')
|
||||||
|
});
|
||||||
|
|
||||||
|
if (self.onAdd) {
|
||||||
|
self.onAdd(newStyleName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
editorSession.subscribe(EditorSession.signalStyleDeleted, function (styleName) {
|
||||||
|
select.removeOption(styleName);
|
||||||
|
|
||||||
|
if (self.onRemove) {
|
||||||
|
self.onRemove(styleName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init(function () {
|
||||||
|
return callback(self);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return ParagraphStyles;
|
||||||
|
});
|
@ -0,0 +1,232 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2012 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 define,require,document,dojo,dijit */
|
||||||
|
define("webodf/editor/widgets/paragraphStylesDialog", [], function () {
|
||||||
|
"use strict";
|
||||||
|
function makeWidget(editorSession, callback) {
|
||||||
|
require([
|
||||||
|
"dijit/Dialog",
|
||||||
|
"dijit/TooltipDialog",
|
||||||
|
"dijit/popup",
|
||||||
|
"dijit/layout/TabContainer",
|
||||||
|
"dijit/layout/ContentPane",
|
||||||
|
"dijit/form/Button",
|
||||||
|
"dijit/form/DropDownButton",
|
||||||
|
"dijit/form/RadioButton"], function (Dialog, TooltipDialog, popup, TabContainer, ContentPane, Button, DropDownButton, RadioButton) {
|
||||||
|
var i,
|
||||||
|
dialog,
|
||||||
|
translator = document.translator,
|
||||||
|
tabContainer,
|
||||||
|
alignmentPane,
|
||||||
|
fontEffectsPane,
|
||||||
|
stylePicker,
|
||||||
|
flowPane,
|
||||||
|
numberingPane,
|
||||||
|
tabsPane,
|
||||||
|
capsPane,
|
||||||
|
bordersPane,
|
||||||
|
backgroundPane,
|
||||||
|
indentsPane,
|
||||||
|
actionBar,
|
||||||
|
okButton,
|
||||||
|
cancelButton,
|
||||||
|
cloneButton,
|
||||||
|
deleteButton,
|
||||||
|
cloneTooltip,
|
||||||
|
cloneDropDown,
|
||||||
|
newStyleName = null;
|
||||||
|
|
||||||
|
function accept() {
|
||||||
|
editorSession.updateParagraphStyle(stylePicker.value(), {
|
||||||
|
paragraphProperties: alignmentPane.value(),
|
||||||
|
textProperties: fontEffectsPane.value()
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
dialog.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and enqueues a paragraph-style cloning operation.
|
||||||
|
* Remembers the id of the created style in newStyleName, so the
|
||||||
|
* style picker can be set to it, once the operation has been applied.
|
||||||
|
* @param {!string} styleName id of the style to clone
|
||||||
|
* @param {!string} newStyleDisplayName display name of the new style
|
||||||
|
*/
|
||||||
|
function cloneStyle(styleName, newStyleDisplayName) {
|
||||||
|
newStyleName = editorSession.cloneParagraphStyle(styleName, newStyleDisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteStyle(styleName) {
|
||||||
|
editorSession.deleteStyle(styleName);
|
||||||
|
}
|
||||||
|
// Dialog
|
||||||
|
dialog = new Dialog({
|
||||||
|
title: translator("paragraphStyles")
|
||||||
|
});
|
||||||
|
|
||||||
|
cloneTooltip = new TooltipDialog({
|
||||||
|
content:
|
||||||
|
'<h2 style="margin: 0;">Clone this style</h2><br/>' +
|
||||||
|
'<label for="name">New name </label><input data-dojo-type="dijit/form/TextBox" id="name" name="name"><br/><br/>',
|
||||||
|
style: "width: 300px;"
|
||||||
|
});
|
||||||
|
cloneButton = new Button({
|
||||||
|
label: 'Create',
|
||||||
|
onClick: function () {
|
||||||
|
cloneStyle(stylePicker.value(), cloneTooltip.get('value').name);
|
||||||
|
cloneTooltip.reset();
|
||||||
|
popup.close(cloneTooltip);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cloneTooltip.addChild(cloneButton);
|
||||||
|
cloneDropDown = new DropDownButton({
|
||||||
|
label: 'Clone',
|
||||||
|
showLabel: false,
|
||||||
|
iconClass: 'dijitEditorIcon dijitEditorIconCopy',
|
||||||
|
dropDown: cloneTooltip,
|
||||||
|
style: "float: right; margin-bottom: 5px;"
|
||||||
|
});
|
||||||
|
dialog.addChild(cloneDropDown, 1);
|
||||||
|
|
||||||
|
deleteButton = new Button({
|
||||||
|
label: 'Delete',
|
||||||
|
showLabel: false,
|
||||||
|
iconClass: 'dijitEditorIcon dijitEditorIconDelete',
|
||||||
|
style: "float: right; margin-bottom: 5px;",
|
||||||
|
onClick: function () {
|
||||||
|
deleteStyle(stylePicker.value());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog.addChild(deleteButton, 2);
|
||||||
|
|
||||||
|
// Tab Container
|
||||||
|
tabContainer = new TabContainer({
|
||||||
|
style: "height: 100%; width: 100%;"
|
||||||
|
});
|
||||||
|
dialog.addChild(tabContainer, 3);
|
||||||
|
|
||||||
|
actionBar = dojo.create("div", {
|
||||||
|
"class": "dijitDialogPaneActionBar"
|
||||||
|
});
|
||||||
|
okButton = new dijit.form.Button({
|
||||||
|
label: translator("ok"),
|
||||||
|
onClick: accept
|
||||||
|
}).placeAt(actionBar);
|
||||||
|
cancelButton = new dijit.form.Button({
|
||||||
|
label: translator("cancel"),
|
||||||
|
onClick: cancel
|
||||||
|
}).placeAt(actionBar);
|
||||||
|
dialog.domNode.appendChild(actionBar);
|
||||||
|
|
||||||
|
|
||||||
|
require([
|
||||||
|
"webodf/editor/widgets/paragraphStyles",
|
||||||
|
"webodf/editor/widgets/dialogWidgets/alignmentPane",
|
||||||
|
"webodf/editor/widgets/dialogWidgets/fontEffectsPane"
|
||||||
|
], function (ParagraphStyles, AlignmentPane, FontEffectsPane) {
|
||||||
|
var p, a, f;
|
||||||
|
|
||||||
|
function openStyle(value) {
|
||||||
|
alignmentPane.setStyle(value);
|
||||||
|
fontEffectsPane.setStyle(value);
|
||||||
|
if (editorSession.isStyleUsed(editorSession.getParagraphStyleElement(value))) {
|
||||||
|
deleteButton.domNode.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
deleteButton.domNode.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p = new ParagraphStyles(editorSession, function (paragraphStyles) {
|
||||||
|
stylePicker = paragraphStyles;
|
||||||
|
stylePicker.widget().startup();
|
||||||
|
stylePicker.widget().domNode.style.float = "left";
|
||||||
|
stylePicker.widget().domNode.style.width = "350px";
|
||||||
|
stylePicker.widget().domNode.style.marginTop = "5px";
|
||||||
|
dialog.addChild(stylePicker.widget(), 0);
|
||||||
|
|
||||||
|
stylePicker.onAdd = function (name) {
|
||||||
|
if (newStyleName === name) {
|
||||||
|
stylePicker.setValue(name);
|
||||||
|
newStyleName = null; // reset 'flag' name
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stylePicker.onRemove = function (name) {
|
||||||
|
// Set the first style name as current
|
||||||
|
stylePicker.setValue(stylePicker.widget().getOptions(0));
|
||||||
|
};
|
||||||
|
|
||||||
|
stylePicker.widget().onChange = openStyle;
|
||||||
|
});
|
||||||
|
a = new AlignmentPane(editorSession, function (pane) {
|
||||||
|
alignmentPane = pane;
|
||||||
|
alignmentPane.widget().startup();
|
||||||
|
tabContainer.addChild(alignmentPane.widget());
|
||||||
|
});
|
||||||
|
f = new FontEffectsPane(editorSession, function (pane) {
|
||||||
|
fontEffectsPane = pane;
|
||||||
|
fontEffectsPane.widget().startup();
|
||||||
|
tabContainer.addChild(fontEffectsPane.widget());
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.onShow = function () {
|
||||||
|
var currentStyle = editorSession.getCurrentParagraphStyle();
|
||||||
|
// setting the stylepicker value if the style name is the same
|
||||||
|
// will not trigger onChange, so specifically open the style in
|
||||||
|
// the panes.
|
||||||
|
if (stylePicker.value() === currentStyle) {
|
||||||
|
openStyle(currentStyle);
|
||||||
|
} else {
|
||||||
|
stylePicker.setValue(currentStyle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
tabContainer.startup();
|
||||||
|
|
||||||
|
return callback(dialog);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return function ParagraphStylesDialog(editorSession, callback) {
|
||||||
|
makeWidget(editorSession, function (dialog) {
|
||||||
|
return callback(dialog);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,160 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2012 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 define,require,document */
|
||||||
|
define("webodf/editor/widgets/simpleStyles",
|
||||||
|
["webodf/editor/EditorSession"],
|
||||||
|
|
||||||
|
function (EditorSession) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function makeWidget(editorSession, callback) {
|
||||||
|
require(["dijit/form/ToggleButton"], function (ToggleButton) {
|
||||||
|
var i,
|
||||||
|
widget = {},
|
||||||
|
boldButton,
|
||||||
|
italicButton,
|
||||||
|
underlineButton,
|
||||||
|
strikethroughButton;
|
||||||
|
|
||||||
|
boldButton = new ToggleButton({
|
||||||
|
label: document.translator('bold'),
|
||||||
|
showLabel: false,
|
||||||
|
checked: false,
|
||||||
|
iconClass: "dijitEditorIcon dijitEditorIconBold",
|
||||||
|
onChange: function (checked) {
|
||||||
|
var value = checked ? 'bold' : 'normal';
|
||||||
|
editorSession.formatSelection({
|
||||||
|
'style:text-properties': {
|
||||||
|
'fo:font-weight' : value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
italicButton = new ToggleButton({
|
||||||
|
label: document.translator('italic'),
|
||||||
|
showLabel: false,
|
||||||
|
checked: false,
|
||||||
|
iconClass: "dijitEditorIcon dijitEditorIconItalic",
|
||||||
|
onChange: function (checked) {
|
||||||
|
var value = checked ? 'italic' : 'normal';
|
||||||
|
editorSession.formatSelection({
|
||||||
|
'style:text-properties': {
|
||||||
|
'fo:font-style' : value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
underlineButton = new ToggleButton({
|
||||||
|
label: document.translator('underline'),
|
||||||
|
showLabel: false,
|
||||||
|
checked: false,
|
||||||
|
iconClass: "dijitEditorIcon dijitEditorIconUnderline",
|
||||||
|
onChange: function (checked) {
|
||||||
|
var value = checked ? 'solid' : 'none';
|
||||||
|
editorSession.formatSelection({
|
||||||
|
'style:text-properties': {
|
||||||
|
'style:text-underline-style' : value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
strikethroughButton = new ToggleButton({
|
||||||
|
label: document.translator('strikethrough'),
|
||||||
|
showLabel: false,
|
||||||
|
checked: false,
|
||||||
|
iconClass: "dijitEditorIcon dijitEditorIconStrikethrough",
|
||||||
|
onChange: function (checked) {
|
||||||
|
var value = checked ? 'solid' : 'none';
|
||||||
|
editorSession.formatSelection({
|
||||||
|
'style:text-properties': {
|
||||||
|
'style:text-line-through-style' : value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkStyleButtons() {
|
||||||
|
var fontWeight, fontStyle, underline, strikethrough, appliedStyles;
|
||||||
|
appliedStyles = editorSession.getCurrentSelectionStyle();
|
||||||
|
|
||||||
|
fontWeight = false;
|
||||||
|
fontStyle = false;
|
||||||
|
underline = false;
|
||||||
|
strikethrough = false;
|
||||||
|
|
||||||
|
appliedStyles.forEach(function(appliedStyle) {
|
||||||
|
var textProperties = appliedStyle['style:text-properties'];
|
||||||
|
fontWeight = fontWeight || textProperties['fo:font-weight'] === 'bold';
|
||||||
|
fontStyle = fontStyle || textProperties['fo:font-style'] === 'italic';
|
||||||
|
underline = underline || textProperties['style:text-underline-style'] === 'solid';
|
||||||
|
strikethrough = strikethrough || textProperties['style:text-line-through-style'] === 'solid';
|
||||||
|
});
|
||||||
|
|
||||||
|
// The 3rd parameter is false to avoid firing onChange when setting the value
|
||||||
|
// programmatically.
|
||||||
|
boldButton.set('checked', fontWeight, false);
|
||||||
|
italicButton.set('checked', fontStyle, false);
|
||||||
|
underlineButton.set('checked', underline, false);
|
||||||
|
strikethroughButton.set('checked', strikethrough, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
editorSession.subscribe(EditorSession.signalCursorMoved, checkStyleButtons);
|
||||||
|
editorSession.subscribe(EditorSession.signalParagraphChanged, checkStyleButtons);
|
||||||
|
editorSession.subscribe(EditorSession.signalParagraphStyleModified, checkStyleButtons);
|
||||||
|
|
||||||
|
widget.children = [boldButton, italicButton, underlineButton, strikethroughButton];
|
||||||
|
widget.startup = function () {
|
||||||
|
widget.children.forEach(function (element) {
|
||||||
|
element.startup();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
widget.placeAt = function (container) {
|
||||||
|
widget.children.forEach(function (element) {
|
||||||
|
element.placeAt(container);
|
||||||
|
});
|
||||||
|
return widget;
|
||||||
|
};
|
||||||
|
|
||||||
|
return callback(widget);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return function SimpleStyles(editorSession, callback) {
|
||||||
|
makeWidget(editorSession, function (widget) {
|
||||||
|
return callback(widget);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2012 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 define, require */
|
||||||
|
define("webodf/editor/widgets/toolbarWidgets/currentStyle",
|
||||||
|
["webodf/editor/EditorSession"],
|
||||||
|
|
||||||
|
function (EditorSession) {
|
||||||
|
"use strict";
|
||||||
|
function makeWidget(editorSession, callback) {
|
||||||
|
require(["webodf/editor/widgets/paragraphStyles"], function (ParagraphStyles) {
|
||||||
|
var paragraphStyles, widget;
|
||||||
|
|
||||||
|
paragraphStyles = new ParagraphStyles(editorSession, function (pStyles) {
|
||||||
|
// if the current paragraph style changes, update the widget
|
||||||
|
editorSession.subscribe(EditorSession.signalParagraphChanged, function (info) {
|
||||||
|
if (info.type === 'style') {
|
||||||
|
pStyles.widget().set("value", info.styleName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pStyles.widget().onChange = function (value) {
|
||||||
|
editorSession.setCurrentParagraphStyle(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return callback(pStyles.widget());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return function CurrentStyle(editorSession, callback) {
|
||||||
|
makeWidget(editorSession, function (widget) {
|
||||||
|
return callback(widget);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2012 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 define,require,document */
|
||||||
|
define("webodf/editor/widgets/undoRedoMenu",
|
||||||
|
["webodf/editor/EditorSession"],
|
||||||
|
|
||||||
|
function (EditorSession) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function makeWidget(editorSession, callback) {
|
||||||
|
require(["dijit/form/Button"], function (Button) {
|
||||||
|
var widget = {},
|
||||||
|
undoButton,
|
||||||
|
redoButton;
|
||||||
|
|
||||||
|
undoButton = new Button({
|
||||||
|
label: document.translator('undo'),
|
||||||
|
showLabel: false,
|
||||||
|
disabled: true,
|
||||||
|
iconClass: "dijitEditorIcon dijitEditorIconUndo",
|
||||||
|
onClick: function () {
|
||||||
|
editorSession.undo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
redoButton = new Button({
|
||||||
|
label: document.translator('redo'),
|
||||||
|
showLabel: false,
|
||||||
|
disabled: true,
|
||||||
|
iconClass: "dijitEditorIcon dijitEditorIconRedo",
|
||||||
|
onClick: function () {
|
||||||
|
editorSession.redo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkUndoButtons(e) {
|
||||||
|
undoButton.set('disabled', e.undoAvailable === false);
|
||||||
|
redoButton.set('disabled', e.redoAvailable === false);
|
||||||
|
}
|
||||||
|
|
||||||
|
editorSession.subscribe(EditorSession.signalUndoStackChanged, checkUndoButtons);
|
||||||
|
|
||||||
|
widget.children = [undoButton, redoButton];
|
||||||
|
widget.startup = function () {
|
||||||
|
widget.children.forEach(function (element) {
|
||||||
|
element.startup();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
widget.placeAt = function (container) {
|
||||||
|
widget.children.forEach(function (element) {
|
||||||
|
element.placeAt(container);
|
||||||
|
});
|
||||||
|
return widget;
|
||||||
|
};
|
||||||
|
|
||||||
|
return callback(widget);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return function UndoRedoMenu(editorSession, callback) {
|
||||||
|
makeWidget(editorSession, function (widget) {
|
||||||
|
return callback(widget);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2012 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 define,require,document */
|
||||||
|
define("webodf/editor/widgets/zoomSlider", [], function () {
|
||||||
|
"use strict";
|
||||||
|
function makeWidget(editorSession, callback) {
|
||||||
|
require(["dijit/form/HorizontalSlider", "dijit/form/NumberTextBox", "dojo"], function (HorizontalSlider, NumberTextBox, dojo) {
|
||||||
|
var widget = {},
|
||||||
|
canvas;
|
||||||
|
|
||||||
|
widget = new HorizontalSlider({
|
||||||
|
name: 'zoomSlider',
|
||||||
|
value: 100,
|
||||||
|
minimum: 30,
|
||||||
|
maximum: 150,
|
||||||
|
discreteValues: 100,
|
||||||
|
intermediateChanges: true,
|
||||||
|
style: {
|
||||||
|
width: '150px',
|
||||||
|
height: '25px',
|
||||||
|
float: 'right'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas = dojo.byId('canvas');
|
||||||
|
widget.onChange = function (value) {
|
||||||
|
editorSession.getOdfCanvas().setZoomLevel(value / 100.0);
|
||||||
|
};
|
||||||
|
|
||||||
|
return callback(widget);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return function ZoomSlider(editorSession, callback) {
|
||||||
|
makeWidget(editorSession, function (widget) {
|
||||||
|
return callback(widget);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
<title>test</title>
|
||||||
|
<script type="text/javascript" charset="utf-8">
|
||||||
|
//var dojoConfig = {
|
||||||
|
//has: {
|
||||||
|
//'dojo-requirejs-api':1
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
</script>
|
||||||
|
<script src="dojo-amalgamation.js"></script>
|
||||||
|
<script type="text/javascript" charset="utf-8">
|
||||||
|
function main() {
|
||||||
|
require(["dojo/ready"], function(ready) {
|
||||||
|
ready(function(){alert("ready!");});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="main();">
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -0,0 +1,20 @@
|
|||||||
|
/*globals navigator,dojoConfig */
|
||||||
|
var usedLocale = "C";
|
||||||
|
|
||||||
|
if (navigator && navigator.language.match(/^(de)/)) {
|
||||||
|
usedLocale = navigator.language.substr(0,2);
|
||||||
|
}
|
||||||
|
|
||||||
|
dojoConfig = {
|
||||||
|
locale: usedLocale,
|
||||||
|
paths: {
|
||||||
|
"webodf/editor": "/owncloud/apps/office/js/editor",
|
||||||
|
"dijit": "/owncloud/apps/office/js/editor/dijit",
|
||||||
|
"dojox": "/owncloud/apps/office/js/editor/dojox",
|
||||||
|
"dojo": "/owncloud/apps/office/js/editor/dojo",
|
||||||
|
"resources": "/owncloud/apps/office/js/editor/resources"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//alert("bootstrap hello");
|
Loading…
Reference in New Issue