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