Sync with webodf 82510ae020f8ee8a1f7b65a27b6af107b6023e90
* exposes some state-change/error events in the Editor API * catches more possible errors and handles them (e.g. staying cool on temporary disconnection to server, but warning about it) * improves selection by mouse * fixes selected paragraph style not being set to all selected paragraphs * fixes leaking of some helper attributes into saved ODT files
This commit is contained in:
parent
daf553d06c
commit
e3adf6bd19
90
js/3rdparty/webodf/editor/Editor.js
vendored
90
js/3rdparty/webodf/editor/Editor.js
vendored
@ -76,9 +76,25 @@ define("webodf/editor/Editor", [
|
||||
saveOdtFile = args.saveCallback,
|
||||
close = args.closeCallback,
|
||||
odfCanvas,
|
||||
eventNotifier = new core.EventNotifier([
|
||||
Editor.EVENT_ERROR,
|
||||
Editor.EVENT_BEFORESAVETOFILE,
|
||||
Editor.EVENT_SAVEDTOFILE,
|
||||
Editor.EVENT_HASLOCALUNSYNCEDOPERATIONSCHANGED,
|
||||
Editor.EVENT_HASSESSIONHOSTCONNECTIONCHANGED
|
||||
]),
|
||||
pendingMemberId,
|
||||
pendingEditorReadyCallback;
|
||||
|
||||
/**
|
||||
* @param {!string} eventid
|
||||
* @param {*} args
|
||||
* @return {undefined}
|
||||
*/
|
||||
function fireEvent(eventid, args) {
|
||||
eventNotifier.emit(eventid, args);
|
||||
}
|
||||
|
||||
function getFileBlob(cbSuccess, cbError) {
|
||||
var odfContainer = odfCanvas.odfContainer();
|
||||
|
||||
@ -196,14 +212,27 @@ define("webodf/editor/Editor", [
|
||||
}
|
||||
blob = new Blob([data.buffer], {type: mimetype});
|
||||
saveAs(blob, filename);
|
||||
//TODO: add callback as event handler to saveAs
|
||||
fireEvent(Editor.EVENT_SAVEDTOFILE, null);
|
||||
}
|
||||
function onerror(error) {
|
||||
// TODO: use callback for that
|
||||
alert(error);
|
||||
}
|
||||
|
||||
fireEvent(Editor.EVENT_BEFORESAVETOFILE, null);
|
||||
getFileBlob(onsuccess, onerror);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!Object} error
|
||||
* @return {undefined}
|
||||
*/
|
||||
function handleOperationRouterErrors(error) {
|
||||
// TODO: translate error into Editor ids or at least document the possible values
|
||||
fireEvent(Editor.EVENT_ERROR, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* open the initial document of an editing-session,
|
||||
* request a replay of previous operations, call
|
||||
@ -219,9 +248,24 @@ define("webodf/editor/Editor", [
|
||||
// overwrite router
|
||||
// TODO: serverFactory should be a backendFactory,
|
||||
// and there should be a backendFactory for local editing
|
||||
var opRouter = serverFactory.createOperationRouter(sessionId, memberId, server, odfCanvas.odfContainer());
|
||||
var opRouter = serverFactory.createOperationRouter(sessionId, memberId, server, odfCanvas.odfContainer(), handleOperationRouterErrors);
|
||||
session.setOperationRouter(opRouter);
|
||||
// forward events
|
||||
// TODO: relying here on that opRouter uses the same id strings ATM, those should be defined at OperationRouter interface
|
||||
opRouter.subscribe(Editor.EVENT_HASLOCALUNSYNCEDOPERATIONSCHANGED, function (hasUnsyncedOps) {
|
||||
fireEvent(Editor.EVENT_HASLOCALUNSYNCEDOPERATIONSCHANGED, hasUnsyncedOps);
|
||||
});
|
||||
opRouter.subscribe(Editor.EVENT_HASSESSIONHOSTCONNECTIONCHANGED, function (hasSessionHostConnection) {
|
||||
fireEvent(Editor.EVENT_HASSESSIONHOSTCONNECTIONCHANGED, hasSessionHostConnection);
|
||||
});
|
||||
opRouter.subscribe(Editor.EVENT_BEFORESAVETOFILE, function () {
|
||||
fireEvent(Editor.EVENT_BEFORESAVETOFILE, null);
|
||||
});
|
||||
opRouter.subscribe(Editor.EVENT_SAVEDTOFILE, function () {
|
||||
fireEvent(Editor.EVENT_SAVEDTOFILE, null);
|
||||
});
|
||||
|
||||
// now get existing ops and after that let the user edit
|
||||
opRouter.requestReplay(function done() {
|
||||
editorReadyCallback();
|
||||
});
|
||||
@ -288,6 +332,38 @@ define("webodf/editor/Editor", [
|
||||
editorSession.sessionController.endEditing();
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows to register listeners for certain events. Currently
|
||||
* available events are, with the type of the argument passed to the callback:
|
||||
* Editor.EVENT_BEFORESAVETOFILE - no argument
|
||||
* Editor.EVENT_SAVEDTOFILE - no argument
|
||||
* Editor.EVENT_HASLOCALUNSYNCEDOPERATIONSCHANGED - boolean, reflecting new hasLocalUnsyncedOperations state
|
||||
* Editor.EVENT_HASSESSIONHOSTCONNECTIONCHANGED - boolean, reflecting new hasSessionhostConnection state
|
||||
* Editor.EVENT_ERROR - string, one of these errorcodes:
|
||||
* "notMemberOfSession"
|
||||
* "opExecutionFailure"
|
||||
* "sessionDoesNotExist"
|
||||
* "unknownOpReceived"
|
||||
* "unknownServerReply"
|
||||
* "unresolvableConflictingOps"
|
||||
*
|
||||
* @param {!string} eventid
|
||||
* @param {!Function} listener
|
||||
* @return {undefined}
|
||||
*/
|
||||
this.addEventListener = function (eventid, listener) {
|
||||
eventNotifier.subscribe(eventid, listener);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} eventid
|
||||
* @param {!Function} listener
|
||||
* @return {undefined}
|
||||
*/
|
||||
this.removeEventListener = function (eventid, listener) {
|
||||
eventNotifier.unsubscribe(eventid, listener);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!function(!Object=)} callback, passing an error object in case of error
|
||||
* @return {undefined}
|
||||
@ -440,6 +516,18 @@ define("webodf/editor/Editor", [
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/**@const @type {!string}*/
|
||||
Editor.EVENT_ERROR = "error";
|
||||
/**@const @type {!string}*/
|
||||
Editor.EVENT_BEFORESAVETOFILE = "beforeSaveToFile";
|
||||
/**@const @type {!string}*/
|
||||
Editor.EVENT_SAVEDTOFILE = "savedToFile";
|
||||
/**@const @type {!string}*/
|
||||
Editor.EVENT_HASLOCALUNSYNCEDOPERATIONSCHANGED = "hasLocalUnsyncedOperationsChanged";
|
||||
/**@const @type {!string}*/
|
||||
Editor.EVENT_HASSESSIONHOSTCONNECTIONCHANGED = "hasSessionHostConnectionChanged";
|
||||
|
||||
return Editor;
|
||||
});
|
||||
|
||||
|
48
js/3rdparty/webodf/editor/EditorSession.js
vendored
48
js/3rdparty/webodf/editor/EditorSession.js
vendored
@ -48,7 +48,9 @@ define("webodf/editor/EditorSession", [
|
||||
};
|
||||
|
||||
runtime.loadClass("core.DomUtils");
|
||||
runtime.loadClass("odf.OdfUtils");
|
||||
runtime.loadClass("ops.OdtDocument");
|
||||
runtime.loadClass("ops.StepsTranslator");
|
||||
runtime.loadClass("ops.Session");
|
||||
runtime.loadClass("odf.Namespaces");
|
||||
runtime.loadClass("odf.OdfCanvas");
|
||||
@ -80,6 +82,7 @@ define("webodf/editor/EditorSession", [
|
||||
fontStyles = document.createElement('style'),
|
||||
formatting = odtDocument.getFormatting(),
|
||||
domUtils = new core.DomUtils(),
|
||||
odfUtils = new odf.OdfUtils(),
|
||||
eventNotifier = new core.EventNotifier([
|
||||
EditorSession.signalMemberAdded,
|
||||
EditorSession.signalMemberUpdated,
|
||||
@ -306,16 +309,45 @@ define("webodf/editor/EditorSession", [
|
||||
return currentCommonStyleName;
|
||||
};
|
||||
|
||||
this.setCurrentParagraphStyle = function (value) {
|
||||
var op;
|
||||
if (currentCommonStyleName !== value) {
|
||||
op = new ops.OpSetParagraphStyle();
|
||||
op.init({
|
||||
/**
|
||||
* Round the step up to the next step
|
||||
* @param {!number} step
|
||||
* @returns {!boolean}
|
||||
*/
|
||||
function roundUp(step) {
|
||||
return step === ops.StepsTranslator.NEXT_STEP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the paragraph style with the given
|
||||
* style name to all the paragraphs within
|
||||
* the cursor selection.
|
||||
* @param {!string} styleName
|
||||
* @return {undefined}
|
||||
*/
|
||||
this.setCurrentParagraphStyle = function (styleName) {
|
||||
var range = odtDocument.getCursor(localMemberId).getSelectedRange(),
|
||||
paragraphs = odfUtils.getParagraphElements(range),
|
||||
opQueue = [];
|
||||
|
||||
paragraphs.forEach(function (paragraph) {
|
||||
var paragraphStartPoint = odtDocument.convertDomPointToCursorStep(paragraph, 0, roundUp),
|
||||
paragraphStyleName = paragraph.getAttributeNS(odf.Namespaces.textns, "style-name"),
|
||||
opSetParagraphStyle;
|
||||
|
||||
if (paragraphStyleName !== styleName) {
|
||||
opSetParagraphStyle = new ops.OpSetParagraphStyle();
|
||||
opSetParagraphStyle.init({
|
||||
memberid: localMemberId,
|
||||
position: self.getCursorPosition(),
|
||||
styleName: value
|
||||
styleName: styleName,
|
||||
position: paragraphStartPoint
|
||||
});
|
||||
session.enqueue([op]);
|
||||
opQueue.push(opSetParagraphStyle);
|
||||
}
|
||||
});
|
||||
|
||||
if (opQueue.length > 0) {
|
||||
session.enqueue(opQueue);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -53,9 +53,10 @@ ServerFactory.prototype.createServer = function () {"use strict"; };
|
||||
* @param {!string} memberId
|
||||
* @param {!ops.Server} server
|
||||
* @param {!odf.OdfContainer} odfContainer TODO: needed for pullbox writing to server at end, find better solution
|
||||
* @param {!function(!Object)} errorCallback
|
||||
* @return {!ops.OperationRouter}
|
||||
*/
|
||||
ServerFactory.prototype.createOperationRouter = function (sessionId, memberId, server, odfContainer) {"use strict"; };
|
||||
ServerFactory.prototype.createOperationRouter = function (sessionId, memberId, server, odfContainer, errorCallback) {"use strict"; };
|
||||
|
||||
/**
|
||||
* @param {!ops.Server} server
|
||||
|
@ -50,8 +50,8 @@ define("webodf/editor/server/owncloud/ServerFactory", [
|
||||
};
|
||||
return server;
|
||||
};
|
||||
this.createOperationRouter = function (sid, mid, server, odfContainer) {
|
||||
return new PullBoxOperationRouter(sid, mid, server, odfContainer);
|
||||
this.createOperationRouter = function (sid, mid, server, odfContainer, errorCallback) {
|
||||
return new PullBoxOperationRouter(sid, mid, server, odfContainer, errorCallback);
|
||||
};
|
||||
this.createSessionList = function (server) {
|
||||
return new PullBoxSessionList(server);
|
||||
|
@ -28,7 +28,18 @@
|
||||
define("webodf/editor/server/pullbox/OperationRouter", [], function () {
|
||||
"use strict";
|
||||
|
||||
// TODO: these eventid strings should be defined at OperationRouter interface
|
||||
var /**@const @type {!string}*/
|
||||
EVENT_BEFORESAVETOFILE = "beforeSaveToFile",
|
||||
/**@const @type {!string}*/
|
||||
EVENT_SAVEDTOFILE = "savedToFile",
|
||||
/**@const @type {!string}*/
|
||||
EVENT_HASLOCALUNSYNCEDOPERATIONSCHANGED = "hasLocalUnsyncedOperationsChanged",
|
||||
/**@const @type {!string}*/
|
||||
EVENT_HASSESSIONHOSTCONNECTIONCHANGED = "hasSessionHostConnectionChanged";
|
||||
|
||||
runtime.loadClass("ops.OperationTransformer");
|
||||
runtime.loadClass("core.EventNotifier");
|
||||
|
||||
/**
|
||||
* route operations in a networked collaborative manner.
|
||||
@ -43,11 +54,11 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
|
||||
* @constructor
|
||||
* @implements ops.OperationRouter
|
||||
*/
|
||||
return function PullBoxOperationRouter(sessionId, memberId, server, odfContainer) {
|
||||
return function PullBoxOperationRouter(sessionId, memberId, server, odfContainer, errorCallback) {
|
||||
"use strict";
|
||||
|
||||
var operationFactory,
|
||||
/**@type{function(!ops.Operation)}*/
|
||||
/**@type{function(!ops.Operation):boolean}*/
|
||||
playbackFunction,
|
||||
idleTimeout = null,
|
||||
syncOpsTimeout = null,
|
||||
@ -58,7 +69,7 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
|
||||
/**@type{!boolean}*/
|
||||
isSyncCallRunning = false,
|
||||
/**@type{!boolean}*/
|
||||
hasUnresolvableConflict = false,
|
||||
hasError = false,
|
||||
/**@type{!boolean}*/
|
||||
syncingBlocked = false,
|
||||
/** @type {!string} id of latest op stack state known on the server */
|
||||
@ -71,8 +82,20 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
|
||||
unplayedServerOpspecQueue = [],
|
||||
/** @type {!Array.<!Function>} sync request callbacks which should be called after the received ops have been applied server */
|
||||
uncalledSyncRequestCallbacksQueue = [],
|
||||
/** @type {!Array.<!function(!boolean):undefined>} ops created since the last sync call to the server */
|
||||
hasLocalUnsyncedOpsStateSubscribers = [],
|
||||
/**@type{!boolean}*/
|
||||
hasLocalUnsyncedOps = false,
|
||||
/** @type {!Array.<!function(!boolean):undefined>} */
|
||||
hasSessionHostConnectionStateSubscribers = [],
|
||||
/**@type{!boolean}*/
|
||||
hasSessionHostConnection = true,
|
||||
eventNotifier = new core.EventNotifier([
|
||||
EVENT_BEFORESAVETOFILE,
|
||||
EVENT_SAVEDTOFILE,
|
||||
EVENT_HASLOCALUNSYNCEDOPERATIONSCHANGED,
|
||||
EVENT_HASSESSIONHOSTCONNECTIONCHANGED
|
||||
]),
|
||||
/**@type{!boolean} tells if any local ops have been modifying ops */
|
||||
hasPushedModificationOps = false,
|
||||
operationTransformer = new ops.OperationTransformer(),
|
||||
@ -84,7 +107,8 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
|
||||
* @return {undefined}
|
||||
*/
|
||||
function updateHasLocalUnsyncedOpsState() {
|
||||
var hasLocalUnsyncedOpsNow = (unsyncedClientOpspecQueue.length > 0);
|
||||
var i,
|
||||
hasLocalUnsyncedOpsNow = (unsyncedClientOpspecQueue.length > 0);
|
||||
|
||||
// no change?
|
||||
if (hasLocalUnsyncedOps === hasLocalUnsyncedOpsNow) {
|
||||
@ -92,6 +116,23 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
|
||||
}
|
||||
|
||||
hasLocalUnsyncedOps = hasLocalUnsyncedOpsNow;
|
||||
eventNotifier.emit(EVENT_HASLOCALUNSYNCEDOPERATIONSCHANGED, hasLocalUnsyncedOps);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!boolean} hasConnection
|
||||
* @return {undefined}
|
||||
*/
|
||||
function updateHasSessionHostConnectionState(hasConnection) {
|
||||
var i;
|
||||
|
||||
// no change?
|
||||
if (hasSessionHostConnection === hasConnection) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasSessionHostConnection = hasConnection;
|
||||
eventNotifier.emit(EVENT_HASSESSIONHOSTCONNECTIONCHANGED, hasSessionHostConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,9 +163,16 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
|
||||
op = operationFactory.create(opspec);
|
||||
runtime.log(" op in: "+runtime.toJson(opspec));
|
||||
if (op !== null) {
|
||||
playbackFunction(op);
|
||||
if (!playbackFunction(op)) {
|
||||
hasError = true;
|
||||
errorCallback("opExecutionFailure");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
hasError = true;
|
||||
runtime.log("ignoring invalid incoming opspec: " + opspec);
|
||||
errorCallback("unknownOpReceived");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,7 +262,7 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
|
||||
}, syncOpsDelay);
|
||||
}
|
||||
|
||||
if (isSyncCallRunning || hasUnresolvableConflict) {
|
||||
if (isSyncCallRunning || hasError) {
|
||||
return;
|
||||
}
|
||||
// TODO: hack, remove
|
||||
@ -243,13 +291,25 @@ runtime.log("OperationRouter: sending sync_ops call");
|
||||
client_ops: syncedClientOpspecs
|
||||
}
|
||||
}, function(responseData) {
|
||||
var response = /** @type{{result:string, head_seq:string, ops:Array.<!Object>}} */(runtime.fromJson(responseData));
|
||||
var response,
|
||||
/**@type{!boolean}*/
|
||||
hasUnresolvableConflict = false;
|
||||
|
||||
updateHasSessionHostConnectionState(true);
|
||||
|
||||
// TODO: hack, remove
|
||||
if (syncingBlocked) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
response = /** @type{{result:string, head_seq:string, ops:Array.<!Object>}} */(runtime.fromJson(responseData));
|
||||
} catch (e) {
|
||||
hasError = true;
|
||||
runtime.log("Could not parse reply: "+responseData);
|
||||
errorCallback("unknownServerReply");
|
||||
return;
|
||||
}
|
||||
// TODO: hack, remove
|
||||
runtime.log("sync_ops reply: " + responseData);
|
||||
|
||||
// just new ops?
|
||||
@ -290,20 +350,28 @@ runtime.log("OperationRouter: sending sync_ops call");
|
||||
if (!hasUnresolvableConflict) {
|
||||
isInstantSyncRequested = true;
|
||||
}
|
||||
} else if (response.result === "error") {
|
||||
runtime.log("server reports an error: "+response.error);
|
||||
hasError = true;
|
||||
errorCallback(
|
||||
response.error === "ENOSESSION" ? "sessionDoesNotExist":
|
||||
response.error === "ENOMEMBER" ? "notMemberOfSession":
|
||||
"unknownServerReply"
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
runtime.assert(false, "Unexpected result on sync-ops call: "+response.result);
|
||||
hasError = true;
|
||||
runtime.log("Unexpected result on sync-ops call: "+response.result);
|
||||
errorCallback("unknownServerReply");
|
||||
return;
|
||||
}
|
||||
|
||||
// unlock
|
||||
isSyncCallRunning = false;
|
||||
|
||||
if (hasUnresolvableConflict) {
|
||||
// TODO: offer option to reload session automatically?
|
||||
runtime.assert(false,
|
||||
"Sorry to tell:\n" +
|
||||
"we hit a pair of operations in a state which yet need to be supported for transformation against each other.\n" +
|
||||
"Client disconnected from session, no further editing accepted.\n\n" +
|
||||
"Please reconnect manually for now.");
|
||||
hasError = true;
|
||||
errorCallback("unresolvableConflictingOps");
|
||||
} else {
|
||||
// prepare next sync
|
||||
if (isInstantSyncRequested) {
|
||||
@ -318,6 +386,22 @@ runtime.log("OperationRouter: sending sync_ops call");
|
||||
}
|
||||
playUnplayedServerOpSpecs();
|
||||
}
|
||||
}, function() {
|
||||
runtime.log("meh, server cannot be reached ATM.");
|
||||
// signal connection problem, but do not give up for now
|
||||
updateHasSessionHostConnectionState(false);
|
||||
// put the (not) send ops back into the outgoing queue
|
||||
unsyncedClientOpspecQueue = syncedClientOpspecs.concat(unsyncedClientOpspecQueue);
|
||||
syncRequestCallbacksQueue = syncRequestCallbacksArray.concat(syncRequestCallbacksQueue);
|
||||
// unlock
|
||||
isSyncCallRunning = false;
|
||||
// nothing on client to sync?
|
||||
if (unsyncedClientOpspecQueue.length === 0) {
|
||||
idleTimeout = runtime.getWindow().setTimeout(startSyncOpsTimeout, idleDelay);
|
||||
} else {
|
||||
startSyncOpsTimeout();
|
||||
}
|
||||
playUnplayedServerOpSpecs();
|
||||
});
|
||||
}
|
||||
|
||||
@ -381,7 +465,7 @@ runtime.log("OperationRouter: instant opsSync requested");
|
||||
/**
|
||||
* Sets the method which should be called to apply operations.
|
||||
*
|
||||
* @param {!function(!ops.Operation)} playback_func
|
||||
* @param {!function(!ops.Operation):boolean} playback_func
|
||||
* @return {undefined}
|
||||
*/
|
||||
this.setPlaybackFunction = function (playback_func) {
|
||||
@ -395,7 +479,10 @@ runtime.log("OperationRouter: instant opsSync requested");
|
||||
* @return {undefined}
|
||||
*/
|
||||
this.push = function (operations) {
|
||||
if (hasUnresolvableConflict) {
|
||||
var i, op, opspec,
|
||||
timestamp = (new Date()).getTime();
|
||||
|
||||
if (hasError) {
|
||||
return;
|
||||
}
|
||||
// TODO: should be an assert in the future
|
||||
@ -406,22 +493,27 @@ runtime.log("OperationRouter: instant opsSync requested");
|
||||
return;
|
||||
}
|
||||
|
||||
operations.forEach(function(op) {
|
||||
var timedOp,
|
||||
for (i = 0; i < operations.length; i += 1) {
|
||||
op = operations[i];
|
||||
opspec = op.spec();
|
||||
|
||||
// note if any local ops modified
|
||||
hasPushedModificationOps = hasPushedModificationOps || op.isEdit;
|
||||
|
||||
// apply locally
|
||||
opspec.timestamp = (new Date()).getTime();
|
||||
timedOp = operationFactory.create(opspec);
|
||||
// add timestamp TODO: improve the useless recreation of the op
|
||||
opspec.timestamp = timestamp;
|
||||
op = operationFactory.create(opspec);
|
||||
|
||||
playbackFunction(timedOp);
|
||||
// apply locally
|
||||
if (!playbackFunction(op)) {
|
||||
hasError = true;
|
||||
errorCallback("opExecutionFailure");
|
||||
return;
|
||||
}
|
||||
|
||||
// send to server
|
||||
unsyncedClientOpspecQueue.push(opspec);
|
||||
});
|
||||
}
|
||||
|
||||
triggerPushingOps();
|
||||
|
||||
@ -434,25 +526,65 @@ runtime.log("OperationRouter: instant opsSync requested");
|
||||
* A callback is called on success.
|
||||
*/
|
||||
this.close = function (cb) {
|
||||
function cbDoneSaving(err) {
|
||||
eventNotifier.emit(EVENT_SAVEDTOFILE, null);
|
||||
cb(err);
|
||||
}
|
||||
|
||||
function cbSuccess(fileData) {
|
||||
server.writeSessionStateToFile(sessionId, memberId, lastServerSeq, fileData, cb);
|
||||
server.writeSessionStateToFile(sessionId, memberId, lastServerSeq, fileData, cbDoneSaving);
|
||||
}
|
||||
|
||||
function doClose() {
|
||||
syncingBlocked = true;
|
||||
if (hasPushedModificationOps) {
|
||||
odfContainer.createByteArray(cbSuccess, cb);
|
||||
eventNotifier.emit(EVENT_BEFORESAVETOFILE, null);
|
||||
|
||||
odfContainer.createByteArray(cbSuccess, cbDoneSaving);
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
if (hasLocalUnsyncedOps) {
|
||||
if (hasError) {
|
||||
cb();
|
||||
} else if (hasLocalUnsyncedOps) {
|
||||
requestInstantOpsSync(doClose);
|
||||
} else {
|
||||
doClose();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} eventId
|
||||
* @param {!Function} cb
|
||||
* @return {undefined}
|
||||
*/
|
||||
this.subscribe = function (eventId, cb) {
|
||||
eventNotifier.subscribe(eventId, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} eventId
|
||||
* @param {!Function} cb
|
||||
* @return {undefined}
|
||||
*/
|
||||
this.unsubscribe = function (eventId, cb) {
|
||||
eventNotifier.unsubscribe(eventId, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {!boolean}
|
||||
*/
|
||||
this.hasLocalUnsyncedOps = function () {
|
||||
return hasLocalUnsyncedOps;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {!boolean}
|
||||
*/
|
||||
this.hasSessionHostConnection = function () {
|
||||
return hasSessionHostConnection;
|
||||
};
|
||||
};
|
||||
});
|
||||
|
@ -40,6 +40,7 @@ define("webodf/editor/server/pullbox/Server", [], function () {
|
||||
|
||||
var self = this,
|
||||
token,
|
||||
/**@const*/serverCallTimeout = 10000,
|
||||
base64 = new core.Base64();
|
||||
|
||||
args = args || {};
|
||||
@ -53,22 +54,33 @@ define("webodf/editor/server/pullbox/Server", [], function () {
|
||||
/**
|
||||
* @param {!Object} message
|
||||
* @param {!function(!string)} cb
|
||||
* @param {!function(!number,!string)} cbError passes the status number
|
||||
* and the statustext, or -1 if there was an exception on sending
|
||||
* @return {undefined}
|
||||
*/
|
||||
function call(message, cb) {
|
||||
function call(message, cb, cbError) {
|
||||
var xhr = new XMLHttpRequest(),
|
||||
messageString = JSON.stringify(message);
|
||||
|
||||
function handleResult() {
|
||||
if (xhr.readyState === 4) {
|
||||
if ((xhr.status < 200 || xhr.status >= 300) && xhr.status === 0) {
|
||||
if (xhr.status < 200 || xhr.status >= 300) {
|
||||
// report error
|
||||
runtime.log("Status " + String(xhr.status) + ": " +
|
||||
xhr.responseText || xhr.statusText);
|
||||
}
|
||||
cbError(xhr.status, xhr.statusText);
|
||||
} else {
|
||||
runtime.log("Status " + String(xhr.status) + ": " +
|
||||
xhr.responseText || xhr.statusText);
|
||||
cb(xhr.responseText);
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleTimeout() {
|
||||
runtime.log("Timeout on call to server.");
|
||||
cbError(0, xhr.statusText);
|
||||
}
|
||||
|
||||
runtime.log("Sending message to server: "+messageString);
|
||||
// create body data for request from metadata and payload
|
||||
|
||||
@ -78,11 +90,14 @@ runtime.log("Sending message to server: "+messageString);
|
||||
xhr.setRequestHeader("requesttoken", token);
|
||||
}
|
||||
xhr.onreadystatechange = handleResult;
|
||||
xhr.timeout = serverCallTimeout;
|
||||
// TODO: seems handleResult is called on timeout as well, with xhr.status === 0
|
||||
// xhr.ontimeout = handleTimeout;
|
||||
try {
|
||||
xhr.send(messageString);
|
||||
} catch (e) {
|
||||
runtime.log("Problem with calling server: " + e + " " + data);
|
||||
cb(e.message);
|
||||
cbError(-1, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,14 +166,14 @@ runtime.log("Sending message to server: "+messageString);
|
||||
} else {
|
||||
failCb(responseData);
|
||||
}
|
||||
});
|
||||
}, failCb);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} userId
|
||||
* @param {!string} sessionId
|
||||
* @param {!function(!string)} successCb
|
||||
* @param {function()=} failCb
|
||||
* @param {!function()} failCb
|
||||
* @return {undefined}
|
||||
*/
|
||||
this.joinSession = function (userId, sessionId, successCb, failCb) {
|
||||
@ -175,18 +190,16 @@ runtime.log("Sending message to server: "+messageString);
|
||||
if (response.hasOwnProperty("success") && response.success) {
|
||||
successCb(response.member_id);
|
||||
} else {
|
||||
if (failCb) {
|
||||
failCb();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, failCb);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} sessionId
|
||||
* @param {!string} memberId
|
||||
* @param {!function()} successCb
|
||||
* @param {function()=} failCb
|
||||
* @param {!function()} failCb
|
||||
* @return {undefined}
|
||||
*/
|
||||
this.leaveSession = function (sessionId, memberId, successCb, failCb) {
|
||||
@ -203,18 +216,16 @@ runtime.log("Sending message to server: "+messageString);
|
||||
if (response.hasOwnProperty("success") && response.success) {
|
||||
successCb();
|
||||
} else {
|
||||
if (failCb) {
|
||||
failCb();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, failCb);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} sessionId
|
||||
* @param {!string} memberId
|
||||
* @param {!string} seqHead
|
||||
* @param {function()=} callback
|
||||
* @param {!function(!Object=)} callback
|
||||
* @return {undefined}
|
||||
*/
|
||||
this.writeSessionStateToFile = function(sessionId, memberId, seqHead, fileData, callback) {
|
||||
|
@ -40,8 +40,8 @@ define("webodf/editor/server/pullbox/ServerFactory", [
|
||||
this.createServer = function (args) {
|
||||
return new PullBoxServer(args);
|
||||
};
|
||||
this.createOperationRouter = function (sid, mid, server, odfContainer) {
|
||||
return new PullBoxOperationRouter(sid, mid, server, odfContainer);
|
||||
this.createOperationRouter = function (sid, mid, server, odfContainer, errorCallback) {
|
||||
return new PullBoxOperationRouter(sid, mid, server, odfContainer, errorCallback);
|
||||
};
|
||||
this.createSessionList = function (server) {
|
||||
return new PullBoxSessionList(server);
|
||||
|
@ -111,6 +111,8 @@ define("webodf/editor/server/pullbox/SessionList", [], function () {
|
||||
} else {
|
||||
runtime.log("Meh, sessionlist data broken: " + responseData);
|
||||
}
|
||||
}, function() {
|
||||
// ignore error for now
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ define("webodf/editor/widgets/paragraphStyles",
|
||||
if (value === "") {
|
||||
value = defaultStyleUIId;
|
||||
}
|
||||
select.set('value', value);
|
||||
select.set('value', value, false);
|
||||
};
|
||||
|
||||
// events
|
||||
|
24261
js/3rdparty/webodf/webodf-debug.js
vendored
24261
js/3rdparty/webodf/webodf-debug.js
vendored
File diff suppressed because one or more lines are too long
4013
js/3rdparty/webodf/webodf.js
vendored
4013
js/3rdparty/webodf/webodf.js
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user