webodf update

This commit is contained in:
Tobias Hintze 2013-08-09 21:18:48 +02:00
parent 86952308ce
commit 3deb473927
14 changed files with 972 additions and 978 deletions

View File

@ -58,8 +58,9 @@ html, body, #mainContainer {
-webkit-transform-origin: top center; -webkit-transform-origin: top center;
-moz-transform-origin: top center; -moz-transform-origin: top center;
-o-transform-origin: top center; -o-transform-origin: top center;
overflow: hidden; overflow: hidden;
border: 1px solid #ccc;
} }
#collaboration { #collaboration {
@ -87,7 +88,7 @@ html, body, #mainContainer {
} }
#people { #members {
width: 70px; width: 70px;
padding: 2px; padding: 2px;
text-align: center; text-align: center;
@ -120,14 +121,14 @@ html, body, #mainContainer {
box-shadow: 0px 0px 15px red; box-shadow: 0px 0px 15px red;
} }
#people > #nameInfo { #members > #nameInfo {
padding-top: 3px; padding-top: 3px;
padding-bottom: 3px; padding-bottom: 3px;
width: 100%; width: 100%;
background-color: #eef; background-color: #eef;
} }
#peopleList .userListButton { #memberList .memberListButton {
margin-top: 5px; margin-top: 5px;
padding-top: 3px; padding-top: 3px;
margin-left: auto; margin-left: auto;
@ -141,7 +142,7 @@ html, body, #mainContainer {
cursor: pointer; cursor: pointer;
} }
#peopleList .userListLabel { #memberList .memberListLabel {
color: white; color: white;
border-radius: 5px; border-radius: 5px;
padding: 2px; padding: 2px;
@ -149,11 +150,11 @@ html, body, #mainContainer {
word-wrap: break-word; word-wrap: break-word;
text-align: center justify; text-align: center justify;
} }
div.userListLabel[fullname]:before { div.memberListLabel[fullname]:before {
content: attr(fullname) ""; content: attr(fullname) "";
} }
#peopleList img { #memberList img {
box-shadow: 0px 0px 5px rgb(90, 90, 90) inset; box-shadow: 0px 0px 5px rgb(90, 90, 90) inset;
background-color: rgb(200, 200, 200); background-color: rgb(200, 200, 200);
border-radius: 5px; border-radius: 5px;
@ -164,7 +165,7 @@ div.userListLabel[fullname]:before {
margin: auto; margin: auto;
} }
#peopleList img:hover { #memberList img:hover {
opacity: 0.9; opacity: 0.9;
} }
@ -314,3 +315,4 @@ cursor div:after {
top: 100%; top: 100%;
left: 43%; left: 43%;
} }

View File

@ -36,14 +36,14 @@
define("webodf/editor/Editor", [ define("webodf/editor/Editor", [
"dojo/i18n!webodf/editor/nls/myResources", "dojo/i18n!webodf/editor/nls/myResources",
"webodf/editor/EditorSession", "webodf/editor/EditorSession",
"webodf/editor/UserList", "webodf/editor/MemberList",
"dijit/layout/BorderContainer", "dijit/layout/BorderContainer",
"dijit/layout/ContentPane", "dijit/layout/ContentPane",
"webodf/editor/widgets"], "webodf/editor/widgets"],
function (myResources, function (myResources,
EditorSession, EditorSession,
UserList, MemberList,
BorderContainer, BorderContainer,
ContentPane, ContentPane,
loadWidgets) { loadWidgets) {
@ -52,7 +52,7 @@ define("webodf/editor/Editor", [
/** /**
* @constructor * @constructor
* @param {{networked:boolean=, * @param {{networked:boolean=,
* memberid:string=, * memberid:!string,
* loadCallback:function()=, * loadCallback:function()=,
* saveCallback:function()=, * saveCallback:function()=,
* cursorAddedCallback:function(!string)=, * cursorAddedCallback:function(!string)=,
@ -65,14 +65,13 @@ define("webodf/editor/Editor", [
var self = this, var self = this,
// Private // Private
userid,
memberid = args.memberid, memberid = args.memberid,
session, session,
editorSession, editorSession,
userList, memberList,
networked = args.networked === true, networked = args.networked === true,
opRouter, opRouter,
userModel, memberModel,
loadOdtFile = args.loadCallback, loadOdtFile = args.loadCallback,
saveOdtFile = args.saveCallback, saveOdtFile = args.saveCallback,
cursorAddedHandler = args.cursorAddedCallback, cursorAddedHandler = args.cursorAddedCallback,
@ -99,17 +98,17 @@ define("webodf/editor/Editor", [
*/ */
function initGuiAndDoc(initialDocumentUrl, editorReadyCallback) { function initGuiAndDoc(initialDocumentUrl, editorReadyCallback) {
var odfElement, mainContainer, var odfElement, mainContainer,
editorPane, peoplePane, editorPane, memberListPane,
inviteButton, inviteButton,
viewOptions = { viewOptions = {
editInfoMarkersInitiallyVisible: networked, editInfoMarkersInitiallyVisible: networked,
caretAvatarsInitiallyVisible: networked, caretAvatarsInitiallyVisible: networked,
caretBlinksOnRangeSelect: true caretBlinksOnRangeSelect: true
}, },
peopleListDiv = document.getElementById('peopleList'); memberListDiv = document.getElementById('memberList');
if (networked) { if (networked) {
runtime.assert(peopleListDiv, "missing peopleList div in HTML"); runtime.assert(memberListDiv, "missing memberList div in HTML");
} }
runtime.loadClass('odf.OdfCanvas'); runtime.loadClass('odf.OdfCanvas');
@ -158,19 +157,14 @@ define("webodf/editor/Editor", [
// Allow annotations // Allow annotations
odfCanvas.enableAnnotations(true); odfCanvas.enableAnnotations(true);
if (!memberid) {
// legacy - memberid should be passed in the constructor
memberid = (userid || 'undefined') + "___" + Date.now();
}
session = new ops.Session(odfCanvas); session = new ops.Session(odfCanvas);
editorSession = new EditorSession(session, memberid, { editorSession = new EditorSession(session, memberid, {
viewOptions: viewOptions viewOptions: viewOptions
}); });
editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager()); editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager());
if (peopleListDiv) { if (memberListDiv) {
userList = new UserList(editorSession, peopleListDiv); memberList = new MemberList(editorSession, memberListDiv);
} }
if (registerCallbackForShutdown) { if (registerCallbackForShutdown) {
@ -192,12 +186,12 @@ define("webodf/editor/Editor", [
}, 'editor'); }, 'editor');
mainContainer.addChild(editorPane); mainContainer.addChild(editorPane);
if (networked && peopleListDiv) { if (networked && memberListDiv) {
peoplePane = new ContentPane({ memberListPane = new ContentPane({
region: 'right', region: 'right',
title: translator("people") title: translator("members")
}, 'people'); }, 'members');
mainContainer.addChild(peoplePane); mainContainer.addChild(memberListPane);
} }
mainContainer.startup(); mainContainer.startup();
@ -205,7 +199,7 @@ define("webodf/editor/Editor", [
if (window.inviteButtonProxy) { if (window.inviteButtonProxy) {
inviteButton = document.getElementById('inviteButton'); inviteButton = document.getElementById('inviteButton');
if (inviteButton) { if (inviteButton) {
inviteButton.innerText = translator("invitePeople"); inviteButton.innerText = translator("inviteMembers");
inviteButton.style.display = "block"; inviteButton.style.display = "block";
inviteButton.onclick = window.inviteButtonProxy.clicked; inviteButton.onclick = window.inviteButtonProxy.clicked;
} }
@ -269,12 +263,12 @@ define("webodf/editor/Editor", [
*/ */
self.loadSession = function (sessionId, editorReadyCallback) { self.loadSession = function (sessionId, editorReadyCallback) {
initGuiAndDoc(server.getGenesisUrl(sessionId), function () { initGuiAndDoc(server.getGenesisUrl(sessionId), function () {
// get router and user model // get router and member model
opRouter = opRouter || serverFactory.createOperationRouter(sessionId, memberid, server); opRouter = opRouter || serverFactory.createOperationRouter(sessionId, memberid, server);
session.setOperationRouter(opRouter); session.setOperationRouter(opRouter);
userModel = userModel || serverFactory.createUserModel(server); memberModel = memberModel || serverFactory.createMemberModel(sessionId, server);
session.setUserModel(userModel); session.setMemberModel(memberModel);
opRouter.requestReplay(function done() { opRouter.requestReplay(function done() {
var odtDocument = session.getOdtDocument(); var odtDocument = session.getOdtDocument();
@ -295,9 +289,9 @@ define("webodf/editor/Editor", [
}); });
}; };
// access to user model // access to member model
self.getUserModel = function () { self.getMemberModel = function () {
return userModel; return memberModel;
}; };
} }
return Editor; return Editor;

View File

@ -70,8 +70,8 @@ define("webodf/editor/EditorSession", [
formatting = odtDocument.getFormatting(), formatting = odtDocument.getFormatting(),
styleHelper = new gui.StyleHelper(formatting), styleHelper = new gui.StyleHelper(formatting),
eventNotifier = new core.EventNotifier([ eventNotifier = new core.EventNotifier([
EditorSession.signalUserAdded, EditorSession.signalMemberAdded,
EditorSession.signalUserRemoved, EditorSession.signalMemberRemoved,
EditorSession.signalCursorMoved, EditorSession.signalCursorMoved,
EditorSession.signalParagraphChanged, EditorSession.signalParagraphChanged,
EditorSession.signalStyleCreated, EditorSession.signalStyleCreated,
@ -164,7 +164,7 @@ define("webodf/editor/EditorSession", [
ncName = createNCName(name); ncName = createNCName(name);
// create default paragraph style // create default paragraph style
// memberid is used to avoid id conflicts with ids created by other users // memberid is used to avoid id conflicts with ids created by other members
result = ncName + "_" + ncMemberId; result = ncName + "_" + ncMemberId;
// then loop until result is really unique // then loop until result is really unique
while (formatting.hasParagraphStyle(result)) { while (formatting.hasParagraphStyle(result)) {
@ -196,12 +196,12 @@ define("webodf/editor/EditorSession", [
// 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. // 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) { odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, function (cursor) {
self.emit(EditorSession.signalUserAdded, cursor.getMemberId()); self.emit(EditorSession.signalMemberAdded, cursor.getMemberId());
trackCursor(cursor); trackCursor(cursor);
}); });
odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, function (memberId) { odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, function (memberId) {
self.emit(EditorSession.signalUserRemoved, memberId); self.emit(EditorSession.signalMemberRemoved, memberId);
}); });
odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, function (cursor) { odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, function (cursor) {
@ -251,12 +251,12 @@ define("webodf/editor/EditorSession", [
eventNotifier.subscribe(eventid, cb); eventNotifier.subscribe(eventid, cb);
}; };
this.getUserDetailsAndUpdates = function (memberId, subscriber) { this.getMemberDetailsAndUpdates = function (memberId, subscriber) {
return session.getUserModel().getUserDetailsAndUpdates(memberId, subscriber); return session.getMemberModel().getMemberDetailsAndUpdates(memberId, subscriber);
}; };
this.unsubscribeUserDetailsUpdates = function (memberId, subscriber) { this.unsubscribeMemberDetailsUpdates = function (memberId, subscriber) {
return session.getUserModel().unsubscribeUserDetailsUpdates(memberId, subscriber); return session.getMemberModel().unsubscribeMemberDetailsUpdates(memberId, subscriber);
}; };
this.getCursorPosition = function () { this.getCursorPosition = function () {
@ -458,7 +458,7 @@ define("webodf/editor/EditorSession", [
this.deleteStyle = function (styleName) { this.deleteStyle = function (styleName) {
var op; var op;
op = new ops.OpDeleteParagraphStyle(); op = new ops.OpRemoveParagraphStyle();
op.init({ op.init({
memberid: memberid, memberid: memberid,
styleName: styleName styleName: styleName
@ -541,8 +541,8 @@ define("webodf/editor/EditorSession", [
init(); init();
}; };
/**@const*/EditorSession.signalUserAdded = "userAdded"; /**@const*/EditorSession.signalMemberAdded = "memberAdded";
/**@const*/EditorSession.signalUserRemoved = "userRemoved"; /**@const*/EditorSession.signalMemberRemoved = "memberRemoved";
/**@const*/EditorSession.signalCursorMoved = "cursorMoved"; /**@const*/EditorSession.signalCursorMoved = "cursorMoved";
/**@const*/EditorSession.signalParagraphChanged = "paragraphChanged"; /**@const*/EditorSession.signalParagraphChanged = "paragraphChanged";
/**@const*/EditorSession.signalStyleCreated = "styleCreated"; /**@const*/EditorSession.signalStyleCreated = "styleCreated";

View File

@ -33,32 +33,33 @@
* @source: http://gitorious.org/webodf/webodf/ * @source: http://gitorious.org/webodf/webodf/
*/ */
/*global define,runtime */ /*global define,runtime */
define("webodf/editor/UserList",
define("webodf/editor/MemberList",
["webodf/editor/EditorSession"], ["webodf/editor/EditorSession"],
function (EditorSession) { function (EditorSession) {
"use strict"; "use strict";
return function UserList(editorSession, userListDiv) { return function MemberList(editorSession, memberListDiv) {
var self = this; var self = this;
editorSession.subscribe(EditorSession.signalUserAdded, function (memberId) { editorSession.subscribe(EditorSession.signalMemberAdded, function (memberId) {
self.addUser(memberId); self.addMember(memberId);
}); });
editorSession.subscribe(EditorSession.signalUserRemoved, function (memberId) { editorSession.subscribe(EditorSession.signalMemberRemoved, function (memberId) {
self.removeUser(memberId); self.removeMember(memberId);
}); });
/** /**
* @param {!string} memberId * @param {!string} memberId
*/ */
function updateAvatarButton(memberId, userDetails) { function updateAvatarButton(memberId, memberDetails) {
var node = userListDiv.firstChild; var node = memberListDiv.firstChild;
if (userDetails === null) { if (memberDetails === null) {
// 'null' here means finally unknown user // 'null' here means finally unknown member
// (and not that the data is still loading) // (and not that the data is still loading)
userDetails = { memberDetails = {
memberid: memberId, fullname: "Unknown", memberid: memberId, fullname: "Unknown",
color: "black", imageurl: "avatar-joe.png" color: "black", imageurl: "avatar-joe.png"
}; };
@ -69,11 +70,11 @@ define("webodf/editor/UserList",
while (node) { while (node) {
if (node.localName === "img") { if (node.localName === "img") {
// update avatar image // update avatar image
node.src = userDetails.imageurl; node.src = memberDetails.imageurl;
// update border color // update border color
node.style.borderColor = userDetails.color; node.style.borderColor = memberDetails.color;
} else if (node.localName === "div") { } else if (node.localName === "div") {
node.setAttribute('fullname', userDetails.fullname); node.setAttribute('fullname', memberDetails.fullname);
} }
node = node.nextSibling; node = node.nextSibling;
} }
@ -87,15 +88,15 @@ define("webodf/editor/UserList",
* @param {!string} memberId * @param {!string} memberId
*/ */
function createAvatarButton(memberId) { function createAvatarButton(memberId) {
runtime.assert(userListDiv, "userListDiv unavailable"); runtime.assert(memberListDiv, "memberListDiv unavailable");
var doc = userListDiv.ownerDocument, var doc = memberListDiv.ownerDocument,
htmlns = doc.documentElement.namespaceURI, htmlns = doc.documentElement.namespaceURI,
avatarDiv = doc.createElementNS(htmlns, "div"), avatarDiv = doc.createElementNS(htmlns, "div"),
imageElement = doc.createElement("img"), imageElement = doc.createElement("img"),
fullnameNode = doc.createElement("div"); fullnameNode = doc.createElement("div");
avatarDiv.className = "userListButton"; avatarDiv.className = "memberListButton";
fullnameNode.className = "userListLabel"; fullnameNode.className = "memberListLabel";
avatarDiv.appendChild(imageElement); avatarDiv.appendChild(imageElement);
avatarDiv.appendChild(fullnameNode); avatarDiv.appendChild(fullnameNode);
avatarDiv.memberId = memberId; // TODO: namespace? avatarDiv.memberId = memberId; // TODO: namespace?
@ -112,7 +113,7 @@ define("webodf/editor/UserList",
caret.toggleHandleVisibility(); caret.toggleHandleVisibility();
} }
}; };
userListDiv.appendChild(avatarDiv); memberListDiv.appendChild(avatarDiv);
// preset bogus data // preset bogus data
// TODO: indicate loading state // TODO: indicate loading state
@ -124,10 +125,10 @@ define("webodf/editor/UserList",
* @param {!string} memberId * @param {!string} memberId
*/ */
function removeAvatarButton(memberId) { function removeAvatarButton(memberId) {
var node = userListDiv.firstChild; var node = memberListDiv.firstChild;
while (node) { while (node) {
if (node.memberId === memberId) { if (node.memberId === memberId) {
userListDiv.removeChild(node); memberListDiv.removeChild(node);
return; return;
} }
node = node.nextSibling; node = node.nextSibling;
@ -137,16 +138,16 @@ define("webodf/editor/UserList",
/** /**
* @param {!string} memberId * @param {!string} memberId
*/ */
this.addUser = function (memberId) { this.addMember = function (memberId) {
createAvatarButton(memberId); createAvatarButton(memberId);
editorSession.getUserDetailsAndUpdates(memberId, updateAvatarButton); editorSession.getMemberDetailsAndUpdates(memberId, updateAvatarButton);
}; };
/** /**
* @param {!string} memberId * @param {!string} memberId
*/ */
this.removeUser = function (memberId) { this.removeMember = function (memberId) {
editorSession.unsubscribeUserDetailsUpdates(memberId, updateAvatarButton); editorSession.unsubscribeMemberDetailsUpdates(memberId, updateAvatarButton);
removeAvatarButton(memberId); removeAvatarButton(memberId);
}; };
}; };

View File

@ -1,159 +0,0 @@
/**
* @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 ops, runtime */
function PullBoxSessionList(server) {
"use strict";
var cachedSessionData = {},
subscribers = [],
/** HACK: allow to stop pulling, so that does not mess up the logs
* Remove before merging to master */
pullingActive = true;
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);
}
}
}
function pullSessionList() {
if (!pullingActive) { return; }
server.call("session-list", function(responseData) {
var response = runtime.fromJson(responseData),
sessionList, i,
unupdatedSessions = {};
runtime.log("session-list reply: " + responseData);
if (response.hasOwnProperty("session_list")) {
// collect known sessions
for (i in cachedSessionData) {
if (cachedSessionData.hasOwnProperty(i)) {
unupdatedSessions[i] = ""; // some dummy value, unused
}
}
// add/update with all delivered sessions
sessionList = response.session_list;
for (i = 0; i < sessionList.length; i++) {
if (unupdatedSessions.hasOwnProperty(sessionList[i].id)) {
delete unupdatedSessions[sessionList[i].id];
}
onSessionData(sessionList[i]);
}
// remove unupdated sessions
for (i in unupdatedSessions) {
if (unupdatedSessions.hasOwnProperty(i)) {
onSessionRemoved(i);
}
}
// next update in 5 secs
runtime.getWindow().setTimeout(pullSessionList, 5000);
} else {
runtime.log("Meh, sessionlist data broken: " + responseData);
}
});
}
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);
};
this.stopPulling = function () {
pullingActive = false;
};
function init() {
pullSessionList();
}
init();
}

View File

@ -1,50 +0,0 @@
/**
* @license
* Copyright (C) 2012-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 ops, runtime */
/**
* A model which provides information about sessions.
* @interface
*/
SessionList = function SessionList() {"use strict"; };
/**
* @param {{onCreated:function(!Object),
* onUpdated:function(!Object),
* onRemoved:function(!string) }} subscriber
* @return {undefined}
*/
SessionList.prototype.getSessions = function (subscriber) {"use strict"; };

View File

@ -248,26 +248,22 @@ var webodfEditor = (function () {
* and start the editor on the network. * and start the editor on the network.
* *
* @param {!string} sessionId * @param {!string} sessionId
* @param {!string} userid * @param {!string} memberId
* @param {?string} token * @param {?string} token
* @param {?Object} editorOptions * @param {?Object} editorOptions
* @param {?function(!Object)} editorReadyCallback * @param {?function(!Object)} editorReadyCallback
*/ */
function createNetworkedEditor(sessionId, userid, token, editorOptions, editorReadyCallback) { function createNetworkedEditor(sessionId, memberId, token, editorOptions, editorReadyCallback) {
runtime.assert(sessionId, "sessionId needs to be specified"); runtime.assert(sessionId, "sessionId needs to be specified");
runtime.assert(memberId, "memberId needs to be specified");
runtime.assert(editorInstance === null, "cannot boot with instanciated editor"); runtime.assert(editorInstance === null, "cannot boot with instanciated editor");
editorOptions = editorOptions || {}; editorOptions = editorOptions || {};
editorOptions.memberid = userid + "___" + Date.now(); editorOptions.memberid = memberId;
editorOptions.networked = true; editorOptions.networked = true;
editorOptions.networkSecurityToken = token; editorOptions.networkSecurityToken = token;
// if pre-authentication has happened:
if (token) {
server.setToken(token);
}
require({ }, ["webodf/editor/Editor"], require({ }, ["webodf/editor/Editor"],
function (Editor) { function (Editor) {
// TODO: the networkSecurityToken needs to be retrieved via now.login // TODO: the networkSecurityToken needs to be retrieved via now.login
@ -280,7 +276,7 @@ var webodfEditor = (function () {
editorReadyCallback(editorInstance); editorReadyCallback(editorInstance);
}); });
} }
); );
} }
@ -365,7 +361,8 @@ var webodfEditor = (function () {
* *
*/ */
function boot(args) { function boot(args) {
var editorOptions = {}, loginProcedure = startLoginProcess; var editorOptions = {},
loginProcedure = startLoginProcess;
runtime.assert(!booting, "editor creation already in progress"); runtime.assert(!booting, "editor creation already in progress");
args = args || {}; args = args || {};
@ -399,13 +396,25 @@ var webodfEditor = (function () {
// start the editor with network // start the editor with network
function handleNetworkedSituation() { function handleNetworkedSituation() {
loginProcedure(function (sessionId, userid, token) { var joinSession = server.joinSession;
createNetworkedEditor(sessionId, userid, token, editorOptions, function (ed) {
if (args.callback) { if (args.joinSession) {
args.callback(ed); joinSession = args.joinSession;
} }
loginProcedure(function (sessionId, userId, token) {
// if pre-authentication has happened:
if (token) {
server.setToken(token);
} }
);
joinSession(userId, sessionId, function(memberId) {
createNetworkedEditor(sessionId, memberId, token, editorOptions, function (ed) {
if (args.callback) {
args.callback(ed);
}
});
});
}); });
} }

View File

View File

@ -54,10 +54,11 @@ ServerFactory.prototype.createServer = function () {"use strict"; };
ServerFactory.prototype.createOperationRouter = function (sessionId, memberId, server) {"use strict"; }; ServerFactory.prototype.createOperationRouter = function (sessionId, memberId, server) {"use strict"; };
/** /**
* @param {!string} sessionId
* @param {!ops.Server} server * @param {!ops.Server} server
* @return {!ops.UserModel} * @return {!ops.MemberModel}
*/ */
ServerFactory.prototype.createUserModel = function (server) {"use strict"; }; ServerFactory.prototype.createMemberModel = function (sessionId, server) {"use strict"; };
/** /**
* @param {!ops.Server} server * @param {!ops.Server} server

View File

@ -41,7 +41,7 @@ define("webodf/editor/server/nowjs/serverFactory", [
"use strict"; "use strict";
runtime.loadClass("ops.NowjsServer"); runtime.loadClass("ops.NowjsServer");
runtime.loadClass("ops.NowjsUserModel"); runtime.loadClass("ops.NowjsMemberModel");
runtime.loadClass("ops.NowjsOperationRouter"); runtime.loadClass("ops.NowjsOperationRouter");
/** /**
@ -55,8 +55,8 @@ define("webodf/editor/server/nowjs/serverFactory", [
this.createOperationRouter = function (sid, mid, server) { this.createOperationRouter = function (sid, mid, server) {
return new ops.NowjsOperationRouter(sid, mid, server); return new ops.NowjsOperationRouter(sid, mid, server);
}; };
this.createUserModel = function (server) { this.createMemberModel = function (sid, server) {
return new ops.NowjsUserModel(server); return new ops.NowjsMemberModel(server);
}; };
this.createSessionList = function (server) { this.createSessionList = function (server) {
return new NowjsSessionList(server); return new NowjsSessionList(server);

View File

@ -41,7 +41,7 @@ define("webodf/editor/server/pullbox/serverFactory", [
"use strict"; "use strict";
runtime.loadClass("ops.PullBoxServer"); runtime.loadClass("ops.PullBoxServer");
runtime.loadClass("ops.PullBoxUserModel"); runtime.loadClass("ops.PullBoxMemberModel");
runtime.loadClass("ops.PullBoxOperationRouter"); runtime.loadClass("ops.PullBoxOperationRouter");
/** /**
@ -55,8 +55,8 @@ define("webodf/editor/server/pullbox/serverFactory", [
this.createOperationRouter = function (sid, mid, server) { this.createOperationRouter = function (sid, mid, server) {
return new ops.PullBoxOperationRouter(sid, mid, server); return new ops.PullBoxOperationRouter(sid, mid, server);
}; };
this.createUserModel = function (server) { this.createMemberModel = function (sid, server) {
return new ops.PullBoxUserModel(server); return new ops.PullBoxMemberModel(sid, server);
}; };
this.createSessionList = function (server) { this.createSessionList = function (server) {
return new PullBoxSessionList(server); return new PullBoxSessionList(server);

View File

@ -77,14 +77,16 @@ define("webodf/editor/server/pullbox/sessionList", [], function () {
function pullSessionList() { function pullSessionList() {
if (!pullingActive) { return; } if (!pullingActive) { return; }
server.call("session-list", function(responseData) { server.call({
command: "query_sessiondata_list"
}, function(responseData) {
var response = runtime.fromJson(responseData), var response = runtime.fromJson(responseData),
sessionList, i, sessionList, i,
unupdatedSessions = {}; unupdatedSessions = {};
runtime.log("session-list reply: " + responseData); runtime.log("query_sessiondata_list reply: " + responseData);
if (response.hasOwnProperty("session_list")) { if (response.hasOwnProperty("sessiondata_list")) {
// collect known sessions // collect known sessions
for (i in cachedSessionData) { for (i in cachedSessionData) {
if (cachedSessionData.hasOwnProperty(i)) { if (cachedSessionData.hasOwnProperty(i)) {
@ -93,7 +95,7 @@ define("webodf/editor/server/pullbox/sessionList", [], function () {
} }
// add/update with all delivered sessions // add/update with all delivered sessions
sessionList = response.session_list; sessionList = response.sessiondata_list;
for (i = 0; i < sessionList.length; i++) { for (i = 0; i < sessionList.length; i++) {
if (unupdatedSessions.hasOwnProperty(sessionList[i].id)) { if (unupdatedSessions.hasOwnProperty(sessionList[i].id)) {
delete unupdatedSessions[sessionList[i].id]; delete unupdatedSessions[sessionList[i].id];

View File

@ -2994,6 +2994,7 @@ core.Utils = function Utils() {
this.hashString = hashString this.hashString = hashString
}; };
core.DomUtils = function DomUtils() { core.DomUtils = function DomUtils() {
var self = this;
function findStablePoint(container, offset) { function findStablePoint(container, offset) {
if(offset < container.childNodes.length) { if(offset < container.childNodes.length) {
container = container.childNodes[offset]; container = container.childNodes[offset];
@ -3108,7 +3109,26 @@ core.DomUtils = function DomUtils() {
var nodeLength = node.nodeType === Node.TEXT_NODE ? node.length : node.childNodes.length; var nodeLength = node.nodeType === Node.TEXT_NODE ? node.length : node.childNodes.length;
return range.comparePoint(node, 0) <= 0 && range.comparePoint(node, nodeLength) >= 0 return range.comparePoint(node, 0) <= 0 && range.comparePoint(node, nodeLength) >= 0
} }
this.rangeIntersectsNode = rangeIntersectsNode this.rangeIntersectsNode = rangeIntersectsNode;
function containsNode(parent, descendant) {
return parent === descendant || parent.contains(descendant)
}
this.containsNode = containsNode;
function containsNodeForBrokenWebKit(parent, descendant) {
return parent === descendant || Boolean(parent.compareDocumentPosition(descendant) & Node.DOCUMENT_POSITION_CONTAINED_BY)
}
function init() {
var window = runtime.getWindow(), appVersion, webKitOrSafari;
if(window === null) {
return
}
appVersion = window.navigator.appVersion.toLowerCase();
webKitOrSafari = appVersion.indexOf("chrome") === -1 && (appVersion.indexOf("applewebkit") !== -1 || appVersion.indexOf("safari") !== -1);
if(webKitOrSafari) {
self.containsNode = containsNodeForBrokenWebKit
}
}
init()
}; };
runtime.loadClass("core.DomUtils"); runtime.loadClass("core.DomUtils");
core.Cursor = function Cursor(document, memberId) { core.Cursor = function Cursor(document, memberId) {
@ -5724,7 +5744,7 @@ xmldom.XPath = function() {
@source: http://www.webodf.org/ @source: http://www.webodf.org/
@source: http://gitorious.org/webodf/webodf/ @source: http://gitorious.org/webodf/webodf/
*/ */
gui.AnnotationViewManager = function AnnotationViewManager(odfFragment, annotationsPane) { gui.AnnotationViewManager = function AnnotationViewManager(odfCanvas, odfFragment, annotationsPane) {
var annotations = [], doc = odfFragment.ownerDocument, odfUtils = new odf.OdfUtils, CONNECTOR_MARGIN = 30, NOTE_MARGIN = 20, window = runtime.getWindow(); var annotations = [], doc = odfFragment.ownerDocument, odfUtils = new odf.OdfUtils, CONNECTOR_MARGIN = 30, NOTE_MARGIN = 20, window = runtime.getWindow();
runtime.assert(Boolean(window), "Expected to be run in an environment which has a global window, like a browser."); runtime.assert(Boolean(window), "Expected to be run in an environment which has a global window, like a browser.");
function wrapAnnotation(annotation) { function wrapAnnotation(annotation) {
@ -5782,21 +5802,21 @@ gui.AnnotationViewManager = function AnnotationViewManager(odfFragment, annotati
return Math.sqrt(xs + ys) return Math.sqrt(xs + ys)
} }
function renderAnnotation(annotation) { function renderAnnotation(annotation) {
var annotationNote = annotation.node.parentNode, connectorHorizontal = annotationNote.nextSibling, connectorAngular = connectorHorizontal.nextSibling, annotationWrapper = annotationNote.parentNode, connectorAngle = 0, previousAnnotation = annotations[annotations.indexOf(annotation) - 1], previousRect, creatorNode = annotation.node.getElementsByTagNameNS(odf.Namespaces.dcns, "creator")[0], creatorName; var annotationNote = annotation.node.parentNode, connectorHorizontal = annotationNote.nextSibling, connectorAngular = connectorHorizontal.nextSibling, annotationWrapper = annotationNote.parentNode, connectorAngle = 0, previousAnnotation = annotations[annotations.indexOf(annotation) - 1], previousRect, creatorNode = annotation.node.getElementsByTagNameNS(odf.Namespaces.dcns, "creator")[0], creatorName, zoomLevel = odfCanvas.getZoomLevel();
annotationNote.style.left = annotationsPane.getBoundingClientRect().left - annotationWrapper.getBoundingClientRect().left + "px"; annotationNote.style.left = (annotationsPane.getBoundingClientRect().left - annotationWrapper.getBoundingClientRect().left) / zoomLevel + "px";
annotationNote.style.width = annotationsPane.getBoundingClientRect().width + "px"; annotationNote.style.width = annotationsPane.getBoundingClientRect().width / zoomLevel + "px";
connectorHorizontal.style.width = parseFloat(annotationNote.style.left) - CONNECTOR_MARGIN + "px"; connectorHorizontal.style.width = parseFloat(annotationNote.style.left) - CONNECTOR_MARGIN + "px";
if(previousAnnotation) { if(previousAnnotation) {
previousRect = previousAnnotation.node.parentNode.getBoundingClientRect(); previousRect = previousAnnotation.node.parentNode.getBoundingClientRect();
if(annotationWrapper.getBoundingClientRect().top - previousRect.bottom <= NOTE_MARGIN) { if((annotationWrapper.getBoundingClientRect().top - previousRect.bottom) / zoomLevel <= NOTE_MARGIN) {
annotationNote.style.top = Math.abs(annotationWrapper.getBoundingClientRect().top - previousRect.bottom) + NOTE_MARGIN + "px" annotationNote.style.top = Math.abs(annotationWrapper.getBoundingClientRect().top - previousRect.bottom) / zoomLevel + NOTE_MARGIN + "px"
}else { }else {
annotationNote.style.top = "0px" annotationNote.style.top = "0px"
} }
} }
connectorAngular.style.left = connectorHorizontal.getBoundingClientRect().width + "px"; connectorAngular.style.left = connectorHorizontal.getBoundingClientRect().width / zoomLevel + "px";
connectorAngular.style.width = lineDistance({x:connectorAngular.getBoundingClientRect().left, y:connectorAngular.getBoundingClientRect().top}, {x:annotationNote.getBoundingClientRect().left, y:annotationNote.getBoundingClientRect().top}) + "px"; connectorAngular.style.width = lineDistance({x:connectorAngular.getBoundingClientRect().left / zoomLevel, y:connectorAngular.getBoundingClientRect().top / zoomLevel}, {x:annotationNote.getBoundingClientRect().left / zoomLevel, y:annotationNote.getBoundingClientRect().top / zoomLevel}) + "px";
connectorAngle = Math.asin((annotationNote.getBoundingClientRect().top - connectorAngular.getBoundingClientRect().top) / parseFloat(connectorAngular.style.width)); connectorAngle = Math.asin((annotationNote.getBoundingClientRect().top - connectorAngular.getBoundingClientRect().top) / (zoomLevel * parseFloat(connectorAngular.style.width)));
connectorAngular.style.transform = "rotate(" + connectorAngle + "rad)"; connectorAngular.style.transform = "rotate(" + connectorAngle + "rad)";
connectorAngular.style.MozTransform = "rotate(" + connectorAngle + "rad)"; connectorAngular.style.MozTransform = "rotate(" + connectorAngle + "rad)";
connectorAngular.style.WebkitTransform = "rotate(" + connectorAngle + "rad)"; connectorAngular.style.WebkitTransform = "rotate(" + connectorAngle + "rad)";
@ -8618,18 +8638,14 @@ odf.OdfCanvas = function() {
} }
} }
if(url) { if(url) {
if(/^(?:http|https|ftp):\/\//.test(url)) { try {
callback(url) part = container.getPart(url);
}else { part.onchange = function(part) {
try { callback(part.url)
part = container.getPart(url); };
part.onchange = function(part) { part.load()
callback(part.url) }catch(e) {
}; runtime.log("slight problem: " + e)
part.load()
}catch(e) {
runtime.log("slight problem: " + e)
}
} }
}else { }else {
url = getUrlFromBinaryDataElement(image); url = getUrlFromBinaryDataElement(image);
@ -8917,7 +8933,7 @@ odf.OdfCanvas = function() {
} }
odf.OdfCanvas = function OdfCanvas(element) { odf.OdfCanvas = function OdfCanvas(element) {
runtime.assert(element !== null && element !== undefined, "odf.OdfCanvas constructor needs DOM element"); runtime.assert(element !== null && element !== undefined, "odf.OdfCanvas constructor needs DOM element");
var doc = element.ownerDocument, odfcontainer, formatting = new odf.Formatting, selectionWatcher = new SelectionWatcher(element), pageSwitcher, fontcss, stylesxmlcss, positioncss, editable = false, zoomLevel = 1, eventHandlers = {}, editparagraph, loadingQueue = new LoadingQueue; var self = this, doc = element.ownerDocument, odfcontainer, formatting = new odf.Formatting, selectionWatcher = new SelectionWatcher(element), pageSwitcher, fontcss, stylesxmlcss, positioncss, editable = false, zoomLevel = 1, eventHandlers = {}, editparagraph, loadingQueue = new LoadingQueue;
addWebODFStyleSheet(doc); addWebODFStyleSheet(doc);
pageSwitcher = new PageSwitcher(addStyleSheet(doc)); pageSwitcher = new PageSwitcher(addStyleSheet(doc));
fontcss = addStyleSheet(doc); fontcss = addStyleSheet(doc);
@ -9029,7 +9045,7 @@ odf.OdfCanvas = function() {
if(annotationManager) { if(annotationManager) {
annotationManager.forgetAnnotations() annotationManager.forgetAnnotations()
} }
annotationManager = new gui.AnnotationViewManager(odfnode.body, annotationsPane); annotationManager = new gui.AnnotationViewManager(self, odfnode.body, annotationsPane);
modifyAnnotations(odfnode.body) modifyAnnotations(odfnode.body)
}else { }else {
if(annotationsPane.parentNode) { if(annotationsPane.parentNode) {
@ -9331,6 +9347,8 @@ ops.Server.prototype.networkStatus = function() {
}; };
ops.Server.prototype.login = function(login, password, successCb, failCb) { ops.Server.prototype.login = function(login, password, successCb, failCb) {
}; };
ops.Server.prototype.joinSession = function(userId, sessionId, successCb, failCb) {
};
ops.Server.prototype.getGenesisUrl = function(sessionId) { ops.Server.prototype.getGenesisUrl = function(sessionId) {
}; };
/* /*
@ -9368,16 +9386,10 @@ ops.Server.prototype.getGenesisUrl = function(sessionId) {
@source: http://gitorious.org/webodf/webodf/ @source: http://gitorious.org/webodf/webodf/
*/ */
ops.NowjsServer = function NowjsServer() { ops.NowjsServer = function NowjsServer() {
var self = this, nowObject; var nowObject;
this.getNowObject = function() { this.getNowObject = function() {
return nowObject return nowObject
}; };
function createOperationRouter(sid, mid) {
return new ops.NowjsOperationRouter(sid, mid, self)
}
function createUserModel() {
return new ops.NowjsUserModel(self)
}
this.getGenesisUrl = function(sessionId) { this.getGenesisUrl = function(sessionId) {
return"/session/" + sessionId + "/genesis" return"/session/" + sessionId + "/genesis"
}; };
@ -9421,8 +9433,12 @@ ops.NowjsServer = function NowjsServer() {
nowObject.login(login, password, successCb, failCb) nowObject.login(login, password, successCb, failCb)
} }
}; };
this.createOperationRouter = createOperationRouter; this.joinSession = function(userId, sessionId, successCb, failCb) {
this.createUserModel = createUserModel nowObject.joinSession(userId, sessionId, function(memberId) {
nowObject.memberid = memberId;
successCb(memberId)
}, failCb)
}
}; };
/* /*
@ -9523,6 +9539,19 @@ ops.PullBoxServer = function PullBoxServer(args) {
failCb(responseData) failCb(responseData)
} }
}) })
};
this.joinSession = function(userId, sessionId, successCb, failCb) {
call({command:"join_session", args:{user_id:userId, es_id:sessionId}}, function(responseData) {
var response = (runtime.fromJson(responseData));
runtime.log("join_session reply: " + responseData);
if(response.hasOwnProperty("success") && response.success) {
successCb(response.member_id)
}else {
if(failCb) {
failCb()
}
}
})
} }
}; };
/* /*
@ -10635,7 +10664,7 @@ ops.OpSetParagraphStyle = function OpSetParagraphStyle() {
}; };
this.transform = function(otherOp, hasPriority) { this.transform = function(otherOp, hasPriority) {
var otherOpspec = otherOp.spec(), otherOpType = otherOpspec.optype; var otherOpspec = otherOp.spec(), otherOpType = otherOpspec.optype;
if(otherOpType === "DeleteParagraphStyle") { if(otherOpType === "RemoveParagraphStyle") {
if(otherOpspec.styleName === styleName) { if(otherOpspec.styleName === styleName) {
styleName = "" styleName = ""
} }
@ -10759,7 +10788,7 @@ ops.OpUpdateParagraphStyle = function OpUpdateParagraphStyle() {
} }
} }
}else { }else {
if(otherOpType === "DeleteParagraphStyle") { if(otherOpType === "RemoveParagraphStyle") {
if(otherOpspec.styleName === styleName) { if(otherOpspec.styleName === styleName) {
return[] return[]
} }
@ -10866,7 +10895,7 @@ ops.OpAddParagraphStyle = function OpAddParagraphStyle() {
}; };
this.transform = function(otherOp, hasPriority) { this.transform = function(otherOp, hasPriority) {
var otherSpec = otherOp.spec(); var otherSpec = otherOp.spec();
if((otherSpec.optype === "UpdateParagraphStyle" || otherSpec.optype === "DeleteParagraphStyle") && otherSpec.styleName === styleName) { if((otherSpec.optype === "UpdateParagraphStyle" || otherSpec.optype === "RemoveParagraphStyle") && otherSpec.styleName === styleName) {
return null return null
} }
return[self] return[self]
@ -10949,8 +10978,8 @@ ops.OpAddParagraphStyle = function OpAddParagraphStyle() {
@source: http://www.webodf.org/ @source: http://www.webodf.org/
@source: http://gitorious.org/webodf/webodf/ @source: http://gitorious.org/webodf/webodf/
*/ */
ops.OpDeleteParagraphStyle = function OpDeleteParagraphStyle() { ops.OpRemoveParagraphStyle = function OpRemoveParagraphStyle() {
var self = this, optype = "DeleteParagraphStyle", memberid, timestamp, styleName; var self = this, optype = "RemoveParagraphStyle", memberid, timestamp, styleName;
this.init = function(data) { this.init = function(data) {
memberid = data.memberid; memberid = data.memberid;
timestamp = data.timestamp; timestamp = data.timestamp;
@ -11137,7 +11166,7 @@ runtime.loadClass("ops.OpSplitParagraph");
runtime.loadClass("ops.OpSetParagraphStyle"); runtime.loadClass("ops.OpSetParagraphStyle");
runtime.loadClass("ops.OpUpdateParagraphStyle"); runtime.loadClass("ops.OpUpdateParagraphStyle");
runtime.loadClass("ops.OpAddParagraphStyle"); runtime.loadClass("ops.OpAddParagraphStyle");
runtime.loadClass("ops.OpDeleteParagraphStyle"); runtime.loadClass("ops.OpRemoveParagraphStyle");
runtime.loadClass("ops.OpAddAnnotation"); runtime.loadClass("ops.OpAddAnnotation");
ops.OperationFactory = function OperationFactory() { ops.OperationFactory = function OperationFactory() {
var specs; var specs;
@ -11158,7 +11187,7 @@ ops.OperationFactory = function OperationFactory() {
} }
} }
function init() { function init() {
specs = {AddCursor:constructor(ops.OpAddCursor), ApplyDirectStyling:constructor(ops.OpApplyDirectStyling), InsertTable:constructor(ops.OpInsertTable), InsertText:constructor(ops.OpInsertText), RemoveText:constructor(ops.OpRemoveText), SplitParagraph:constructor(ops.OpSplitParagraph), SetParagraphStyle:constructor(ops.OpSetParagraphStyle), UpdateParagraphStyle:constructor(ops.OpUpdateParagraphStyle), AddParagraphStyle:constructor(ops.OpAddParagraphStyle), DeleteParagraphStyle:constructor(ops.OpDeleteParagraphStyle), specs = {AddCursor:constructor(ops.OpAddCursor), ApplyDirectStyling:constructor(ops.OpApplyDirectStyling), InsertTable:constructor(ops.OpInsertTable), InsertText:constructor(ops.OpInsertText), RemoveText:constructor(ops.OpRemoveText), SplitParagraph:constructor(ops.OpSplitParagraph), SetParagraphStyle:constructor(ops.OpSetParagraphStyle), UpdateParagraphStyle:constructor(ops.OpUpdateParagraphStyle), AddParagraphStyle:constructor(ops.OpAddParagraphStyle), RemoveParagraphStyle:constructor(ops.OpRemoveParagraphStyle),
MoveCursor:constructor(ops.OpMoveCursor), RemoveCursor:constructor(ops.OpRemoveCursor), AddAnnotation:constructor(ops.OpAddAnnotation)} MoveCursor:constructor(ops.OpMoveCursor), RemoveCursor:constructor(ops.OpRemoveCursor), AddAnnotation:constructor(ops.OpAddAnnotation)}
} }
init() init()
@ -11555,7 +11584,7 @@ runtime.loadClass("ops.OpSplitParagraph");
runtime.loadClass("ops.OpSetParagraphStyle"); runtime.loadClass("ops.OpSetParagraphStyle");
runtime.loadClass("ops.OpAddParagraphStyle"); runtime.loadClass("ops.OpAddParagraphStyle");
runtime.loadClass("ops.OpUpdateParagraphStyle"); runtime.loadClass("ops.OpUpdateParagraphStyle");
runtime.loadClass("ops.OpDeleteParagraphStyle"); runtime.loadClass("ops.OpRemoveParagraphStyle");
ops.OperationTransformer = function OperationTransformer() { ops.OperationTransformer = function OperationTransformer() {
var operationFactory; var operationFactory;
function transformOpVsOp(opA, opB) { function transformOpVsOp(opA, opB) {
@ -11731,17 +11760,6 @@ ops.EditInfo = function EditInfo(container, odtDocument) {
return sortEdits() return sortEdits()
}; };
this.addEdit = function(memberid, timestamp) { this.addEdit = function(memberid, timestamp) {
var id, userid = memberid.split("___")[0];
if(!editHistory[memberid]) {
for(id in editHistory) {
if(editHistory.hasOwnProperty(id)) {
if(id.split("___")[0] === userid) {
delete editHistory[id];
break
}
}
}
}
editHistory[memberid] = {time:timestamp} editHistory[memberid] = {time:timestamp}
}; };
this.clearEdits = function() { this.clearEdits = function() {
@ -11890,7 +11908,7 @@ gui.Caret = function Caret(cursor, avatarInitiallyVisible, blinkOnRangeSelect) {
caretOffsetTopLeft.y += caretElement.offsetTop; caretOffsetTopLeft.y += caretElement.offsetTop;
return{left:caretOffsetTopLeft.x - margin, top:caretOffsetTopLeft.y - margin, right:caretOffsetTopLeft.x + caretElement.scrollWidth - 1 + margin, bottom:caretOffsetTopLeft.y + caretElement.scrollHeight - 1 + margin} return{left:caretOffsetTopLeft.x - margin, top:caretOffsetTopLeft.y - margin, right:caretOffsetTopLeft.x + caretElement.scrollWidth - 1 + margin, bottom:caretOffsetTopLeft.y + caretElement.scrollHeight - 1 + margin}
} }
this.refreshCursor = function() { this.refreshCursorBlinking = function() {
if(blinkOnRangeSelect || cursor.getSelectedRange().collapsed) { if(blinkOnRangeSelect || cursor.getSelectedRange().collapsed) {
shouldBlink = true; shouldBlink = true;
blink(true) blink(true)
@ -12168,6 +12186,8 @@ gui.Clipboard = function Clipboard() {
} }
init() init()
}; };
runtime.loadClass("core.DomUtils");
runtime.loadClass("odf.OdfUtils");
runtime.loadClass("ops.OpAddCursor"); runtime.loadClass("ops.OpAddCursor");
runtime.loadClass("ops.OpRemoveCursor"); runtime.loadClass("ops.OpRemoveCursor");
runtime.loadClass("ops.OpMoveCursor"); runtime.loadClass("ops.OpMoveCursor");
@ -12181,7 +12201,8 @@ runtime.loadClass("gui.KeyboardHandler");
runtime.loadClass("gui.StyleHelper"); runtime.loadClass("gui.StyleHelper");
gui.SessionController = function() { gui.SessionController = function() {
gui.SessionController = function SessionController(session, inputMemberId) { gui.SessionController = function SessionController(session, inputMemberId) {
var odtDocument = session.getOdtDocument(), odfUtils = new odf.OdfUtils, clipboard = new gui.Clipboard, clickHandler = new gui.ClickHandler, keyDownHandler = new gui.KeyboardHandler, keyPressHandler = new gui.KeyboardHandler, styleHelper = new gui.StyleHelper(odtDocument.getFormatting()), keyboardMovementsFilter = new core.PositionFilterChain, baseFilter = odtDocument.getPositionFilter(), undoManager = null; var window = (runtime.getWindow()), odtDocument = session.getOdtDocument(), domUtils = new core.DomUtils, odfUtils = new odf.OdfUtils, clipboard = new gui.Clipboard, clickHandler = new gui.ClickHandler, keyDownHandler = new gui.KeyboardHandler, keyPressHandler = new gui.KeyboardHandler, styleHelper = new gui.StyleHelper(odtDocument.getFormatting()), keyboardMovementsFilter = new core.PositionFilterChain, baseFilter = odtDocument.getPositionFilter(), undoManager = null;
runtime.assert(window !== null, "Expected to be run in an environment which has a global window, like a browser.");
keyboardMovementsFilter.addFilter("BaseFilter", baseFilter); keyboardMovementsFilter.addFilter("BaseFilter", baseFilter);
keyboardMovementsFilter.addFilter("RootFilter", odtDocument.createRootFilter(inputMemberId)); keyboardMovementsFilter.addFilter("RootFilter", odtDocument.createRootFilter(inputMemberId));
function listenEvent(eventTarget, eventType, eventHandler, includeDirect) { function listenEvent(eventTarget, eventType, eventHandler, includeDirect) {
@ -12258,21 +12279,67 @@ gui.SessionController = function() {
} }
return null return null
} }
function findClosestPosition(node) {
var canvasElement = odtDocument.getOdfCanvas().getElement(), newNode = odtDocument.getRootNode(), newOffset = 0, beforeCanvas, iterator;
beforeCanvas = canvasElement.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_PRECEDING;
if(!beforeCanvas) {
iterator = gui.SelectionMover.createPositionIterator(newNode);
iterator.moveToEnd();
newNode = iterator.container();
newOffset = iterator.unfilteredDomOffset()
}
return{node:newNode, offset:newOffset}
}
function getSelection(e) {
var canvasElement = odtDocument.getOdfCanvas().getElement(), selection = window.getSelection(), anchorNode, anchorOffset, focusNode, focusOffset, anchorNodeInsideCanvas, focusNodeInsideCanvas, caretPos, node;
if(selection.anchorNode === null && selection.focusNode === null) {
caretPos = caretPositionFromPoint(e.clientX, e.clientY);
if(!caretPos) {
return null
}
anchorNode = (caretPos.container);
anchorOffset = caretPos.offset;
focusNode = anchorNode;
focusOffset = anchorOffset
}else {
anchorNode = (selection.anchorNode);
anchorOffset = selection.anchorOffset;
focusNode = (selection.focusNode);
focusOffset = selection.focusOffset
}
runtime.assert(anchorNode !== null && focusNode !== null, "anchorNode is null or focusNode is null");
anchorNodeInsideCanvas = domUtils.containsNode(canvasElement, anchorNode);
focusNodeInsideCanvas = domUtils.containsNode(canvasElement, focusNode);
if(!anchorNodeInsideCanvas && !focusNodeInsideCanvas) {
return null
}
if(!anchorNodeInsideCanvas) {
node = findClosestPosition(anchorNode);
anchorNode = node.node;
anchorOffset = node.offset
}
if(!focusNodeInsideCanvas) {
node = findClosestPosition(focusNode);
focusNode = node.node;
focusOffset = node.offset
}
canvasElement.focus();
return{anchorNode:anchorNode, anchorOffset:anchorOffset, focusNode:focusNode, focusOffset:focusOffset}
}
function selectRange(e) { function selectRange(e) {
runtime.setTimeout(function() { runtime.setTimeout(function() {
var selection = runtime.getWindow().getSelection(), oldPosition = odtDocument.getCursorPosition(inputMemberId), range, caretPos, stepsToAnchor, stepsToFocus, op; var selection = getSelection(e), oldPosition, stepsToAnchor, stepsToFocus, op;
if(selection.anchorNode === null && selection.focusNode === null) { if(selection === null) {
caretPos = caretPositionFromPoint(e.clientX, e.clientY); return
if(caretPos) {
range = odtDocument.getDOM().createRange();
range.setStart(caretPos.container, caretPos.offset);
range.collapse(true);
selection.addRange(range)
}
} }
stepsToAnchor = countStepsToNode(selection.anchorNode, selection.anchorOffset); stepsToAnchor = countStepsToNode(selection.anchorNode, selection.anchorOffset);
stepsToFocus = countStepsToNode(selection.focusNode, selection.focusOffset); if(selection.focusNode === selection.anchorNode && selection.focusOffset === selection.anchorOffset) {
stepsToFocus = stepsToAnchor
}else {
stepsToFocus = countStepsToNode(selection.focusNode, selection.focusOffset)
}
if(stepsToFocus !== null && stepsToFocus !== 0 || stepsToAnchor !== null && stepsToAnchor !== 0) { if(stepsToFocus !== null && stepsToFocus !== 0 || stepsToAnchor !== null && stepsToAnchor !== 0) {
oldPosition = odtDocument.getCursorPosition(inputMemberId);
op = createOpMoveCursor(oldPosition + stepsToAnchor, stepsToFocus - stepsToAnchor); op = createOpMoveCursor(oldPosition + stepsToAnchor, stepsToFocus - stepsToAnchor);
session.enqueue(op) session.enqueue(op)
} }
@ -12282,18 +12349,22 @@ gui.SessionController = function() {
selectRange(e) selectRange(e)
} }
function selectWord() { function selectWord() {
var currentNode, i, c, op, iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), cursorNode = odtDocument.getCursor(inputMemberId).getNode(), oldPosition = odtDocument.getCursorPosition(inputMemberId), alphaNumeric = /[A-Za-z0-9]/, stepsToStart = 0, stepsToEnd = 0; var canvasElement = odtDocument.getOdfCanvas().getElement(), alphaNumeric = /[A-Za-z0-9]/, stepsToStart = 0, stepsToEnd = 0, iterator, cursorNode, oldPosition, currentNode, i, c, op;
if(!domUtils.containsNode(canvasElement, window.getSelection().focusNode)) {
return
}
iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode());
cursorNode = odtDocument.getCursor(inputMemberId).getNode();
iterator.setUnfilteredPosition(cursorNode, 0); iterator.setUnfilteredPosition(cursorNode, 0);
if(iterator.previousPosition()) { if(iterator.previousPosition()) {
currentNode = iterator.getCurrentNode(); currentNode = iterator.getCurrentNode();
if(currentNode.nodeType === Node.TEXT_NODE) { if(currentNode.nodeType === Node.TEXT_NODE) {
for(i = currentNode.data.length - 1;i >= 0;i -= 1) { for(i = currentNode.data.length - 1;i >= 0;i -= 1) {
c = currentNode.data[i]; c = currentNode.data[i];
if(alphaNumeric.test(c)) { if(!alphaNumeric.test(c)) {
stepsToStart -= 1
}else {
break break
} }
stepsToStart -= 1
} }
} }
} }
@ -12303,25 +12374,31 @@ gui.SessionController = function() {
if(currentNode.nodeType === Node.TEXT_NODE) { if(currentNode.nodeType === Node.TEXT_NODE) {
for(i = 0;i < currentNode.data.length;i += 1) { for(i = 0;i < currentNode.data.length;i += 1) {
c = currentNode.data[i]; c = currentNode.data[i];
if(alphaNumeric.test(c)) { if(!alphaNumeric.test(c)) {
stepsToEnd += 1
}else {
break break
} }
stepsToEnd += 1
} }
} }
} }
if(stepsToStart !== 0 || stepsToEnd !== 0) { if(stepsToStart !== 0 || stepsToEnd !== 0) {
oldPosition = odtDocument.getCursorPosition(inputMemberId);
op = createOpMoveCursor(oldPosition + stepsToStart, Math.abs(stepsToStart) + Math.abs(stepsToEnd)); op = createOpMoveCursor(oldPosition + stepsToStart, Math.abs(stepsToStart) + Math.abs(stepsToEnd));
session.enqueue(op) session.enqueue(op)
} }
} }
function selectParagraph() { function selectParagraph() {
var stepsToStart, stepsToEnd, op, iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), paragraphNode = odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode()), oldPosition = odtDocument.getCursorPosition(inputMemberId); var canvasElement = odtDocument.getOdfCanvas().getElement(), iterator, paragraphNode, oldPosition, stepsToStart, stepsToEnd, op;
if(!domUtils.containsNode(canvasElement, window.getSelection().focusNode)) {
return
}
paragraphNode = odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode());
stepsToStart = odtDocument.getDistanceFromCursor(inputMemberId, paragraphNode, 0); stepsToStart = odtDocument.getDistanceFromCursor(inputMemberId, paragraphNode, 0);
iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode());
iterator.moveToEndOfNode(paragraphNode); iterator.moveToEndOfNode(paragraphNode);
stepsToEnd = odtDocument.getDistanceFromCursor(inputMemberId, paragraphNode, iterator.unfilteredDomOffset()); stepsToEnd = odtDocument.getDistanceFromCursor(inputMemberId, paragraphNode, iterator.unfilteredDomOffset());
if(stepsToStart !== 0 || stepsToEnd !== 0) { if(stepsToStart !== 0 || stepsToEnd !== 0) {
oldPosition = odtDocument.getCursorPosition(inputMemberId);
op = createOpMoveCursor(oldPosition + stepsToStart, Math.abs(stepsToStart) + Math.abs(stepsToEnd)); op = createOpMoveCursor(oldPosition + stepsToStart, Math.abs(stepsToStart) + Math.abs(stepsToEnd));
session.enqueue(op) session.enqueue(op)
} }
@ -12540,7 +12617,7 @@ gui.SessionController = function() {
return true return true
} }
function maintainCursorSelection() { function maintainCursorSelection() {
var cursor = odtDocument.getCursor(inputMemberId), selection = runtime.getWindow().getSelection(); var cursor = odtDocument.getCursor(inputMemberId), selection = window.getSelection();
if(cursor) { if(cursor) {
selection.removeAllRanges(); selection.removeAllRanges();
selection.addRange(cursor.getSelectedRange().cloneRange()) selection.addRange(cursor.getSelectedRange().cloneRange())
@ -12583,7 +12660,7 @@ gui.SessionController = function() {
} }
} }
function handlePaste(e) { function handlePaste(e) {
var plainText, window = runtime.getWindow(); var plainText;
if(window.clipboardData && window.clipboardData.getData) { if(window.clipboardData && window.clipboardData.getData) {
plainText = window.clipboardData.getData("Text") plainText = window.clipboardData.getData("Text")
}else { }else {
@ -12655,7 +12732,7 @@ gui.SessionController = function() {
listenEvent(canvasElement, "copy", handleCopy); listenEvent(canvasElement, "copy", handleCopy);
listenEvent(canvasElement, "beforepaste", handleBeforePaste, true); listenEvent(canvasElement, "beforepaste", handleBeforePaste, true);
listenEvent(canvasElement, "paste", handlePaste); listenEvent(canvasElement, "paste", handlePaste);
listenEvent(canvasElement, "mouseup", clickHandler.handleMouseUp); listenEvent(window, "mouseup", clickHandler.handleMouseUp);
listenEvent(canvasElement, "contextmenu", handleContextMenu); listenEvent(canvasElement, "contextmenu", handleContextMenu);
odtDocument.subscribe(ops.OdtDocument.signalOperationExecuted, maintainCursorSelection); odtDocument.subscribe(ops.OdtDocument.signalOperationExecuted, maintainCursorSelection);
odtDocument.subscribe(ops.OdtDocument.signalOperationExecuted, updateUndoStack); odtDocument.subscribe(ops.OdtDocument.signalOperationExecuted, updateUndoStack);
@ -12679,7 +12756,7 @@ gui.SessionController = function() {
removeEvent(canvasElement, "copy", handleCopy); removeEvent(canvasElement, "copy", handleCopy);
removeEvent(canvasElement, "paste", handlePaste); removeEvent(canvasElement, "paste", handlePaste);
removeEvent(canvasElement, "beforepaste", handleBeforePaste); removeEvent(canvasElement, "beforepaste", handleBeforePaste);
removeEvent(canvasElement, "mouseup", clickHandler.handleMouseUp); removeEvent(window, "mouseup", clickHandler.handleMouseUp);
removeEvent(canvasElement, "contextmenu", handleContextMenu); removeEvent(canvasElement, "contextmenu", handleContextMenu);
op = new ops.OpRemoveCursor; op = new ops.OpRemoveCursor;
op.init({memberid:inputMemberId}); op.init({memberid:inputMemberId});
@ -12711,7 +12788,7 @@ gui.SessionController = function() {
return undoManager return undoManager
}; };
function init() { function init() {
var isMacOS = runtime.getWindow().navigator.appVersion.toLowerCase().indexOf("mac") !== -1, modifier = gui.KeyboardHandler.Modifier, keyCode = gui.KeyboardHandler.KeyCode; var isMacOS = window.navigator.appVersion.toLowerCase().indexOf("mac") !== -1, modifier = gui.KeyboardHandler.Modifier, keyCode = gui.KeyboardHandler.KeyCode;
keyDownHandler.bind(keyCode.Tab, modifier.None, function() { keyDownHandler.bind(keyCode.Tab, modifier.None, function() {
insertText("\t"); insertText("\t");
return true return true
@ -12779,15 +12856,9 @@ gui.SessionController = function() {
}; };
return gui.SessionController return gui.SessionController
}(); }();
ops.UserModel = function UserModel() {
};
ops.UserModel.prototype.getUserDetailsAndUpdates = function(memberId, subscriber) {
};
ops.UserModel.prototype.unsubscribeUserDetailsUpdates = function(memberId, subscriber) {
};
/* /*
Copyright (C) 2012 KO GmbH <copyright@kogmbh.com> Copyright (C) 2012-2013 KO GmbH <copyright@kogmbh.com>
@licstart @licstart
The JavaScript code in this page is free software: you can redistribute it The JavaScript code in this page is free software: you can redistribute it
@ -12819,19 +12890,88 @@ ops.UserModel.prototype.unsubscribeUserDetailsUpdates = function(memberId, subsc
@source: http://www.webodf.org/ @source: http://www.webodf.org/
@source: http://gitorious.org/webodf/webodf/ @source: http://gitorious.org/webodf/webodf/
*/ */
ops.TrivialUserModel = function TrivialUserModel() { ops.MemberModel = function MemberModel() {
var users = {}; };
users.bob = {memberid:"bob", fullname:"Bob Pigeon", color:"red", imageurl:"avatar-pigeon.png"}; ops.MemberModel.prototype.getMemberDetailsAndUpdates = function(memberId, subscriber) {
users.alice = {memberid:"alice", fullname:"Alice Bee", color:"green", imageurl:"avatar-flower.png"}; };
users.you = {memberid:"you", fullname:"I, Robot", color:"blue", imageurl:"avatar-joe.png"}; ops.MemberModel.prototype.unsubscribeMemberDetailsUpdates = function(memberId, subscriber) {
this.getUserDetailsAndUpdates = function(memberId, subscriber) { };
var userid = memberId.split("___")[0]; /*
subscriber(memberId, users[userid] || null)
Copyright (C) 2012-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/
*/
ops.TrivialMemberModel = function TrivialMemberModel() {
this.getMemberDetailsAndUpdates = function(memberId, subscriber) {
subscriber(memberId, null)
}; };
this.unsubscribeUserDetailsUpdates = function(memberId, subscriber) { this.unsubscribeMemberDetailsUpdates = function(memberId, subscriber) {
} }
}; };
ops.NowjsUserModel = function NowjsUserModel(server) { /*
Copyright (C) 2012-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/
*/
ops.NowjsMemberModel = function NowjsMemberModel(server) {
var cachedUserData = {}, memberDataSubscribers = {}, nowObject = server.getNowObject(); var cachedUserData = {}, memberDataSubscribers = {}, nowObject = server.getNowObject();
function userIdFromMemberId(memberId) { function userIdFromMemberId(memberId) {
return memberId.split("___")[0] return memberId.split("___")[0]
@ -12846,7 +12986,7 @@ ops.NowjsUserModel = function NowjsUserModel(server) {
} }
} }
} }
this.getUserDetailsAndUpdates = function(memberId, subscriber) { this.getMemberDetailsAndUpdates = function(memberId, subscriber) {
var userId = userIdFromMemberId(memberId), userData = cachedUserData[userId], subscribers = memberDataSubscribers[userId] || [], i; var userId = userIdFromMemberId(memberId), userData = cachedUserData[userId], subscribers = memberDataSubscribers[userId] || [], i;
memberDataSubscribers[userId] = subscribers; memberDataSubscribers[userId] = subscribers;
runtime.assert(subscriber !== undefined, "missing callback"); runtime.assert(subscriber !== undefined, "missing callback");
@ -12856,7 +12996,7 @@ ops.NowjsUserModel = function NowjsUserModel(server) {
} }
} }
if(i < subscribers.length) { if(i < subscribers.length) {
runtime.log("double subscription request for " + memberId + " in NowjsUserModel::getUserDetailsAndUpdates") runtime.log("double subscription request for " + memberId + " in NowjsMemberModel::getMemberDetailsAndUpdates")
}else { }else {
subscribers.push({memberId:memberId, subscriber:subscriber}); subscribers.push({memberId:memberId, subscriber:subscriber});
if(subscribers.length === 1) { if(subscribers.length === 1) {
@ -12867,7 +13007,7 @@ ops.NowjsUserModel = function NowjsUserModel(server) {
subscriber(memberId, userData) subscriber(memberId, userData)
} }
}; };
this.unsubscribeUserDetailsUpdates = function(memberId, subscriber) { this.unsubscribeMemberDetailsUpdates = function(memberId, subscriber) {
var i, userId = userIdFromMemberId(memberId), subscribers = memberDataSubscribers[userId]; var i, userId = userIdFromMemberId(memberId), subscribers = memberDataSubscribers[userId];
runtime.assert(subscriber !== undefined, "missing subscriber parameter or null"); runtime.assert(subscriber !== undefined, "missing subscriber parameter or null");
runtime.assert(subscribers, "tried to unsubscribe when no one is subscribed ('" + memberId + "')"); runtime.assert(subscribers, "tried to unsubscribe when no one is subscribed ('" + memberId + "')");
@ -12926,61 +13066,53 @@ ops.NowjsUserModel = function NowjsUserModel(server) {
@source: http://www.webodf.org/ @source: http://www.webodf.org/
@source: http://gitorious.org/webodf/webodf/ @source: http://gitorious.org/webodf/webodf/
*/ */
ops.PullBoxUserModel = function PullBoxUserModel(server) { ops.PullBoxMemberModel = function PullBoxMemberModel(sessionId, server) {
var cachedUserData = {}, memberDataSubscribers = {}, serverPullingActivated = false, pullingIntervall = 2E4; var cachedMemberData = {}, memberDataSubscribers = {}, serverPullingActivated = false, pullingIntervall = 2E4;
function userIdFromMemberId(memberId) { function cacheMemberDatum(memberData) {
return memberId.split("___")[0]
}
function cacheUserDatum(userData) {
var subscribers, i; var subscribers, i;
subscribers = memberDataSubscribers[userData.userid]; subscribers = memberDataSubscribers[memberData.memberid];
if(subscribers) { if(subscribers) {
cachedUserData[userData.userid] = userData; cachedMemberData[memberData.memberid] = memberData;
for(i = 0;i < subscribers.length;i += 1) { for(i = 0;i < subscribers.length;i += 1) {
subscribers[i].subscriber(subscribers[i].memberId, userData) subscribers[i](memberData.memberid, memberData)
} }
} }
} }
function pullUserData() { function pullMemberData() {
var i, userIds = []; var i, memberIds = Object.keys(memberDataSubscribers);
for(i in memberDataSubscribers) { runtime.log("member-list request for : " + memberIds.join(","));
if(memberDataSubscribers.hasOwnProperty(i)) { server.call({command:"query_memberdata_list", args:{es_id:sessionId, member_ids:memberIds}}, function(responseData) {
userIds.push(i) var response = (runtime.fromJson(responseData)), memberDataList, newMemberData, oldMemberData;
} runtime.log("member-list reply: " + responseData);
} if(response.hasOwnProperty("memberdata_list")) {
runtime.log("user-list request for : " + userIds.join(",")); memberDataList = response.memberdata_list;
server.call({command:"user-list", args:{user_ids:userIds}}, function(responseData) { for(i = 0;i < memberDataList.length;i += 1) {
var response = (runtime.fromJson(responseData)), userList, newUserData, oldUserData; newMemberData = {memberid:memberDataList[i].member_id, fullname:memberDataList[i].display_name, imageurl:memberDataList[i].avatar_url, color:memberDataList[i].color};
runtime.log("user-list reply: " + responseData); oldMemberData = cachedMemberData.hasOwnProperty(newMemberData.memberid) ? cachedMemberData[newMemberData.memberid] : null;
if(response.hasOwnProperty("userdata_list")) { if(!oldMemberData || oldMemberData.fullname !== newMemberData.fullname || oldMemberData.imageurl !== newMemberData.imageurl || oldMemberData.color !== newMemberData.color) {
userList = response.userdata_list; cacheMemberDatum(newMemberData)
for(i = 0;i < userList.length;i += 1) {
newUserData = {userid:userList[i].uid, fullname:userList[i].fullname, imageurl:"/user/" + userList[i].avatarId + "/avatar.png", color:userList[i].color};
oldUserData = cachedUserData.hasOwnProperty(userList[i].uid) ? cachedUserData[userList[i].uid] : null;
if(!oldUserData || oldUserData.fullname !== newUserData.fullname || oldUserData.imageurl !== newUserData.imageurl || oldUserData.color !== newUserData.color) {
cacheUserDatum(newUserData)
} }
} }
}else { }else {
runtime.log("Meh, userlist data broken: " + responseData) runtime.log("Meh, memberdata list broken: " + responseData)
} }
}) })
} }
function periodicPullUserData() { function periodicPullMemberData() {
if(!serverPullingActivated) { if(!serverPullingActivated) {
return return
} }
pullUserData(); pullMemberData();
runtime.setTimeout(periodicPullUserData, pullingIntervall) runtime.setTimeout(periodicPullMemberData, pullingIntervall)
} }
function activatePeriodicUserDataPulling() { function activatePeriodicMemberDataPulling() {
if(serverPullingActivated) { if(serverPullingActivated) {
return return
} }
serverPullingActivated = true; serverPullingActivated = true;
runtime.setTimeout(periodicPullUserData, pullingIntervall) runtime.setTimeout(periodicPullMemberData, pullingIntervall)
} }
function deactivatePeriodicUserDataPulling() { function deactivatePeriodicMemberDataPulling() {
var key; var key;
if(!serverPullingActivated) { if(!serverPullingActivated) {
return return
@ -12992,35 +13124,35 @@ ops.PullBoxUserModel = function PullBoxUserModel(server) {
} }
serverPullingActivated = false serverPullingActivated = false
} }
this.getUserDetailsAndUpdates = function(memberId, subscriber) { this.getMemberDetailsAndUpdates = function(memberId, subscriber) {
var userId = userIdFromMemberId(memberId), userData = cachedUserData[userId], subscribers = memberDataSubscribers[userId] || [], i; var memberData = cachedMemberData[memberId], subscribers = memberDataSubscribers[memberId] || [], i;
memberDataSubscribers[userId] = subscribers; memberDataSubscribers[memberId] = subscribers;
runtime.assert(subscriber !== undefined, "missing callback"); runtime.assert(subscriber !== undefined, "missing callback");
for(i = 0;i < subscribers.length;i += 1) { for(i = 0;i < subscribers.length;i += 1) {
if(subscribers[i].subscriber === subscriber && subscribers[i].memberId === memberId) { if(subscribers[i] === subscriber) {
break break
} }
} }
if(i < subscribers.length) { if(i < subscribers.length) {
runtime.log("double subscription request for " + memberId + " in PullBoxUserModel::getUserDetailsAndUpdates") runtime.log("double subscription request for " + memberId + " in PullBoxMemberModel::getMemberDetailsAndUpdates")
}else { }else {
subscribers.push({memberId:memberId, subscriber:subscriber}); subscribers.push(subscriber);
if(subscribers.length === 1) { if(subscribers.length === 1) {
pullUserData() pullMemberData()
} }
} }
if(userData) { if(memberData) {
subscriber(memberId, userData) subscriber(memberId, memberData)
} }
activatePeriodicUserDataPulling() activatePeriodicMemberDataPulling()
}; };
this.unsubscribeUserDetailsUpdates = function(memberId, subscriber) { this.unsubscribeMemberDetailsUpdates = function(memberId, subscriber) {
var i, userId = userIdFromMemberId(memberId), subscribers = memberDataSubscribers[userId]; var i, subscribers = memberDataSubscribers[memberId];
runtime.assert(subscriber !== undefined, "missing subscriber parameter or null"); runtime.assert(subscriber !== undefined, "missing subscriber parameter or null");
runtime.assert(subscribers, "tried to unsubscribe when no one is subscribed ('" + memberId + "')"); runtime.assert(subscribers, "tried to unsubscribe when no one is subscribed ('" + memberId + "')");
if(subscribers) { if(subscribers) {
for(i = 0;i < subscribers.length;i += 1) { for(i = 0;i < subscribers.length;i += 1) {
if(subscribers[i].subscriber === subscriber && subscribers[i].memberId === memberId) { if(subscribers[i] === subscriber) {
break break
} }
} }
@ -13028,9 +13160,9 @@ ops.PullBoxUserModel = function PullBoxUserModel(server) {
subscribers.splice(i, 1); subscribers.splice(i, 1);
if(subscribers.length === 0) { if(subscribers.length === 0) {
runtime.log("no more subscribers for: " + memberId); runtime.log("no more subscribers for: " + memberId);
delete memberDataSubscribers[userId]; delete memberDataSubscribers[memberId];
delete cachedUserData[userId]; delete cachedMemberData[memberId];
deactivatePeriodicUserDataPulling() deactivatePeriodicMemberDataPulling()
} }
} }
}; };
@ -13193,14 +13325,7 @@ ops.NowjsOperationRouter = function NowjsOperationRouter(sessionId, memberid, se
done_cb() done_cb()
} }
}) })
};
function init() {
nowObject.memberid = memberid;
nowObject.joinSession(sessionId, function(sessionJoinSuccess) {
runtime.assert(sessionJoinSuccess, "Trying to join a session which does not exists or where we are already in")
})
} }
init()
}; };
/* /*
@ -13336,10 +13461,10 @@ ops.PullBoxOperationRouter = function PullBoxOperationRouter(sessionId, memberId
syncLock = true; syncLock = true;
syncedClientOpspecs = unsyncedClientOpspecQueue; syncedClientOpspecs = unsyncedClientOpspecQueue;
unsyncedClientOpspecQueue = []; unsyncedClientOpspecQueue = [];
server.call({command:"sync-ops", args:{es_id:sessionId, member_id:memberId, seq_head:String(lastServerSeq), client_ops:syncedClientOpspecs}}, function(responseData) { server.call({command:"sync_ops", args:{es_id:sessionId, member_id:memberId, seq_head:String(lastServerSeq), client_ops:syncedClientOpspecs}}, function(responseData) {
var shouldRetryInstantly = false, response = (runtime.fromJson(responseData)); var shouldRetryInstantly = false, response = (runtime.fromJson(responseData));
runtime.log("sync-ops reply: " + responseData); runtime.log("sync-ops reply: " + responseData);
if(response.result === "newOps") { if(response.result === "new_ops") {
if(response.ops.length > 0) { if(response.ops.length > 0) {
if(unsyncedClientOpspecQueue.length === 0) { if(unsyncedClientOpspecQueue.length === 0) {
receiveOpSpecsFromNetwork(compressOpSpecs(response.ops)) receiveOpSpecsFromNetwork(compressOpSpecs(response.ops))
@ -13347,18 +13472,18 @@ ops.PullBoxOperationRouter = function PullBoxOperationRouter(sessionId, memberId
runtime.log("meh, have new ops locally meanwhile, have to do transformations."); runtime.log("meh, have new ops locally meanwhile, have to do transformations.");
hasUnresolvableConflict = !handleOpsSyncConflict(compressOpSpecs(response.ops)) hasUnresolvableConflict = !handleOpsSyncConflict(compressOpSpecs(response.ops))
} }
lastServerSeq = response.headSeq lastServerSeq = response.head_seq
} }
}else { }else {
if(response.result === "added") { if(response.result === "added") {
runtime.log("All added to server"); runtime.log("All added to server");
lastServerSeq = response.headSeq lastServerSeq = response.head_seq
}else { }else {
if(response.result === "conflict") { if(response.result === "conflict") {
unsyncedClientOpspecQueue = syncedClientOpspecs.concat(unsyncedClientOpspecQueue); unsyncedClientOpspecQueue = syncedClientOpspecs.concat(unsyncedClientOpspecQueue);
runtime.log("meh, server has new ops meanwhile, have to do transformations."); runtime.log("meh, server has new ops meanwhile, have to do transformations.");
hasUnresolvableConflict = !handleOpsSyncConflict(compressOpSpecs(response.ops)); hasUnresolvableConflict = !handleOpsSyncConflict(compressOpSpecs(response.ops));
lastServerSeq = response.headSeq; lastServerSeq = response.head_seq;
if(!hasUnresolvableConflict) { if(!hasUnresolvableConflict) {
shouldRetryInstantly = true shouldRetryInstantly = true
} }
@ -13423,15 +13548,7 @@ ops.PullBoxOperationRouter = function PullBoxOperationRouter(sessionId, memberId
playbackFunction(timedOp); playbackFunction(timedOp);
unsyncedClientOpspecQueue.push(opspec); unsyncedClientOpspecQueue.push(opspec);
triggerPushingOps() triggerPushingOps()
};
function init() {
server.call({command:"join-session", args:{session_id:sessionId, member_id:memberId}}, function(responseData) {
var response = Boolean(runtime.fromJson(responseData));
runtime.log("join-session reply: " + responseData);
runtime.assert(response, "Trying to join a session which does not exists or where we are already in")
})
} }
init()
}; };
gui.EditInfoHandle = function EditInfoHandle(parentElement) { gui.EditInfoHandle = function EditInfoHandle(parentElement) {
var edits = [], handle, document = (parentElement.ownerDocument), htmlns = document.documentElement.namespaceURI, editinfons = "urn:webodf:names:editinfo"; var edits = [], handle, document = (parentElement.ownerDocument), htmlns = document.documentElement.namespaceURI, editinfons = "urn:webodf:names:editinfo";
@ -13561,7 +13678,7 @@ gui.EditInfoMarker = function EditInfoMarker(editInfo, initialVisibility) {
}; };
/* /*
Copyright (C) 2012 KO GmbH <copyright@kogmbh.com> Copyright (C) 2012-2013 KO GmbH <copyright@kogmbh.com>
@licstart @licstart
The JavaScript code in this page is free software: you can redistribute it The JavaScript code in this page is free software: you can redistribute it
@ -13594,7 +13711,7 @@ gui.EditInfoMarker = function EditInfoMarker(editInfo, initialVisibility) {
@source: http://gitorious.org/webodf/webodf/ @source: http://gitorious.org/webodf/webodf/
*/ */
runtime.loadClass("gui.Caret"); runtime.loadClass("gui.Caret");
runtime.loadClass("ops.TrivialUserModel"); runtime.loadClass("ops.TrivialMemberModel");
runtime.loadClass("ops.EditInfo"); runtime.loadClass("ops.EditInfo");
runtime.loadClass("gui.EditInfoMarker"); runtime.loadClass("gui.EditInfoMarker");
gui.SessionViewOptions = function() { gui.SessionViewOptions = function() {
@ -13604,7 +13721,7 @@ gui.SessionViewOptions = function() {
}; };
gui.SessionView = function() { gui.SessionView = function() {
function configOption(userValue, defaultValue) { function configOption(userValue, defaultValue) {
return userValue !== undefined ? userValue : defaultValue return userValue !== undefined ? Boolean(userValue) : defaultValue
} }
function SessionView(viewOptions, session, caretManager) { function SessionView(viewOptions, session, caretManager) {
var avatarInfoStyles, editInfons = "urn:webodf:names:editinfo", editInfoMap = {}, showEditInfoMarkers = configOption(viewOptions.editInfoMarkersInitiallyVisible, true), showCaretAvatars = configOption(viewOptions.caretAvatarsInitiallyVisible, true), blinkOnRangeSelect = configOption(viewOptions.caretBlinksOnRangeSelect, true); var avatarInfoStyles, editInfons = "urn:webodf:names:editinfo", editInfoMap = {}, showEditInfoMarkers = configOption(viewOptions.editInfoMarkersInitiallyVisible, true), showCaretAvatars = configOption(viewOptions.caretAvatarsInitiallyVisible, true), blinkOnRangeSelect = configOption(viewOptions.caretBlinksOnRangeSelect, true);
@ -13708,26 +13825,26 @@ gui.SessionView = function() {
this.getCaret = function(memberid) { this.getCaret = function(memberid) {
return caretManager.getCaret(memberid) return caretManager.getCaret(memberid)
}; };
function renderMemberData(memberId, userData) { function renderMemberData(memberId, memberData) {
var caret = caretManager.getCaret(memberId); var caret = caretManager.getCaret(memberId);
if(userData === undefined) { if(memberData === undefined) {
runtime.log('UserModel sent undefined data for member "' + memberId + '".'); runtime.log('MemberModel sent undefined data for member "' + memberId + '".');
return return
} }
if(userData === null) { if(memberData === null) {
userData = {memberid:memberId, fullname:"Unknown Identity", color:"black", imageurl:"avatar-joe.png"} memberData = {memberid:memberId, fullname:"Unknown Identity", color:"black", imageurl:"avatar-joe.png"}
} }
if(caret) { if(caret) {
caret.setAvatarImageUrl(userData.imageurl); caret.setAvatarImageUrl(memberData.imageurl);
caret.setColor(userData.color) caret.setColor(memberData.color)
} }
setAvatarInfoStyle(memberId, userData.fullname, userData.color) setAvatarInfoStyle(memberId, memberData.fullname, memberData.color)
} }
function onCursorAdded(cursor) { function onCursorAdded(cursor) {
var memberId = cursor.getMemberId(), userModel = session.getUserModel(); var memberId = cursor.getMemberId(), memberModel = session.getMemberModel();
caretManager.registerCursor(cursor, showCaretAvatars, blinkOnRangeSelect); caretManager.registerCursor(cursor, showCaretAvatars, blinkOnRangeSelect);
renderMemberData(memberId, null); renderMemberData(memberId, null);
userModel.getUserDetailsAndUpdates(memberId, renderMemberData); memberModel.getMemberDetailsAndUpdates(memberId, renderMemberData);
runtime.log("+++ View here +++ eagerly created an Caret for '" + memberId + "'! +++") runtime.log("+++ View here +++ eagerly created an Caret for '" + memberId + "'! +++")
} }
function onCursorRemoved(memberid) { function onCursorRemoved(memberid) {
@ -13739,7 +13856,7 @@ gui.SessionView = function() {
} }
} }
if(!hasMemberEditInfo) { if(!hasMemberEditInfo) {
session.getUserModel().unsubscribeUserDetailsUpdates(memberid, renderMemberData) session.getMemberModel().unsubscribeMemberDetailsUpdates(memberid, renderMemberData)
} }
} }
function init() { function init() {
@ -13762,7 +13879,7 @@ gui.SessionView = function() {
}(); }();
/* /*
Copyright (C) 2012 KO GmbH <copyright@kogmbh.com> Copyright (C) 2012-2013 KO GmbH <copyright@kogmbh.com>
@licstart @licstart
The JavaScript code in this page is free software: you can redistribute it The JavaScript code in this page is free software: you can redistribute it
@ -13797,35 +13914,44 @@ gui.SessionView = function() {
runtime.loadClass("gui.Caret"); runtime.loadClass("gui.Caret");
gui.CaretManager = function CaretManager(sessionController) { gui.CaretManager = function CaretManager(sessionController) {
var carets = {}; var carets = {};
function getCaret(memberId) {
return carets.hasOwnProperty(memberId) ? carets[memberId] : null
}
function getCanvasElement() { function getCanvasElement() {
return sessionController.getSession().getOdtDocument().getOdfCanvas().getElement() return sessionController.getSession().getOdtDocument().getOdfCanvas().getElement()
} }
function removeCaret(memberId) { function removeCaret(memberId) {
if(memberId === sessionController.getInputMemberId()) { if(memberId === sessionController.getInputMemberId()) {
getCanvasElement().removeAttribute("tabindex", 0) getCanvasElement().removeAttribute("tabindex")
} }
delete carets[memberId] delete carets[memberId]
} }
function refreshCaret(cursor) { function refreshLocalCaretBlinking(cursor) {
var caret = carets[cursor.getMemberId()]; var caret, memberId = cursor.getMemberId();
if(caret) { if(memberId === sessionController.getInputMemberId()) {
caret.refreshCursor() caret = getCaret(memberId);
if(caret) {
caret.refreshCursorBlinking()
}
} }
} }
function ensureLocalCaretVisible(info) { function ensureLocalCaretVisible(info) {
var caret = carets[info.memberId]; var caret;
if(info.memberId === sessionController.getInputMemberId() && caret) { if(info.memberId === sessionController.getInputMemberId()) {
caret.ensureVisible() caret = getCaret(info.memberId);
if(caret) {
caret.ensureVisible()
}
} }
} }
function focusLocalCaret() { function focusLocalCaret() {
var caret = carets[sessionController.getInputMemberId()]; var caret = getCaret(sessionController.getInputMemberId());
if(caret) { if(caret) {
caret.setFocus() caret.setFocus()
} }
} }
function blurLocalCaret() { function blurLocalCaret() {
var caret = carets[sessionController.getInputMemberId()]; var caret = getCaret(sessionController.getInputMemberId());
if(caret) { if(caret) {
caret.removeFocus() caret.removeFocus()
} }
@ -13841,9 +13967,7 @@ gui.CaretManager = function CaretManager(sessionController) {
} }
return caret return caret
}; };
this.getCaret = function(memberid) { this.getCaret = getCaret;
return carets[memberid]
};
this.getCarets = function() { this.getCarets = function() {
return Object.keys(carets).map(function(memberid) { return Object.keys(carets).map(function(memberid) {
return carets[memberid] return carets[memberid]
@ -13852,7 +13976,7 @@ gui.CaretManager = function CaretManager(sessionController) {
function init() { function init() {
var session = sessionController.getSession(), odtDocument = session.getOdtDocument(), canvasElement = getCanvasElement(); var session = sessionController.getSession(), odtDocument = session.getOdtDocument(), canvasElement = getCanvasElement();
odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, ensureLocalCaretVisible); odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, ensureLocalCaretVisible);
odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, refreshCaret); odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, refreshLocalCaretBlinking);
odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, removeCaret); odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, removeCaret);
canvasElement.onfocus = focusLocalCaret; canvasElement.onfocus = focusLocalCaret;
canvasElement.onblur = blurLocalCaret canvasElement.onblur = blurLocalCaret
@ -15038,14 +15162,14 @@ ops.OdtDocument.signalUndoStackChanged = "undo/changed";
@source: http://www.webodf.org/ @source: http://www.webodf.org/
@source: http://gitorious.org/webodf/webodf/ @source: http://gitorious.org/webodf/webodf/
*/ */
runtime.loadClass("ops.TrivialUserModel"); runtime.loadClass("ops.TrivialMemberModel");
runtime.loadClass("ops.TrivialOperationRouter"); runtime.loadClass("ops.TrivialOperationRouter");
runtime.loadClass("ops.OperationFactory"); runtime.loadClass("ops.OperationFactory");
runtime.loadClass("ops.OdtDocument"); runtime.loadClass("ops.OdtDocument");
ops.Session = function Session(odfCanvas) { ops.Session = function Session(odfCanvas) {
var self = this, operationFactory = new ops.OperationFactory, odtDocument = new ops.OdtDocument(odfCanvas), userModel = new ops.TrivialUserModel, operationRouter = null; var self = this, operationFactory = new ops.OperationFactory, odtDocument = new ops.OdtDocument(odfCanvas), memberModel = new ops.TrivialMemberModel, operationRouter = null;
this.setUserModel = function(uModel) { this.setMemberModel = function(uModel) {
userModel = uModel memberModel = uModel
}; };
this.setOperationFactory = function(opFactory) { this.setOperationFactory = function(opFactory) {
operationFactory = opFactory; operationFactory = opFactory;
@ -15061,8 +15185,8 @@ ops.Session = function Session(odfCanvas) {
}); });
opRouter.setOperationFactory(operationFactory) opRouter.setOperationFactory(operationFactory)
}; };
this.getUserModel = function() { this.getMemberModel = function() {
return userModel return memberModel
}; };
this.getOperationFactory = function() { this.getOperationFactory = function() {
return operationFactory return operationFactory

File diff suppressed because one or more lines are too long