initial WIP - amd clash-issues webodf/dojo/owncloud

pull/1/head
Tobias Hintze 11 years ago
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);
});
};
});

@ -1,30 +1,57 @@
/*globals $,OC,fileDownloadPath,t,document,odf,webodfEditor */
var officeMain = {
onView: function(dir, file) {
OC.addStyle('office', 'webodf');
OC.addScript('office', 'webodf-debug').done(function() {
var location = fileDownloadPath(dir, file);
// fade out files menu and add odf menu
$('.documentslist').fadeOut('slow').promise().done(function() {
// odf action toolbar
var odfToolbarHtml =
'<div id="odf-toolbar">' +
'<button id="odf_close">' + t('files_odfviewer', 'Close') +
'</button></div>';
$('#controls').append(odfToolbarHtml);
"use strict";
OC.addScript('office', 'dojo-amalgamation').done(function() {
document.dojoAnchor.require(["dojo/ready"], function(ready) {
ready(function(){alert("ready!");});
});
});
(function no_op() {return {no_op:function(){}};}()).no_op(function() {
OC.addScript('office', 'webodf').done(function() {
OC.addScript('office', 'webodf_bootstrap').done(function() {
OC.addScript('office', 'boot_editor').done(function() {
var doclocation = fileDownloadPath(dir, file);
// fade out files menu and add odf menu
$('.documentslist').fadeOut('slow').promise().done(function() {
// odf action toolbar
var odfToolbarHtml =
'<div id="odf-toolbar">' +
'<button id="odf_close">' + t('files_odfviewer', 'Close') +
'</button></div>';
$('#controls').append(odfToolbarHtml);
});
// fade out file list and show WebODF canvas
$('table').fadeOut('slow').promise().done(function() {
var odfelement, odfcanvas, canvashtml = '<div id = "mainContainer" style="display: none;">'+
'<div id = "editor">'+
'<span id = "toolbar"></span>'+
'<div id = "container">'+
'<div id="canvas"></div>'+
'</div>'+
'</div>'+
'</div>';
$('table').after(canvashtml);
// in case we are on the public sharing page we shall display the odf into the preview tag
// $('#preview').html(canvashtml);
// fade out file list and show pdf canvas
$('table').fadeOut('slow').promise().done(function() {
;
var canvashtml = '<div id="odf-canvas"></div>';
$('table').after(canvashtml);
// in case we are on the public sharing page we shall display the odf into the preview tag
$('#preview').html(canvashtml);
webodfEditor.boot(
{
collaborative: 0,
docUrl: doclocation,
callback: function() { alert('live!'); }
}
);
var odfelement = document.getElementById("odf-canvas");
var odfcanvas = new odf.OdfCanvas(odfelement);
odfcanvas.load(location);
// odfelement = document.getElementById("odf-canvas");
// odfcanvas = new odf.OdfCanvas(odfelement);
// odfcanvas.load(doclocation);
});
});
});
});
});
},

@ -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…
Cancel
Save