From e3adf6bd19355f30b1c876a41c101e551bac2a9a Mon Sep 17 00:00:00 2001 From: "Friedrich W. H. Kossebau" Date: Tue, 3 Dec 2013 20:34:48 +0100 Subject: [PATCH] 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 --- js/3rdparty/webodf/editor/Editor.js | 90 +- js/3rdparty/webodf/editor/EditorSession.js | 52 +- .../webodf/editor/server/ServerFactory.js | 3 +- .../editor/server/owncloud/ServerFactory.js | 4 +- .../editor/server/pullbox/OperationRouter.js | 188 +- .../webodf/editor/server/pullbox/Server.js | 43 +- .../editor/server/pullbox/ServerFactory.js | 4 +- .../editor/server/pullbox/SessionList.js | 2 + .../webodf/editor/widgets/paragraphStyles.js | 2 +- js/3rdparty/webodf/webodf-debug.js | 27831 ++++++++-------- js/3rdparty/webodf/webodf.js | 1849 +- 11 files changed, 15231 insertions(+), 14837 deletions(-) diff --git a/js/3rdparty/webodf/editor/Editor.js b/js/3rdparty/webodf/editor/Editor.js index e05638b6..86840e2f 100644 --- a/js/3rdparty/webodf/editor/Editor.js +++ b/js/3rdparty/webodf/editor/Editor.js @@ -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; }); diff --git a/js/3rdparty/webodf/editor/EditorSession.js b/js/3rdparty/webodf/editor/EditorSession.js index b96f4a7e..91d28b43 100644 --- a/js/3rdparty/webodf/editor/EditorSession.js +++ b/js/3rdparty/webodf/editor/EditorSession.js @@ -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({ - memberid: localMemberId, - position: self.getCursorPosition(), - styleName: value - }); - session.enqueue([op]); + /** + * 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, + styleName: styleName, + position: paragraphStartPoint + }); + opQueue.push(opSetParagraphStyle); + } + }); + + if (opQueue.length > 0) { + session.enqueue(opQueue); } }; diff --git a/js/3rdparty/webodf/editor/server/ServerFactory.js b/js/3rdparty/webodf/editor/server/ServerFactory.js index c028c426..23680445 100644 --- a/js/3rdparty/webodf/editor/server/ServerFactory.js +++ b/js/3rdparty/webodf/editor/server/ServerFactory.js @@ -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 diff --git a/js/3rdparty/webodf/editor/server/owncloud/ServerFactory.js b/js/3rdparty/webodf/editor/server/owncloud/ServerFactory.js index 7c4e1067..0d6b2b43 100644 --- a/js/3rdparty/webodf/editor/server/owncloud/ServerFactory.js +++ b/js/3rdparty/webodf/editor/server/owncloud/ServerFactory.js @@ -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); diff --git a/js/3rdparty/webodf/editor/server/pullbox/OperationRouter.js b/js/3rdparty/webodf/editor/server/pullbox/OperationRouter.js index a24744a5..a6cf8d61 100644 --- a/js/3rdparty/webodf/editor/server/pullbox/OperationRouter.js +++ b/js/3rdparty/webodf/editor/server/pullbox/OperationRouter.js @@ -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.} sync request callbacks which should be called after the received ops have been applied server */ uncalledSyncRequestCallbacksQueue = [], + /** @type {!Array.} ops created since the last sync call to the server */ + hasLocalUnsyncedOpsStateSubscribers = [], /**@type{!boolean}*/ hasLocalUnsyncedOps = false, + /** @type {!Array.} */ + 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.}} */(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.}} */(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, - opspec = op.spec(); + 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; + }; }; }); diff --git a/js/3rdparty/webodf/editor/server/pullbox/Server.js b/js/3rdparty/webodf/editor/server/pullbox/Server.js index ee17cee9..9b749fdc 100644 --- a/js/3rdparty/webodf/editor/server/pullbox/Server.js +++ b/js/3rdparty/webodf/editor/server/pullbox/Server.js @@ -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); } - 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(); } - }); + }, 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(); } - }); + }, 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) { diff --git a/js/3rdparty/webodf/editor/server/pullbox/ServerFactory.js b/js/3rdparty/webodf/editor/server/pullbox/ServerFactory.js index b2b5478c..626b5ed8 100644 --- a/js/3rdparty/webodf/editor/server/pullbox/ServerFactory.js +++ b/js/3rdparty/webodf/editor/server/pullbox/ServerFactory.js @@ -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); diff --git a/js/3rdparty/webodf/editor/server/pullbox/SessionList.js b/js/3rdparty/webodf/editor/server/pullbox/SessionList.js index 988da2eb..68b96169 100644 --- a/js/3rdparty/webodf/editor/server/pullbox/SessionList.js +++ b/js/3rdparty/webodf/editor/server/pullbox/SessionList.js @@ -111,6 +111,8 @@ define("webodf/editor/server/pullbox/SessionList", [], function () { } else { runtime.log("Meh, sessionlist data broken: " + responseData); } + }, function() { + // ignore error for now }); } diff --git a/js/3rdparty/webodf/editor/widgets/paragraphStyles.js b/js/3rdparty/webodf/editor/widgets/paragraphStyles.js index e7a93640..57acaacb 100644 --- a/js/3rdparty/webodf/editor/widgets/paragraphStyles.js +++ b/js/3rdparty/webodf/editor/widgets/paragraphStyles.js @@ -75,7 +75,7 @@ define("webodf/editor/widgets/paragraphStyles", if (value === "") { value = defaultStyleUIId; } - select.set('value', value); + select.set('value', value, false); }; // events diff --git a/js/3rdparty/webodf/webodf-debug.js b/js/3rdparty/webodf/webodf-debug.js index 52a5ea3d..07813529 100644 --- a/js/3rdparty/webodf/webodf-debug.js +++ b/js/3rdparty/webodf/webodf-debug.js @@ -1,46 +1,4 @@ -/* - - - Copyright (C) 2012 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -var core = {}; -var gui = {}; -var xmldom = {}; -var odf = {}; -var ops = {}; +var webodf_version = "0.4.2-1556-gf8a94ee"; function Runtime() { } Runtime.prototype.getVariable = function(name) { @@ -77,17 +35,23 @@ Runtime.prototype.clearTimeout = function(timeoutID) { }; Runtime.prototype.libraryPaths = function() { }; +Runtime.prototype.currentDirectory = function() { +}; +Runtime.prototype.setCurrentDirectory = function(dir) { +}; Runtime.prototype.type = function() { }; Runtime.prototype.getDOMImplementation = function() { }; Runtime.prototype.parseXML = function(xml) { }; +Runtime.prototype.exit = function(exitCode) { +}; Runtime.prototype.getWindow = function() { }; Runtime.prototype.assert = function(condition, message, callback) { }; -var IS_COMPILED_CODE = false; +var IS_COMPILED_CODE = true; Runtime.byteArrayToString = function(bytearray, encoding) { function byteArrayToString(bytearray) { var s = "", i, l = bytearray.length; @@ -486,6 +450,9 @@ function BrowserRuntime(logoutput) { }; this.setCurrentDirectory = function() { }; + this.currentDirectory = function() { + return"" + }; this.type = function() { return"BrowserRuntime" }; @@ -592,7 +559,7 @@ function NodeJSRuntime() { }) }; this.readFileSync = function(path, encoding) { - var enc = encoding === "binary" ? null : encoding, r = fs.readFileSync(path, enc), s; + var s, enc = encoding === "binary" ? null : encoding, r = fs.readFileSync(path, enc); if(r === null) { throw"File " + path + " could not be read."; } @@ -678,7 +645,7 @@ function NodeJSRuntime() { init() } function RhinoRuntime() { - var self = this, dom = Packages.javax.xml.parsers.DocumentBuilderFactory.newInstance(), builder, entityresolver, currentDirectory = ""; + var self = this, Packages = {}, dom = Packages.javax.xml.parsers.DocumentBuilderFactory.newInstance(), builder, entityresolver, currentDirectory = ""; dom.setValidating(false); dom.setNamespaceAware(true); dom.setExpandEntityReferences(false); @@ -695,7 +662,7 @@ function RhinoRuntime() { builder = dom.newDocumentBuilder(); builder.setEntityResolver(entityresolver); this.byteArrayFromString = function(string, encoding) { - var a = [], i, l = string.length; + var i, l = string.length, a = new Uint8Array(new ArrayBuffer(l)); for(i = 0;i < l;i += 1) { a[i] = string.charCodeAt(i) & 255 } @@ -706,13 +673,12 @@ function RhinoRuntime() { this.fromJson = Runtime.fromJson; this.toJson = Runtime.toJson; function loadXML(path, callback) { - var file = new Packages.java.io.File(path), xmlDocument; + var file = new Packages.java.io.File(path), xmlDocument = null; try { xmlDocument = builder.parse(file) }catch(err) { print(err); - callback(err); - return + return callback(err, null) } callback(null, xmlDocument) } @@ -722,10 +688,10 @@ function RhinoRuntime() { } var file = new Packages.java.io.File(path), data, rhinoencoding = encoding === "binary" ? "latin1" : encoding; if(!file.isFile()) { - callback(path + " is not a file.") + callback(path + " is not a file.", null) }else { data = readFile(path, rhinoencoding); - if(encoding === "binary") { + if(data && encoding === "binary") { data = self.byteArrayFromString(data, "binary") } callback(null, data) @@ -765,8 +731,9 @@ function RhinoRuntime() { if(currentDirectory) { path = currentDirectory + "/" + path } - var file = new Packages.java.io.File(path); - if(file["delete"]()) { + var file = new Packages.java.io.File(path), otherPath = path + Math.random(), other = new Packages.java.io.File(otherPath); + if(file.rename(other)) { + other.deleteOnExit(); callback(null) }else { callback("Could not delete " + path) @@ -780,7 +747,7 @@ function RhinoRuntime() { if(data) { callback(null, this.byteArrayFromString(data.substring(offset, offset + length), "binary")) }else { - callback("Cannot read " + path) + callback("Cannot read " + path, null) } }; this.readFileSync = function(path, encoding) { @@ -848,14 +815,15 @@ function RhinoRuntime() { return builder.getDOMImplementation() }; this.parseXML = function(xml) { - return builder.parse(xml) + var reader = new Packages.java.io.StringReader(xml), source = new Packages.org.xml.sax.InputSource(reader); + return builder.parse(source) }; this.exit = quit; this.getWindow = function() { return null } } -var runtime = function() { +Runtime.create = function create() { var result; if(String(typeof window) !== "undefined") { result = new BrowserRuntime(window.document.getElementById("logoutput")) @@ -867,98 +835,175 @@ var runtime = function() { } } return result -}(); +}; +var runtime = Runtime.create(); +var core = {}; +var gui = {}; +var xmldom = {}; +var odf = {}; +var ops = {}; (function() { - var cache = {}, dircontents = {}; - function getOrDefinePackage(packageNameComponents) { - var topname = packageNameComponents[0], i, pkg; - pkg = eval("if (typeof " + topname + " === 'undefined') {" + "eval('" + topname + " = {};');}" + topname); - for(i = 1;i < packageNameComponents.length - 1;i += 1) { - if(!pkg.hasOwnProperty(packageNameComponents[i])) { - pkg = pkg[packageNameComponents[i]] = {} - }else { - pkg = pkg[packageNameComponents[i]] - } - } - return pkg[packageNameComponents[packageNameComponents.length - 1]] - } - runtime.loadClass = function(classpath) { - if(IS_COMPILED_CODE) { + var dependencies = {}, loadedFiles = {}; + function loadManifest(dir, manifests) { + var path = dir + "/manifest.json", content, list, manifest, m; + if(loadedFiles.hasOwnProperty(path)) { return } - if(cache.hasOwnProperty(classpath)) { + loadedFiles[path] = 1; + try { + content = runtime.readFileSync(path, "utf-8") + }catch(e) { + console.log(String(e)); return } - var names = classpath.split("."), impl; - impl = getOrDefinePackage(names); - if(impl) { - cache[classpath] = true; - return + list = JSON.parse((content)); + manifest = (list); + for(m in manifest) { + if(manifest.hasOwnProperty(m)) { + manifests[m] = {dir:dir, deps:manifest[m]} + } } - function getPathFromManifests(classpath) { - var path = classpath.replace(/\./g, "/") + ".js", dirs = runtime.libraryPaths(), i, dir, code, codestr; - if(runtime.currentDirectory) { - dirs.push(runtime.currentDirectory()) + } + function expandPathDependencies(path, manifests, allDeps) { + var d = manifests[path].deps, deps = {}; + allDeps[path] = deps; + d.forEach(function(dp) { + deps[dp] = 1 + }); + d.forEach(function(dp) { + if(!allDeps[dp]) { + expandPathDependencies(dp, manifests, allDeps) } - for(i = 0;i < dirs.length;i += 1) { - dir = dirs[i]; - if(!dircontents.hasOwnProperty(dir)) { - try { - code = runtime.readFileSync(dirs[i] + "/manifest.js", "utf8"); - if(code && code.length) { - codestr = (code); - dircontents[dir] = eval(codestr) - }else { - dircontents[dir] = null - } - }catch(e1) { - dircontents[dir] = null; - runtime.log("Cannot load manifest for " + dir + ".") + }); + d.forEach(function(dp) { + Object.keys(allDeps[dp]).forEach(function(k) { + deps[k] = 1 + }) + }) + } + function sortDeps(deps, allDeps) { + var i, sorted = []; + function add(path, stack) { + var j, d = allDeps[path]; + if(sorted.indexOf(path) === -1 && stack.indexOf(path) === -1) { + stack.push(path); + for(j = 0;j < deps.length;j += 1) { + if(d[deps[j]]) { + add(deps[j], stack) } } - code = null; - dir = dircontents[dir]; - if(dir && (dir.indexOf && dir.indexOf(path) !== -1)) { - return dirs[i] + "/" + path - } + stack.pop(); + sorted.push(path) } - return null } - function load(classpath) { - var code, path; - path = getPathFromManifests(classpath); - if(!path) { - throw classpath + " is not listed in any manifest.js."; + for(i = 0;i < deps.length;i += 1) { + add(deps[i], []) + } + return sorted + } + function expandDependencies(manifests) { + var path, deps, allDeps = {}; + for(path in manifests) { + if(manifests.hasOwnProperty(path)) { + expandPathDependencies(path, manifests, allDeps) } - try { - code = runtime.readFileSync(path, "utf8") - }catch(e2) { - runtime.log("Error loading " + classpath + " " + e2); - throw e2; + } + for(path in manifests) { + if(manifests.hasOwnProperty(path)) { + deps = (Object.keys(allDeps[path])); + manifests[path].deps = sortDeps(deps, allDeps); + manifests[path].deps.push(path) + } + } + dependencies = manifests + } + function loadManifests() { + if(Object.keys(dependencies).length > 0) { + return + } + var paths = runtime.libraryPaths(), manifests = {}, i; + if(runtime.currentDirectory()) { + loadManifest(runtime.currentDirectory(), manifests) + } + for(i = 0;i < paths.length;i += 1) { + loadManifest(paths[i], manifests) + } + expandDependencies(manifests) + } + function classPath(classname) { + return classname.replace(".", "/") + ".js" + } + function getDependencies(classname) { + var classpath = classPath(classname), deps = [], d = dependencies[classpath].deps, i; + for(i = 0;i < d.length;i += 1) { + if(!loadedFiles.hasOwnProperty(d[i])) { + deps.push(d[i]) } - if(code === undefined) { - throw"Cannot load class " + classpath; + } + return deps + } + function evalArray(paths, contents) { + var i = 0; + while(i < paths.length && contents[i] !== undefined) { + if(contents[i] !== null) { + eval((contents[i])); + contents[i] = null } - code += "\n//# sourceURL=" + path; - code += "\n//@ sourceURL=" + path; - try { - code = eval(classpath + " = eval(code);") - }catch(e4) { - runtime.log("Error loading " + classpath + " " + e4); - throw e4; + i += 1 + } + } + function loadFiles(paths) { + var contents = [], i, p, c, async = false; + contents.length = paths.length; + function addContent(pos, path, content) { + content += "\n//# sourceURL=" + path; + content += "\n//@ sourceURL=" + path; + contents[pos] = content + } + function loadFile(pos) { + var path = dependencies[paths[pos]].dir + "/" + paths[pos]; + runtime.readFile(path, "utf8", function(err, content) { + if(err) { + throw err; + } + if(contents[pos] === undefined) { + addContent(pos, path, (content)) + } + }) + } + if(async) { + for(i = 0;i < paths.length;i += 1) { + loadedFiles[paths[i]] = 1; + loadFile(i) + } + } + for(i = paths.length - 1;i >= 0;i -= 1) { + loadedFiles[paths[i]] = 1; + if(contents[i] === undefined) { + p = paths[i]; + p = dependencies[p].dir + "/" + p; + c = runtime.readFileSync(p, "utf-8"); + addContent(i, p, (c)) } - return code } - impl = load(classpath); - if(!impl || Runtime.getFunctionName(impl) !== names[names.length - 1]) { - runtime.log("Loaded code is not for " + names[names.length - 1]); - throw"Loaded code is not for " + names[names.length - 1]; + evalArray(paths, contents) + } + runtime.loadClass = function(classname) { + if(IS_COMPILED_CODE) { + return + } + var classpath = classPath(classname), paths; + if(loadedFiles.hasOwnProperty(classpath)) { + return } - cache[classpath] = true + loadManifests(); + paths = getDependencies(classname); + loadFiles(paths) } })(); (function() { - var translator = function() { + var translator = function(string) { + return string }; function tr(original) { var result = translator(original); @@ -994,7 +1039,7 @@ var runtime = function() { runtime.setCurrentDirectory(path); function inner_run() { var script, path, args, argv, result; - result = eval(codestring); + result = (eval(codestring)); if(result) { runtime.exit(result) } @@ -1023,6 +1068,43 @@ var runtime = function() { } } })(String(typeof arguments) !== "undefined" && arguments); +core.Async = function Async() { + this.forEach = function(items, f, callback) { + var i, l = items.length, itemsDone = 0; + function end(err) { + if(itemsDone !== l) { + if(err) { + itemsDone = l; + callback(err) + }else { + itemsDone += 1; + if(itemsDone === l) { + callback(null) + } + } + } + } + for(i = 0;i < l;i += 1) { + f(items[i], end) + } + }; + this.destroyAll = function(items, callback) { + function destroy(itemIndex, err) { + if(err) { + callback(err) + }else { + if(itemIndex < items.length) { + items[itemIndex](function(err) { + destroy(itemIndex + 1, err) + }) + }else { + callback() + } + } + } + destroy(0, undefined) + } +}; function makeBase64() { function makeB64tab(bin) { var t = {}, i, l; @@ -1251,1235 +1333,953 @@ function makeBase64() { return core.Base64 } core.Base64 = makeBase64(); -core.RawDeflate = function() { - var zip_WSIZE = 32768, zip_STORED_BLOCK = 0, zip_STATIC_TREES = 1, zip_DYN_TREES = 2, zip_DEFAULT_LEVEL = 6, zip_FULL_SEARCH = true, zip_INBUFSIZ = 32768, zip_INBUF_EXTRA = 64, zip_OUTBUFSIZ = 1024 * 8, zip_window_size = 2 * zip_WSIZE, zip_MIN_MATCH = 3, zip_MAX_MATCH = 258, zip_BITS = 16, zip_LIT_BUFSIZE = 8192, zip_HASH_BITS = 13, zip_DIST_BUFSIZE = zip_LIT_BUFSIZE, zip_HASH_SIZE = 1 << zip_HASH_BITS, zip_HASH_MASK = zip_HASH_SIZE - 1, zip_WMASK = zip_WSIZE - 1, zip_NIL = 0, zip_TOO_FAR = 4096, - zip_MIN_LOOKAHEAD = zip_MAX_MATCH + zip_MIN_MATCH + 1, zip_MAX_DIST = zip_WSIZE - zip_MIN_LOOKAHEAD, zip_SMALLEST = 1, zip_MAX_BITS = 15, zip_MAX_BL_BITS = 7, zip_LENGTH_CODES = 29, zip_LITERALS = 256, zip_END_BLOCK = 256, zip_L_CODES = zip_LITERALS + 1 + zip_LENGTH_CODES, zip_D_CODES = 30, zip_BL_CODES = 19, zip_REP_3_6 = 16, zip_REPZ_3_10 = 17, zip_REPZ_11_138 = 18, zip_HEAP_SIZE = 2 * zip_L_CODES + 1, zip_H_SHIFT = parseInt((zip_HASH_BITS + zip_MIN_MATCH - 1) / zip_MIN_MATCH, 10), zip_free_queue, - zip_qhead, zip_qtail, zip_initflag, zip_outbuf = null, zip_outcnt, zip_outoff, zip_complete, zip_window, zip_d_buf, zip_l_buf, zip_prev, zip_bi_buf, zip_bi_valid, zip_block_start, zip_ins_h, zip_hash_head, zip_prev_match, zip_match_available, zip_match_length, zip_prev_length, zip_strstart, zip_match_start, zip_eofile, zip_lookahead, zip_max_chain_length, zip_max_lazy_match, zip_compr_level, zip_good_match, zip_nice_match, zip_dyn_ltree, zip_dyn_dtree, zip_static_ltree, zip_static_dtree, zip_bl_tree, - zip_l_desc, zip_d_desc, zip_bl_desc, zip_bl_count, zip_heap, zip_heap_len, zip_heap_max, zip_depth, zip_length_code, zip_dist_code, zip_base_length, zip_base_dist, zip_flag_buf, zip_last_lit, zip_last_dist, zip_last_flags, zip_flags, zip_flag_bit, zip_opt_len, zip_static_len, zip_deflate_data, zip_deflate_pos, zip_extra_lbits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0], zip_extra_dbits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, - 9, 10, 10, 11, 11, 12, 12, 13, 13], zip_extra_blbits = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7], zip_bl_order = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15], zip_configuration_table; - if(zip_LIT_BUFSIZE > zip_INBUFSIZ) { - runtime.log("error: zip_INBUFSIZ is too small") - } - if(zip_WSIZE << 1 > 1 << zip_BITS) { - runtime.log("error: zip_WSIZE is too large") - } - if(zip_HASH_BITS > zip_BITS - 1) { - runtime.log("error: zip_HASH_BITS is too large") - } - if(zip_HASH_BITS < 8 || zip_MAX_MATCH !== 258) { - runtime.log("error: Code too clever") +core.ByteArray = function ByteArray(data) { + this.pos = 0; + this.data = data; + this.readUInt32LE = function() { + this.pos += 4; + var d = this.data, pos = this.pos; + return d[--pos] << 24 | d[--pos] << 16 | d[--pos] << 8 | d[--pos] + }; + this.readUInt16LE = function() { + this.pos += 2; + var d = this.data, pos = this.pos; + return d[--pos] << 8 | d[--pos] } - function Zip_DeflateCT() { - this.fc = 0; - this.dl = 0 +}; +core.ByteArrayWriter = function ByteArrayWriter(encoding) { + var self = this, length = 0, bufferSize = 1024, data = new Uint8Array(new ArrayBuffer(bufferSize)); + function expand(extraLength) { + var newData; + if(extraLength > bufferSize - length) { + bufferSize = Math.max(2 * bufferSize, length + extraLength); + newData = new Uint8Array(new ArrayBuffer(bufferSize)); + newData.set(data); + data = newData + } } - function Zip_DeflateTreeDesc() { - this.dyn_tree = null; - this.static_tree = null; - this.extra_bits = null; - this.extra_base = 0; - this.elems = 0; - this.max_length = 0; - this.max_code = 0 + this.appendByteArrayWriter = function(writer) { + self.appendByteArray(writer.getByteArray()) + }; + this.appendByteArray = function(array) { + var l = array.length; + expand(l); + data.set(array, length); + length += l + }; + this.appendArray = function(array) { + var l = array.length; + expand(l); + data.set(array, length); + length += l + }; + this.appendUInt16LE = function(value) { + self.appendArray([value & 255, value >> 8 & 255]) + }; + this.appendUInt32LE = function(value) { + self.appendArray([value & 255, value >> 8 & 255, value >> 16 & 255, value >> 24 & 255]) + }; + this.appendString = function(string) { + self.appendByteArray(runtime.byteArrayFromString(string, encoding)) + }; + this.getLength = function() { + return length + }; + this.getByteArray = function() { + var a = new Uint8Array(new ArrayBuffer(length)); + a.set(data.subarray(0, length)); + return a } - function Zip_DeflateConfiguration(a, b, c, d) { - this.good_length = a; - this.max_lazy = b; - this.nice_length = c; - this.max_chain = d +}; +core.CSSUnits = function CSSUnits() { + var self = this, sizemap = {"in":1, "cm":2.54, "mm":25.4, "pt":72, "pc":12}; + this.convert = function(value, oldUnit, newUnit) { + return value * sizemap[newUnit] / sizemap[oldUnit] + }; + this.convertMeasure = function(measure, newUnit) { + var value, oldUnit, newMeasure; + if(measure && newUnit) { + value = parseFloat(measure); + oldUnit = measure.replace(value.toString(), ""); + newMeasure = self.convert(value, oldUnit, newUnit).toString() + }else { + newMeasure = "" + } + return newMeasure + }; + this.getUnits = function(measure) { + return measure.substr(measure.length - 2, measure.length) } - function Zip_DeflateBuffer() { - this.next = null; - this.len = 0; - this.ptr = []; - this.ptr.length = zip_OUTBUFSIZ; - this.off = 0 +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +(function() { + var browserQuirks; + function getBrowserQuirks() { + var range, directBoundingRect, rangeBoundingRect, testContainer, testElement, detectedQuirks, window, document; + if(browserQuirks === undefined) { + window = runtime.getWindow(); + document = window && window.document; + browserQuirks = {rangeBCRIgnoresElementBCR:false, unscaledRangeClientRects:false}; + if(document) { + testContainer = document.createElement("div"); + testContainer.style.position = "absolute"; + testContainer.style.left = "-99999px"; + testContainer.style.transform = "scale(2)"; + testContainer.style["-webkit-transform"] = "scale(2)"; + testElement = document.createElement("div"); + testContainer.appendChild(testElement); + document.body.appendChild(testContainer); + range = document.createRange(); + range.selectNode(testElement); + browserQuirks.rangeBCRIgnoresElementBCR = range.getClientRects().length === 0; + testElement.appendChild(document.createTextNode("Rect transform test")); + directBoundingRect = testElement.getBoundingClientRect(); + rangeBoundingRect = range.getBoundingClientRect(); + browserQuirks.unscaledRangeClientRects = Math.abs(directBoundingRect.height - rangeBoundingRect.height) > 2; + range.detach(); + document.body.removeChild(testContainer); + detectedQuirks = Object.keys(browserQuirks).map(function(quirk) { + return quirk + ":" + String(browserQuirks[quirk]) + }).join(", "); + runtime.log("Detected browser quirks - " + detectedQuirks) + } + } + return browserQuirks } - zip_configuration_table = [new Zip_DeflateConfiguration(0, 0, 0, 0), new Zip_DeflateConfiguration(4, 4, 8, 4), new Zip_DeflateConfiguration(4, 5, 16, 8), new Zip_DeflateConfiguration(4, 6, 32, 32), new Zip_DeflateConfiguration(4, 4, 16, 16), new Zip_DeflateConfiguration(8, 16, 32, 32), new Zip_DeflateConfiguration(8, 16, 128, 128), new Zip_DeflateConfiguration(8, 32, 128, 256), new Zip_DeflateConfiguration(32, 128, 258, 1024), new Zip_DeflateConfiguration(32, 258, 258, 4096)]; - function zip_deflate_start(level) { - var i; - if(!level) { - level = zip_DEFAULT_LEVEL - }else { - if(level < 1) { - level = 1 + core.DomUtils = function DomUtils() { + var sharedRange = null; + function getSharedRange(doc) { + var range; + if(sharedRange) { + range = sharedRange }else { - if(level > 9) { - level = 9 - } + sharedRange = range = (doc.createRange()) } + return range } - zip_compr_level = level; - zip_initflag = false; - zip_eofile = false; - if(zip_outbuf !== null) { - return - } - zip_free_queue = zip_qhead = zip_qtail = null; - zip_outbuf = []; - zip_outbuf.length = zip_OUTBUFSIZ; - zip_window = []; - zip_window.length = zip_window_size; - zip_d_buf = []; - zip_d_buf.length = zip_DIST_BUFSIZE; - zip_l_buf = []; - zip_l_buf.length = zip_INBUFSIZ + zip_INBUF_EXTRA; - zip_prev = []; - zip_prev.length = 1 << zip_BITS; - zip_dyn_ltree = []; - zip_dyn_ltree.length = zip_HEAP_SIZE; - for(i = 0;i < zip_HEAP_SIZE;i++) { - zip_dyn_ltree[i] = new Zip_DeflateCT - } - zip_dyn_dtree = []; - zip_dyn_dtree.length = 2 * zip_D_CODES + 1; - for(i = 0;i < 2 * zip_D_CODES + 1;i++) { - zip_dyn_dtree[i] = new Zip_DeflateCT - } - zip_static_ltree = []; - zip_static_ltree.length = zip_L_CODES + 2; - for(i = 0;i < zip_L_CODES + 2;i++) { - zip_static_ltree[i] = new Zip_DeflateCT + function findStablePoint(container, offset) { + var c = container; + if(offset < c.childNodes.length) { + c = c.childNodes.item(offset); + offset = 0; + while(c.firstChild) { + c = c.firstChild + } + }else { + while(c.lastChild) { + c = c.lastChild; + offset = c.nodeType === Node.TEXT_NODE ? c.textContent.length : c.childNodes.length + } + } + return{container:c, offset:offset} } - zip_static_dtree = []; - zip_static_dtree.length = zip_D_CODES; - for(i = 0;i < zip_D_CODES;i++) { - zip_static_dtree[i] = new Zip_DeflateCT + function splitBoundaries(range) { + var modifiedNodes = [], end, splitStart, node, text; + if(range.startContainer.nodeType === Node.TEXT_NODE || range.endContainer.nodeType === Node.TEXT_NODE) { + end = range.endContainer && findStablePoint(range.endContainer, range.endOffset); + range.setEnd(end.container, end.offset); + node = range.endContainer; + if(range.endOffset !== 0 && node.nodeType === Node.TEXT_NODE) { + text = (node); + if(range.endOffset !== text.length) { + modifiedNodes.push(text.splitText(range.endOffset)); + modifiedNodes.push(text) + } + } + node = range.startContainer; + if(range.startOffset !== 0 && node.nodeType === Node.TEXT_NODE) { + text = (node); + if(range.startOffset !== text.length) { + splitStart = text.splitText(range.startOffset); + modifiedNodes.push(text); + modifiedNodes.push(splitStart); + range.setStart(splitStart, 0) + } + } + } + return modifiedNodes } - zip_bl_tree = []; - zip_bl_tree.length = 2 * zip_BL_CODES + 1; - for(i = 0;i < 2 * zip_BL_CODES + 1;i++) { - zip_bl_tree[i] = new Zip_DeflateCT + this.splitBoundaries = splitBoundaries; + function containsRange(container, insideRange) { + return container.compareBoundaryPoints(Range.START_TO_START, insideRange) <= 0 && container.compareBoundaryPoints(Range.END_TO_END, insideRange) >= 0 } - zip_l_desc = new Zip_DeflateTreeDesc; - zip_d_desc = new Zip_DeflateTreeDesc; - zip_bl_desc = new Zip_DeflateTreeDesc; - zip_bl_count = []; - zip_bl_count.length = zip_MAX_BITS + 1; - zip_heap = []; - zip_heap.length = 2 * zip_L_CODES + 1; - zip_depth = []; - zip_depth.length = 2 * zip_L_CODES + 1; - zip_length_code = []; - zip_length_code.length = zip_MAX_MATCH - zip_MIN_MATCH + 1; - zip_dist_code = []; - zip_dist_code.length = 512; - zip_base_length = []; - zip_base_length.length = zip_LENGTH_CODES; - zip_base_dist = []; - zip_base_dist.length = zip_D_CODES; - zip_flag_buf = []; - zip_flag_buf.length = parseInt(zip_LIT_BUFSIZE / 8, 10) - } - var zip_reuse_queue = function(p) { - p.next = zip_free_queue; - zip_free_queue = p - }; - var zip_new_queue = function() { - var p; - if(zip_free_queue !== null) { - p = zip_free_queue; - zip_free_queue = zip_free_queue.next - }else { - p = new Zip_DeflateBuffer + this.containsRange = containsRange; + function rangesIntersect(range1, range2) { + return range1.compareBoundaryPoints(Range.END_TO_START, range2) <= 0 && range1.compareBoundaryPoints(Range.START_TO_END, range2) >= 0 } - p.next = null; - p.len = p.off = 0; - return p - }; - var zip_head1 = function(i) { - return zip_prev[zip_WSIZE + i] - }; - var zip_head2 = function(i, val) { - zip_prev[zip_WSIZE + i] = val; - return val - }; - var zip_qoutbuf = function() { - var q, i; - if(zip_outcnt !== 0) { - q = zip_new_queue(); - if(zip_qhead === null) { - zip_qhead = zip_qtail = q - }else { - zip_qtail = zip_qtail.next = q + this.rangesIntersect = rangesIntersect; + function getNodesInRange(range, nodeFilter) { + var document = range.startContainer.ownerDocument, elements = [], rangeRoot = range.commonAncestorContainer, root = (rangeRoot.nodeType === Node.TEXT_NODE ? rangeRoot.parentNode : rangeRoot), n, filterResult, treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ALL, nodeFilter, false); + treeWalker.currentNode = range.startContainer; + n = range.startContainer; + while(n) { + filterResult = nodeFilter(n); + if(filterResult === NodeFilter.FILTER_ACCEPT) { + elements.push(n) + }else { + if(filterResult === NodeFilter.FILTER_REJECT) { + break + } + } + n = n.parentNode } - q.len = zip_outcnt - zip_outoff; - for(i = 0;i < q.len;i++) { - q.ptr[i] = zip_outbuf[zip_outoff + i] + elements.reverse(); + n = treeWalker.nextNode(); + while(n) { + elements.push(n); + n = treeWalker.nextNode() } - zip_outcnt = zip_outoff = 0 + return elements } - }; - var zip_put_byte = function(c) { - zip_outbuf[zip_outoff + zip_outcnt++] = c; - if(zip_outoff + zip_outcnt === zip_OUTBUFSIZ) { - zip_qoutbuf() - } - }; - var zip_put_short = function(w) { - w &= 65535; - if(zip_outoff + zip_outcnt < zip_OUTBUFSIZ - 2) { - zip_outbuf[zip_outoff + zip_outcnt++] = w & 255; - zip_outbuf[zip_outoff + zip_outcnt++] = w >>> 8 - }else { - zip_put_byte(w & 255); - zip_put_byte(w >>> 8) + this.getNodesInRange = getNodesInRange; + function mergeTextNodes(node, nextNode) { + var mergedNode = null, text, nextText; + if(node.nodeType === Node.TEXT_NODE) { + text = (node); + if(text.length === 0) { + text.parentNode.removeChild(text); + if(nextNode.nodeType === Node.TEXT_NODE) { + mergedNode = nextNode + } + }else { + if(nextNode.nodeType === Node.TEXT_NODE) { + nextText = (nextNode); + text.appendData(nextText.data); + nextNode.parentNode.removeChild(nextNode) + } + mergedNode = node + } + } + return mergedNode } - }; - var zip_INSERT_STRING = function() { - zip_ins_h = (zip_ins_h << zip_H_SHIFT ^ zip_window[zip_strstart + zip_MIN_MATCH - 1] & 255) & zip_HASH_MASK; - zip_hash_head = zip_head1(zip_ins_h); - zip_prev[zip_strstart & zip_WMASK] = zip_hash_head; - zip_head2(zip_ins_h, zip_strstart) - }; - var zip_Buf_size = 16; - var zip_send_bits = function(value, length) { - if(zip_bi_valid > zip_Buf_size - length) { - zip_bi_buf |= value << zip_bi_valid; - zip_put_short(zip_bi_buf); - zip_bi_buf = value >> zip_Buf_size - zip_bi_valid; - zip_bi_valid += length - zip_Buf_size - }else { - zip_bi_buf |= value << zip_bi_valid; - zip_bi_valid += length + function normalizeTextNodes(node) { + if(node && node.nextSibling) { + node = mergeTextNodes(node, node.nextSibling) + } + if(node && node.previousSibling) { + mergeTextNodes(node.previousSibling, node) + } } - }; - var zip_SEND_CODE = function(c, tree) { - zip_send_bits(tree[c].fc, tree[c].dl) - }; - var zip_D_CODE = function(dist) { - return(dist < 256 ? zip_dist_code[dist] : zip_dist_code[256 + (dist >> 7)]) & 255 - }; - var zip_SMALLER = function(tree, n, m) { - return tree[n].fc < tree[m].fc || tree[n].fc === tree[m].fc && zip_depth[n] <= zip_depth[m] - }; - var zip_read_buff = function(buff, offset, n) { - var i; - for(i = 0;i < n && zip_deflate_pos < zip_deflate_data.length;i++) { - buff[offset + i] = zip_deflate_data.charCodeAt(zip_deflate_pos++) & 255 + this.normalizeTextNodes = normalizeTextNodes; + function rangeContainsNode(limits, node) { + var range = node.ownerDocument.createRange(), nodeRange = node.ownerDocument.createRange(), result; + range.setStart(limits.startContainer, limits.startOffset); + range.setEnd(limits.endContainer, limits.endOffset); + nodeRange.selectNodeContents(node); + result = containsRange(range, nodeRange); + range.detach(); + nodeRange.detach(); + return result } - return i - }; - var zip_fill_window = function() { - var n, m; - var more = zip_window_size - zip_lookahead - zip_strstart; - if(more === -1) { - more-- - }else { - if(zip_strstart >= zip_WSIZE + zip_MAX_DIST) { - for(n = 0;n < zip_WSIZE;n++) { - zip_window[n] = zip_window[n + zip_WSIZE] - } - zip_match_start -= zip_WSIZE; - zip_strstart -= zip_WSIZE; - zip_block_start -= zip_WSIZE; - for(n = 0;n < zip_HASH_SIZE;n++) { - m = zip_head1(n); - zip_head2(n, m >= zip_WSIZE ? m - zip_WSIZE : zip_NIL) - } - for(n = 0;n < zip_WSIZE;n++) { - m = zip_prev[n]; - zip_prev[n] = m >= zip_WSIZE ? m - zip_WSIZE : zip_NIL - } - more += zip_WSIZE + this.rangeContainsNode = rangeContainsNode; + function mergeIntoParent(targetNode) { + var parent = targetNode.parentNode; + while(targetNode.firstChild) { + parent.insertBefore(targetNode.firstChild, targetNode) } + parent.removeChild(targetNode); + return parent } - if(!zip_eofile) { - n = zip_read_buff(zip_window, zip_strstart + zip_lookahead, more); - if(n <= 0) { - zip_eofile = true - }else { - zip_lookahead += n + this.mergeIntoParent = mergeIntoParent; + function removeUnwantedNodes(targetNode, shouldRemove) { + var parent = targetNode.parentNode, node = targetNode.firstChild, next; + while(node) { + next = node.nextSibling; + removeUnwantedNodes(node, shouldRemove); + node = next } + if(shouldRemove(targetNode)) { + parent = mergeIntoParent(targetNode) + } + return parent } - }; - var zip_lm_init = function() { - var j; - for(j = 0;j < zip_HASH_SIZE;j++) { - zip_prev[zip_WSIZE + j] = 0 - } - zip_max_lazy_match = zip_configuration_table[zip_compr_level].max_lazy; - zip_good_match = zip_configuration_table[zip_compr_level].good_length; - if(!zip_FULL_SEARCH) { - zip_nice_match = zip_configuration_table[zip_compr_level].nice_length + this.removeUnwantedNodes = removeUnwantedNodes; + function getElementsByTagNameNS(node, namespace, tagName) { + var e = [], list, i, l; + list = node.getElementsByTagNameNS(namespace, tagName); + e.length = l = list.length; + for(i = 0;i < l;i += 1) { + e[i] = (list.item(i)) + } + return e } - zip_max_chain_length = zip_configuration_table[zip_compr_level].max_chain; - zip_strstart = 0; - zip_block_start = 0; - zip_lookahead = zip_read_buff(zip_window, 0, 2 * zip_WSIZE); - if(zip_lookahead <= 0) { - zip_eofile = true; - zip_lookahead = 0; - return + this.getElementsByTagNameNS = getElementsByTagNameNS; + function rangeIntersectsNode(range, node) { + var nodeRange = node.ownerDocument.createRange(), result; + nodeRange.selectNodeContents(node); + result = rangesIntersect(range, nodeRange); + nodeRange.detach(); + return result } - zip_eofile = false; - while(zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) { - zip_fill_window() + this.rangeIntersectsNode = rangeIntersectsNode; + function containsNode(parent, descendant) { + return parent === descendant || (parent).contains((descendant)) } - zip_ins_h = 0; - for(j = 0;j < zip_MIN_MATCH - 1;j++) { - zip_ins_h = (zip_ins_h << zip_H_SHIFT ^ zip_window[j] & 255) & zip_HASH_MASK + this.containsNode = containsNode; + function containsNodeForBrokenWebKit(parent, descendant) { + return parent === descendant || Boolean(parent.compareDocumentPosition(descendant) & Node.DOCUMENT_POSITION_CONTAINED_BY) } - }; - var zip_longest_match = function(cur_match) { - var chain_length = zip_max_chain_length; - var scanp = zip_strstart; - var matchp; - var len; - var best_len = zip_prev_length; - var limit = zip_strstart > zip_MAX_DIST ? zip_strstart - zip_MAX_DIST : zip_NIL; - var strendp = zip_strstart + zip_MAX_MATCH; - var scan_end1 = zip_window[scanp + best_len - 1]; - var scan_end = zip_window[scanp + best_len]; - if(zip_prev_length >= zip_good_match) { - chain_length >>= 2 + function getPositionInContainingNode(node, container) { + var offset = 0, n; + while(node.parentNode !== container) { + runtime.assert(node.parentNode !== null, "parent is null"); + node = (node.parentNode) + } + n = container.firstChild; + while(n !== node) { + offset += 1; + n = n.nextSibling + } + return offset } - do { - matchp = cur_match; - if(zip_window[matchp + best_len] !== scan_end || (zip_window[matchp + best_len - 1] !== scan_end1 || (zip_window[matchp] !== zip_window[scanp] || zip_window[++matchp] !== zip_window[scanp + 1]))) { - continue + function comparePoints(c1, o1, c2, o2) { + if(c1 === c2) { + return o2 - o1 } - scanp += 2; - matchp++; - do { - ++scanp - }while(zip_window[scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && scanp < strendp)))))))); - len = zip_MAX_MATCH - (strendp - scanp); - scanp = strendp - zip_MAX_MATCH; - if(len > best_len) { - zip_match_start = cur_match; - best_len = len; - if(zip_FULL_SEARCH) { - if(len >= zip_MAX_MATCH) { - break - } + var comparison = c1.compareDocumentPosition(c2); + if(comparison === 2) { + comparison = -1 + }else { + if(comparison === 4) { + comparison = 1 }else { - if(len >= zip_nice_match) { - break + if(comparison === 10) { + o1 = getPositionInContainingNode(c1, c2); + comparison = o1 < o2 ? 1 : -1 + }else { + o2 = getPositionInContainingNode(c2, c1); + comparison = o2 < o1 ? -1 : 1 } } - scan_end1 = zip_window[scanp + best_len - 1]; - scan_end = zip_window[scanp + best_len] } - cur_match = zip_prev[cur_match & zip_WMASK] - }while(cur_match > limit && --chain_length !== 0); - return best_len - }; - var zip_ct_tally = function(dist, lc) { - zip_l_buf[zip_last_lit++] = lc; - if(dist === 0) { - zip_dyn_ltree[lc].fc++ - }else { - dist--; - zip_dyn_ltree[zip_length_code[lc] + zip_LITERALS + 1].fc++; - zip_dyn_dtree[zip_D_CODE(dist)].fc++; - zip_d_buf[zip_last_dist++] = dist; - zip_flags |= zip_flag_bit - } - zip_flag_bit <<= 1; - if((zip_last_lit & 7) === 0) { - zip_flag_buf[zip_last_flags++] = zip_flags; - zip_flags = 0; - zip_flag_bit = 1 + return comparison } - if(zip_compr_level > 2 && (zip_last_lit & 4095) === 0) { - var out_length = zip_last_lit * 8; - var in_length = zip_strstart - zip_block_start; - var dcode; - for(dcode = 0;dcode < zip_D_CODES;dcode++) { - out_length += zip_dyn_dtree[dcode].fc * (5 + zip_extra_dbits[dcode]) - } - out_length >>= 3; - if(zip_last_dist < parseInt(zip_last_lit / 2, 10) && out_length < parseInt(in_length / 2, 10)) { - return true + this.comparePoints = comparePoints; + function adaptRangeDifferenceToZoomLevel(inputNumber, zoomLevel) { + if(getBrowserQuirks().unscaledRangeClientRects) { + return inputNumber } + return inputNumber / zoomLevel } - return zip_last_lit === zip_LIT_BUFSIZE - 1 || zip_last_dist === zip_DIST_BUFSIZE - }; - var zip_pqdownheap = function(tree, k) { - var v = zip_heap[k]; - var j = k << 1; - while(j <= zip_heap_len) { - if(j < zip_heap_len && zip_SMALLER(tree, zip_heap[j + 1], zip_heap[j])) { - j++ - } - if(zip_SMALLER(tree, v, zip_heap[j])) { - break + this.adaptRangeDifferenceToZoomLevel = adaptRangeDifferenceToZoomLevel; + function getBoundingClientRect(node) { + var doc = (node.ownerDocument), quirks = getBrowserQuirks(), range, element; + if(quirks.unscaledRangeClientRects === false || quirks.rangeBCRIgnoresElementBCR) { + if(node.nodeType === Node.ELEMENT_NODE) { + element = (node); + return element.getBoundingClientRect() + } } - zip_heap[k] = zip_heap[j]; - k = j; - j <<= 1 + range = getSharedRange(doc); + range.selectNode(node); + return range.getBoundingClientRect() } - zip_heap[k] = v - }; - var zip_gen_bitlen = function(desc) { - var tree = desc.dyn_tree; - var extra = desc.extra_bits; - var base = desc.extra_base; - var max_code = desc.max_code; - var max_length = desc.max_length; - var stree = desc.static_tree; - var h; - var n, m; - var bits; - var xbits; - var f; - var overflow = 0; - for(bits = 0;bits <= zip_MAX_BITS;bits++) { - zip_bl_count[bits] = 0 + this.getBoundingClientRect = getBoundingClientRect; + function mapKeyValObjOntoNode(node, properties, nsResolver) { + Object.keys(properties).forEach(function(key) { + var parts = key.split(":"), prefix = parts[0], localName = parts[1], ns = nsResolver(prefix), value = properties[key], element; + if(ns) { + element = (node.getElementsByTagNameNS(ns, localName)[0]); + if(!element) { + element = node.ownerDocument.createElementNS(ns, key); + node.appendChild(element) + } + element.textContent = value + }else { + runtime.log("Key ignored: " + key) + } + }) } - tree[zip_heap[zip_heap_max]].dl = 0; - for(h = zip_heap_max + 1;h < zip_HEAP_SIZE;h++) { - n = zip_heap[h]; - bits = tree[tree[n].dl].dl + 1; - if(bits > max_length) { - bits = max_length; - overflow++ - } - tree[n].dl = bits; - if(n > max_code) { - continue - } - zip_bl_count[bits]++; - xbits = 0; - if(n >= base) { - xbits = extra[n - base] - } - f = tree[n].fc; - zip_opt_len += f * (bits + xbits); - if(stree !== null) { - zip_static_len += f * (stree[n].dl + xbits) + this.mapKeyValObjOntoNode = mapKeyValObjOntoNode; + function removeKeyElementsFromNode(node, propertyNames, nsResolver) { + propertyNames.forEach(function(propertyName) { + var parts = propertyName.split(":"), prefix = parts[0], localName = parts[1], ns = nsResolver(prefix), element; + if(ns) { + element = (node.getElementsByTagNameNS(ns, localName)[0]); + if(element) { + element.parentNode.removeChild(element) + }else { + runtime.log("Element for " + propertyName + " not found.") + } + }else { + runtime.log("Property Name ignored: " + propertyName) + } + }) + } + this.removeKeyElementsFromNode = removeKeyElementsFromNode; + function getKeyValRepresentationOfNode(node, prefixResolver) { + var properties = {}, currentSibling = node.firstElementChild, prefix; + while(currentSibling) { + prefix = prefixResolver(currentSibling.namespaceURI); + if(prefix) { + properties[prefix + ":" + currentSibling.localName] = currentSibling.textContent + } + currentSibling = currentSibling.nextElementSibling } + return properties } - if(overflow === 0) { - return + this.getKeyValRepresentationOfNode = getKeyValRepresentationOfNode; + function mapObjOntoNode(node, properties, nsResolver) { + Object.keys(properties).forEach(function(key) { + var parts = key.split(":"), prefix = parts[0], localName = parts[1], ns = nsResolver(prefix), value = properties[key], element; + if(typeof value === "object" && Object.keys((value)).length) { + if(ns) { + element = (node.getElementsByTagNameNS(ns, localName)[0]) || node.ownerDocument.createElementNS(ns, key) + }else { + element = (node.getElementsByTagName(localName)[0]) || node.ownerDocument.createElement(key) + } + node.appendChild(element); + mapObjOntoNode(element, (value), nsResolver) + }else { + if(ns) { + node.setAttributeNS(ns, key, String(value)) + } + } + }) } - do { - bits = max_length - 1; - while(zip_bl_count[bits] === 0) { - bits-- + this.mapObjOntoNode = mapObjOntoNode; + function init(self) { + var appVersion, webKitOrSafari, ie, window = runtime.getWindow(); + if(window === null) { + return } - zip_bl_count[bits]--; - zip_bl_count[bits + 1] += 2; - zip_bl_count[max_length]--; - overflow -= 2 - }while(overflow > 0); - for(bits = max_length;bits !== 0;bits--) { - n = zip_bl_count[bits]; - while(n !== 0) { - m = zip_heap[--h]; - if(m > max_code) { - continue - } - if(tree[m].dl !== bits) { - zip_opt_len += (bits - tree[m].dl) * tree[m].fc; - tree[m].fc = bits - } - n-- + appVersion = window.navigator.appVersion.toLowerCase(); + webKitOrSafari = appVersion.indexOf("chrome") === -1 && (appVersion.indexOf("applewebkit") !== -1 || appVersion.indexOf("safari") !== -1); + ie = appVersion.indexOf("msie"); + if(webKitOrSafari || ie) { + self.containsNode = containsNodeForBrokenWebKit } } + init(this) }; - var zip_bi_reverse = function(code, len) { - var res = 0; - do { - res |= code & 1; - code >>= 1; - res <<= 1 - }while(--len > 0); - return res >> 1 - }; - var zip_gen_codes = function(tree, max_code) { - var next_code = []; - next_code.length = zip_MAX_BITS + 1; - var code = 0; - var bits; - var n; - for(bits = 1;bits <= zip_MAX_BITS;bits++) { - code = code + zip_bl_count[bits - 1] << 1; - next_code[bits] = code + return core.DomUtils +})(); +/* + + Copyright (C) 2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +core.EventNotifier = function EventNotifier(eventIds) { + var eventListener = {}; + this.emit = function(eventId, args) { + var i, subscribers; + runtime.assert(eventListener.hasOwnProperty(eventId), 'unknown event fired "' + eventId + '"'); + subscribers = eventListener[eventId]; + for(i = 0;i < subscribers.length;i += 1) { + subscribers[i](args) } - var len; - for(n = 0;n <= max_code;n++) { - len = tree[n].dl; - if(len === 0) { - continue - } - tree[n].fc = zip_bi_reverse(next_code[len]++, len) + }; + this.subscribe = function(eventId, cb) { + runtime.assert(eventListener.hasOwnProperty(eventId), 'tried to subscribe to unknown event "' + eventId + '"'); + eventListener[eventId].push(cb); + runtime.log('event "' + eventId + '" subscribed.') + }; + this.unsubscribe = function(eventId, cb) { + var cbIndex; + runtime.assert(eventListener.hasOwnProperty(eventId), 'tried to unsubscribe from unknown event "' + eventId + '"'); + cbIndex = eventListener[eventId].indexOf(cb); + runtime.assert(cbIndex !== -1, 'tried to unsubscribe unknown callback from event "' + eventId + '"'); + if(cbIndex !== -1) { + eventListener[eventId].splice(cbIndex, 1) } + runtime.log('event "' + eventId + '" unsubscribed.') }; - var zip_build_tree = function(desc) { - var tree = desc.dyn_tree; - var stree = desc.static_tree; - var elems = desc.elems; - var n, m; - var max_code = -1; - var node = elems; - zip_heap_len = 0; - zip_heap_max = zip_HEAP_SIZE; - for(n = 0;n < elems;n++) { - if(tree[n].fc !== 0) { - zip_heap[++zip_heap_len] = max_code = n; - zip_depth[n] = 0 - }else { - tree[n].dl = 0 + function init() { + var i, eventId; + for(i = 0;i < eventIds.length;i += 1) { + eventId = eventIds[i]; + runtime.assert(!eventListener.hasOwnProperty(eventId), 'Duplicated event ids: "' + eventId + '" registered more than once.'); + eventListener[eventId] = [] + } + } + init() +}; +/* + + Copyright (C) 2012 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +core.LoopWatchDog = function LoopWatchDog(timeout, maxChecks) { + var startTime = Date.now(), checks = 0; + function check() { + var t; + if(timeout) { + t = Date.now(); + if(t - startTime > timeout) { + runtime.log("alert", "watchdog timeout"); + throw"timeout!"; } } - var xnew; - while(zip_heap_len < 2) { - xnew = zip_heap[++zip_heap_len] = max_code < 2 ? ++max_code : 0; - tree[xnew].fc = 1; - zip_depth[xnew] = 0; - zip_opt_len--; - if(stree !== null) { - zip_static_len -= stree[xnew].dl + if(maxChecks > 0) { + checks += 1; + if(checks > maxChecks) { + runtime.log("alert", "watchdog loop overflow"); + throw"loop overflow"; } } - desc.max_code = max_code; - for(n = zip_heap_len >> 1;n >= 1;n--) { - zip_pqdownheap(tree, n) + } + this.check = check +}; +core.PositionIterator = function PositionIterator(root, whatToShow, filter, expandEntityReferences) { + var self = this, walker, currentPos, nodeFilter, TEXT_NODE = Node.TEXT_NODE, ELEMENT_NODE = Node.ELEMENT_NODE, FILTER_ACCEPT = NodeFilter.FILTER_ACCEPT, FILTER_REJECT = NodeFilter.FILTER_REJECT; + function EmptyTextNodeFilter() { + this.acceptNode = function(node) { + var text = (node); + if(!node || node.nodeType === TEXT_NODE && text.length === 0) { + return FILTER_REJECT + } + return FILTER_ACCEPT } - do { - n = zip_heap[zip_SMALLEST]; - zip_heap[zip_SMALLEST] = zip_heap[zip_heap_len--]; - zip_pqdownheap(tree, zip_SMALLEST); - m = zip_heap[zip_SMALLEST]; - zip_heap[--zip_heap_max] = n; - zip_heap[--zip_heap_max] = m; - tree[node].fc = tree[n].fc + tree[m].fc; - if(zip_depth[n] > zip_depth[m] + 1) { - zip_depth[node] = zip_depth[n] - }else { - zip_depth[node] = zip_depth[m] + 1 + } + function FilteredEmptyTextNodeFilter(filter) { + this.acceptNode = function(node) { + var text = (node); + if(!node || node.nodeType === TEXT_NODE && text.length === 0) { + return FILTER_REJECT } - tree[n].dl = tree[m].dl = node; - zip_heap[zip_SMALLEST] = node++; - zip_pqdownheap(tree, zip_SMALLEST) - }while(zip_heap_len >= 2); - zip_heap[--zip_heap_max] = zip_heap[zip_SMALLEST]; - zip_gen_bitlen(desc); - zip_gen_codes(tree, max_code) - }; - var zip_scan_tree = function(tree, max_code) { - var n; - var prevlen = -1; - var curlen; - var nextlen = tree[0].dl; - var count = 0; - var max_count = 7; - var min_count = 4; - if(nextlen === 0) { - max_count = 138; - min_count = 3 + return filter.acceptNode(node) } - tree[max_code + 1].dl = 65535; - for(n = 0;n <= max_code;n++) { - curlen = nextlen; - nextlen = tree[n + 1].dl; - if(++count < max_count && curlen === nextlen) { - continue + } + this.nextPosition = function() { + var currentNode = walker.currentNode, nodeType = currentNode.nodeType, text = (currentNode); + if(currentNode === root) { + return false + } + if(currentPos === 0 && nodeType === ELEMENT_NODE) { + if(walker.firstChild() === null) { + currentPos = 1 } - if(count < min_count) { - zip_bl_tree[curlen].fc += count + }else { + if(nodeType === TEXT_NODE && currentPos + 1 < text.length) { + currentPos += 1 }else { - if(curlen !== 0) { - if(curlen !== prevlen) { - zip_bl_tree[curlen].fc++ - } - zip_bl_tree[zip_REP_3_6].fc++ + if(walker.nextSibling() !== null) { + currentPos = 0 }else { - if(count <= 10) { - zip_bl_tree[zip_REPZ_3_10].fc++ + if(walker.parentNode()) { + currentPos = 1 }else { - zip_bl_tree[zip_REPZ_11_138].fc++ + return false } } } - count = 0; - prevlen = curlen; - if(nextlen === 0) { - max_count = 138; - min_count = 3 + } + return true + }; + function setAtEnd() { + var text = (walker.currentNode), type = text.nodeType; + if(type === TEXT_NODE) { + currentPos = text.length - 1 + }else { + currentPos = type === ELEMENT_NODE ? 1 : 0 + } + } + function previousNode() { + if(walker.previousSibling() === null) { + if(!walker.parentNode() || walker.currentNode === root) { + walker.firstChild(); + return false + } + currentPos = 0 + }else { + setAtEnd() + } + return true + } + this.previousPosition = function() { + var moved = true, currentNode = walker.currentNode; + if(currentPos === 0) { + moved = previousNode() + }else { + if(currentNode.nodeType === TEXT_NODE) { + currentPos -= 1 }else { - if(curlen === nextlen) { - max_count = 6; - min_count = 3 + if(walker.lastChild() !== null) { + setAtEnd() }else { - max_count = 7; - min_count = 4 + if(currentNode === root) { + moved = false + }else { + currentPos = 0 + } } } } + return moved }; - var zip_build_bl_tree = function() { - var max_blindex; - zip_scan_tree(zip_dyn_ltree, zip_l_desc.max_code); - zip_scan_tree(zip_dyn_dtree, zip_d_desc.max_code); - zip_build_tree(zip_bl_desc); - for(max_blindex = zip_BL_CODES - 1;max_blindex >= 3;max_blindex--) { - if(zip_bl_tree[zip_bl_order[max_blindex]].dl !== 0) { - break - } + this.previousNode = previousNode; + this.container = function() { + var n = (walker.currentNode), t = n.nodeType; + if(currentPos === 0 && t !== TEXT_NODE) { + n = (n.parentNode) } - zip_opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; - return max_blindex + return n }; - var zip_bi_windup = function() { - if(zip_bi_valid > 8) { - zip_put_short(zip_bi_buf) + this.rightNode = function() { + var n = walker.currentNode, text = (n), nodeType = n.nodeType; + if(nodeType === TEXT_NODE && currentPos === text.length) { + n = n.nextSibling; + while(n && nodeFilter(n) !== FILTER_ACCEPT) { + n = n.nextSibling + } }else { - if(zip_bi_valid > 0) { - zip_put_byte(zip_bi_buf) + if(nodeType === ELEMENT_NODE && currentPos === 1) { + n = null } } - zip_bi_buf = 0; - zip_bi_valid = 0 + return n }; - var zip_compress_block = function(ltree, dtree) { - var dist; - var lc; - var lx = 0; - var dx = 0; - var fx = 0; - var flag = 0; - var code; - var extra; - if(zip_last_lit !== 0) { - do { - if((lx & 7) === 0) { - flag = zip_flag_buf[fx++] - } - lc = zip_l_buf[lx++] & 255; - if((flag & 1) === 0) { - zip_SEND_CODE(lc, ltree) - }else { - code = zip_length_code[lc]; - zip_SEND_CODE(code + zip_LITERALS + 1, ltree); - extra = zip_extra_lbits[code]; - if(extra !== 0) { - lc -= zip_base_length[code]; - zip_send_bits(lc, extra) - } - dist = zip_d_buf[dx++]; - code = zip_D_CODE(dist); - zip_SEND_CODE(code, dtree); - extra = zip_extra_dbits[code]; - if(extra !== 0) { - dist -= zip_base_dist[code]; - zip_send_bits(dist, extra) - } + this.leftNode = function() { + var n = walker.currentNode; + if(currentPos === 0) { + n = n.previousSibling; + while(n && nodeFilter(n) !== FILTER_ACCEPT) { + n = n.previousSibling + } + }else { + if(n.nodeType === ELEMENT_NODE) { + n = n.lastChild; + while(n && nodeFilter(n) !== FILTER_ACCEPT) { + n = n.previousSibling } - flag >>= 1 - }while(lx < zip_last_lit) + } } - zip_SEND_CODE(zip_END_BLOCK, ltree) + return n }; - var zip_send_tree = function(tree, max_code) { - var n; - var prevlen = -1; - var curlen; - var nextlen = tree[0].dl; - var count = 0; - var max_count = 7; - var min_count = 4; - if(nextlen === 0) { - max_count = 138; - min_count = 3 + this.getCurrentNode = function() { + var n = (walker.currentNode); + return n + }; + this.unfilteredDomOffset = function() { + if(walker.currentNode.nodeType === TEXT_NODE) { + return currentPos } - for(n = 0;n <= max_code;n++) { - curlen = nextlen; - nextlen = tree[n + 1].dl; - if(++count < max_count && curlen === nextlen) { - continue - } - if(count < min_count) { - do { - zip_SEND_CODE(curlen, zip_bl_tree) - }while(--count !== 0) - }else { - if(curlen !== 0) { - if(curlen !== prevlen) { - zip_SEND_CODE(curlen, zip_bl_tree); - count-- - } - zip_SEND_CODE(zip_REP_3_6, zip_bl_tree); - zip_send_bits(count - 3, 2) + var c = 0, n = walker.currentNode; + if(currentPos === 1) { + n = n.lastChild + }else { + n = n.previousSibling + } + while(n) { + c += 1; + n = n.previousSibling + } + return c + }; + this.getPreviousSibling = function() { + var currentNode = walker.currentNode, sibling = walker.previousSibling(); + walker.currentNode = currentNode; + return sibling + }; + this.getNextSibling = function() { + var currentNode = walker.currentNode, sibling = walker.nextSibling(); + walker.currentNode = currentNode; + return sibling + }; + this.setUnfilteredPosition = function(container, offset) { + var filterResult, node, text; + runtime.assert(container !== null && container !== undefined, "PositionIterator.setUnfilteredPosition called without container"); + walker.currentNode = container; + if(container.nodeType === TEXT_NODE) { + currentPos = offset; + text = (container); + runtime.assert(offset <= text.length, "Error in setPosition: " + offset + " > " + text.length); + runtime.assert(offset >= 0, "Error in setPosition: " + offset + " < 0"); + if(offset === text.length) { + if(walker.nextSibling()) { + currentPos = 0 }else { - if(count <= 10) { - zip_SEND_CODE(zip_REPZ_3_10, zip_bl_tree); - zip_send_bits(count - 3, 3) + if(walker.parentNode()) { + currentPos = 1 }else { - zip_SEND_CODE(zip_REPZ_11_138, zip_bl_tree); - zip_send_bits(count - 11, 7) + runtime.assert(false, "Error in setUnfilteredPosition: position not valid.") } } } - count = 0; - prevlen = curlen; - if(nextlen === 0) { - max_count = 138; - min_count = 3 - }else { - if(curlen === nextlen) { - max_count = 6; - min_count = 3 - }else { - max_count = 7; - min_count = 4 - } - } + return true } - }; - var zip_send_all_trees = function(lcodes, dcodes, blcodes) { - var rank; - zip_send_bits(lcodes - 257, 5); - zip_send_bits(dcodes - 1, 5); - zip_send_bits(blcodes - 4, 4); - for(rank = 0;rank < blcodes;rank++) { - zip_send_bits(zip_bl_tree[zip_bl_order[rank]].dl, 3) + filterResult = nodeFilter(container); + node = container.parentNode; + while(node && (node !== root && filterResult === FILTER_ACCEPT)) { + filterResult = nodeFilter(node); + if(filterResult !== FILTER_ACCEPT) { + walker.currentNode = node + } + node = node.parentNode } - zip_send_tree(zip_dyn_ltree, lcodes - 1); - zip_send_tree(zip_dyn_dtree, dcodes - 1) - }; - var zip_init_block = function() { - var n; - for(n = 0;n < zip_L_CODES;n++) { - zip_dyn_ltree[n].fc = 0 + if(offset < container.childNodes.length && filterResult !== NodeFilter.FILTER_REJECT) { + walker.currentNode = (container.childNodes.item(offset)); + filterResult = nodeFilter(walker.currentNode); + currentPos = 0 + }else { + currentPos = 1 } - for(n = 0;n < zip_D_CODES;n++) { - zip_dyn_dtree[n].fc = 0 + if(filterResult === NodeFilter.FILTER_REJECT) { + currentPos = 1 } - for(n = 0;n < zip_BL_CODES;n++) { - zip_bl_tree[n].fc = 0 + if(filterResult !== FILTER_ACCEPT) { + return self.nextPosition() } - zip_dyn_ltree[zip_END_BLOCK].fc = 1; - zip_opt_len = zip_static_len = 0; - zip_last_lit = zip_last_dist = zip_last_flags = 0; - zip_flags = 0; - zip_flag_bit = 1 + runtime.assert(nodeFilter(walker.currentNode) === FILTER_ACCEPT, "PositionIterater.setUnfilteredPosition call resulted in an non-visible node being set"); + return true }; - var zip_flush_block = function(eof) { - var opt_lenb, static_lenb; - var max_blindex; - var stored_len; - stored_len = zip_strstart - zip_block_start; - zip_flag_buf[zip_last_flags] = zip_flags; - zip_build_tree(zip_l_desc); - zip_build_tree(zip_d_desc); - max_blindex = zip_build_bl_tree(); - opt_lenb = zip_opt_len + 3 + 7 >> 3; - static_lenb = zip_static_len + 3 + 7 >> 3; - if(static_lenb <= opt_lenb) { - opt_lenb = static_lenb + this.moveToEnd = function() { + walker.currentNode = root; + currentPos = 1 + }; + this.moveToEndOfNode = function(node) { + var text; + if(node.nodeType === TEXT_NODE) { + text = (node); + self.setUnfilteredPosition(text, text.length) + }else { + walker.currentNode = node; + currentPos = 1 } - if(stored_len + 4 <= opt_lenb && zip_block_start >= 0) { - var i; - zip_send_bits((zip_STORED_BLOCK << 1) + eof, 3); - zip_bi_windup(); - zip_put_short(stored_len); - zip_put_short(~stored_len); - for(i = 0;i < stored_len;i++) { - zip_put_byte(zip_window[zip_block_start + i]) - } + }; + this.getNodeFilter = function() { + return nodeFilter + }; + function init() { + var f; + if(filter) { + f = new FilteredEmptyTextNodeFilter(filter) }else { - if(static_lenb === opt_lenb) { - zip_send_bits((zip_STATIC_TREES << 1) + eof, 3); - zip_compress_block(zip_static_ltree, zip_static_dtree) - }else { - zip_send_bits((zip_DYN_TREES << 1) + eof, 3); - zip_send_all_trees(zip_l_desc.max_code + 1, zip_d_desc.max_code + 1, max_blindex + 1); - zip_compress_block(zip_dyn_ltree, zip_dyn_dtree) - } + f = new EmptyTextNodeFilter } - zip_init_block(); - if(eof !== 0) { - zip_bi_windup() + nodeFilter = (f.acceptNode); + nodeFilter.acceptNode = nodeFilter; + whatToShow = whatToShow || 4294967295; + runtime.assert(root.nodeType !== Node.TEXT_NODE, "Internet Explorer doesn't allow tree walker roots to be text nodes"); + walker = root.ownerDocument.createTreeWalker(root, whatToShow, nodeFilter, expandEntityReferences); + currentPos = 0; + if(walker.firstChild() === null) { + currentPos = 1 } - }; - var zip_deflate_fast = function() { - var flush; - while(zip_lookahead !== 0 && zip_qhead === null) { - zip_INSERT_STRING(); - if(zip_hash_head !== zip_NIL && zip_strstart - zip_hash_head <= zip_MAX_DIST) { - zip_match_length = zip_longest_match(zip_hash_head); - if(zip_match_length > zip_lookahead) { - zip_match_length = zip_lookahead - } - } - if(zip_match_length >= zip_MIN_MATCH) { - flush = zip_ct_tally(zip_strstart - zip_match_start, zip_match_length - zip_MIN_MATCH); - zip_lookahead -= zip_match_length; - if(zip_match_length <= zip_max_lazy_match) { - zip_match_length--; - do { - zip_strstart++; - zip_INSERT_STRING() - }while(--zip_match_length !== 0); - zip_strstart++ - }else { - zip_strstart += zip_match_length; - zip_match_length = 0; - zip_ins_h = zip_window[zip_strstart] & 255; - zip_ins_h = (zip_ins_h << zip_H_SHIFT ^ zip_window[zip_strstart + 1] & 255) & zip_HASH_MASK - } - }else { - flush = zip_ct_tally(0, zip_window[zip_strstart] & 255); - zip_lookahead--; - zip_strstart++ - } - if(flush) { - zip_flush_block(0); - zip_block_start = zip_strstart - } - while(zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) { - zip_fill_window() - } - } - }; - var zip_deflate_better = function() { - var flush; - while(zip_lookahead !== 0 && zip_qhead === null) { - zip_INSERT_STRING(); - zip_prev_length = zip_match_length; - zip_prev_match = zip_match_start; - zip_match_length = zip_MIN_MATCH - 1; - if(zip_hash_head !== zip_NIL && (zip_prev_length < zip_max_lazy_match && zip_strstart - zip_hash_head <= zip_MAX_DIST)) { - zip_match_length = zip_longest_match(zip_hash_head); - if(zip_match_length > zip_lookahead) { - zip_match_length = zip_lookahead - } - if(zip_match_length === zip_MIN_MATCH && zip_strstart - zip_match_start > zip_TOO_FAR) { - zip_match_length-- - } - } - if(zip_prev_length >= zip_MIN_MATCH && zip_match_length <= zip_prev_length) { - flush = zip_ct_tally(zip_strstart - 1 - zip_prev_match, zip_prev_length - zip_MIN_MATCH); - zip_lookahead -= zip_prev_length - 1; - zip_prev_length -= 2; - do { - zip_strstart++; - zip_INSERT_STRING() - }while(--zip_prev_length !== 0); - zip_match_available = 0; - zip_match_length = zip_MIN_MATCH - 1; - zip_strstart++; - if(flush) { - zip_flush_block(0); - zip_block_start = zip_strstart - } - }else { - if(zip_match_available !== 0) { - if(zip_ct_tally(0, zip_window[zip_strstart - 1] & 255)) { - zip_flush_block(0); - zip_block_start = zip_strstart - } - zip_strstart++; - zip_lookahead-- - }else { - zip_match_available = 1; - zip_strstart++; - zip_lookahead-- - } - } - while(zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) { - zip_fill_window() - } + } + init() +}; +core.zip_HuftNode = function() { + this.e = 0; + this.b = 0; + this.n = 0; + this.t = null +}; +core.zip_HuftList = function() { + this.next = null; + this.list = null +}; +core.RawInflate = function RawInflate() { + var zip_WSIZE = 32768; + var zip_STORED_BLOCK = 0; + var zip_lbits = 9; + var zip_dbits = 6; + var zip_slide = []; + var zip_wp; + var zip_fixed_tl = null; + var zip_fixed_td; + var zip_fixed_bl; + var zip_bit_buf; + var zip_bit_len; + var zip_method; + var zip_eof; + var zip_copy_leng; + var zip_copy_dist; + var zip_tl, zip_td; + var zip_bl, zip_bd; + var zip_inflate_data; + var zip_inflate_pos; + var zip_MASK_BITS = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535]; + var zip_cplens = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0]; + var zip_cplext = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99]; + var zip_cpdist = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577]; + var zip_cpdext = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13]; + var zip_border = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + function Zip_HuftBuild(b, n, s, d, e, mm) { + this.BMAX = 16; + this.N_MAX = 288; + this.status = 0; + this.root = null; + this.m = 0; + var a, c = new Array(this.BMAX + 1), el, f, g, h, i, j, k, lx = new Array(this.BMAX + 1), p, pidx, q, r = new core.zip_HuftNode, u = new Array(this.BMAX), v = new Array(this.N_MAX), w, x = new Array(this.BMAX + 1), xp, y, z, o, tail; + tail = this.root = null; + for(i = 0;i < c.length;i++) { + c[i] = 0 } - }; - var zip_ct_init = function() { - var n; - var bits; - var length; - var code; - var dist; - if(zip_static_dtree[0].dl !== 0) { - return + for(i = 0;i < lx.length;i++) { + lx[i] = 0 } - zip_l_desc.dyn_tree = zip_dyn_ltree; - zip_l_desc.static_tree = zip_static_ltree; - zip_l_desc.extra_bits = zip_extra_lbits; - zip_l_desc.extra_base = zip_LITERALS + 1; - zip_l_desc.elems = zip_L_CODES; - zip_l_desc.max_length = zip_MAX_BITS; - zip_l_desc.max_code = 0; - zip_d_desc.dyn_tree = zip_dyn_dtree; - zip_d_desc.static_tree = zip_static_dtree; - zip_d_desc.extra_bits = zip_extra_dbits; - zip_d_desc.extra_base = 0; - zip_d_desc.elems = zip_D_CODES; - zip_d_desc.max_length = zip_MAX_BITS; - zip_d_desc.max_code = 0; - zip_bl_desc.dyn_tree = zip_bl_tree; - zip_bl_desc.static_tree = null; - zip_bl_desc.extra_bits = zip_extra_blbits; - zip_bl_desc.extra_base = 0; - zip_bl_desc.elems = zip_BL_CODES; - zip_bl_desc.max_length = zip_MAX_BL_BITS; - zip_bl_desc.max_code = 0; - length = 0; - for(code = 0;code < zip_LENGTH_CODES - 1;code++) { - zip_base_length[code] = length; - for(n = 0;n < 1 << zip_extra_lbits[code];n++) { - zip_length_code[length++] = code - } + for(i = 0;i < u.length;i++) { + u[i] = null } - zip_length_code[length - 1] = code; - dist = 0; - for(code = 0;code < 16;code++) { - zip_base_dist[code] = dist; - for(n = 0;n < 1 << zip_extra_dbits[code];n++) { - zip_dist_code[dist++] = code - } + for(i = 0;i < v.length;i++) { + v[i] = 0 } - dist >>= 7; - n = code; - for(code = n;code < zip_D_CODES;code++) { - zip_base_dist[code] = dist << 7; - for(n = 0;n < 1 << zip_extra_dbits[code] - 7;n++) { - zip_dist_code[256 + dist++] = code - } + for(i = 0;i < x.length;i++) { + x[i] = 0 } - for(bits = 0;bits <= zip_MAX_BITS;bits++) { - zip_bl_count[bits] = 0 + el = n > 256 ? b[256] : this.BMAX; + p = b; + pidx = 0; + i = n; + do { + c[p[pidx]]++; + pidx++ + }while(--i > 0); + if(c[0] === n) { + this.root = null; + this.m = 0; + this.status = 0; + return } - n = 0; - while(n <= 143) { - zip_static_ltree[n++].dl = 8; - zip_bl_count[8]++ + for(j = 1;j <= this.BMAX;j++) { + if(c[j] !== 0) { + break + } } - while(n <= 255) { - zip_static_ltree[n++].dl = 9; - zip_bl_count[9]++ + k = j; + if(mm < j) { + mm = j } - while(n <= 279) { - zip_static_ltree[n++].dl = 7; - zip_bl_count[7]++ + for(i = this.BMAX;i !== 0;i--) { + if(c[i] !== 0) { + break + } } - while(n <= 287) { - zip_static_ltree[n++].dl = 8; - zip_bl_count[8]++ + g = i; + if(mm > i) { + mm = i } - zip_gen_codes(zip_static_ltree, zip_L_CODES + 1); - for(n = 0;n < zip_D_CODES;n++) { - zip_static_dtree[n].dl = 5; - zip_static_dtree[n].fc = zip_bi_reverse(n, 5) + for(y = 1 << j;j < i;j++, y <<= 1) { + y -= c[j]; + if(y < 0) { + this.status = 2; + this.m = mm; + return + } } - zip_init_block() - }; - var zip_init_deflate = function() { - if(zip_eofile) { + y -= c[i]; + if(y < 0) { + this.status = 2; + this.m = mm; return } - zip_bi_buf = 0; - zip_bi_valid = 0; - zip_ct_init(); - zip_lm_init(); - zip_qhead = null; - zip_outcnt = 0; - zip_outoff = 0; - if(zip_compr_level <= 3) { - zip_prev_length = zip_MIN_MATCH - 1; - zip_match_length = 0 - }else { - zip_match_length = zip_MIN_MATCH - 1; - zip_match_available = 0 + c[i] += y; + x[1] = j = 0; + p = c; + pidx = 1; + xp = 2; + while(--i > 0) { + j += p[pidx++]; + x[xp++] = j } - zip_complete = false - }; - var zip_qcopy = function(buff, off, buff_size) { - var n, i, j, p; - n = 0; - while(zip_qhead !== null && n < buff_size) { - i = buff_size - n; - if(i > zip_qhead.len) { - i = zip_qhead.len - } - for(j = 0;j < i;j++) { - buff[off + n + j] = zip_qhead.ptr[zip_qhead.off + j] - } - zip_qhead.off += i; - zip_qhead.len -= i; - n += i; - if(zip_qhead.len === 0) { - p = zip_qhead; - zip_qhead = zip_qhead.next; - zip_reuse_queue(p) - } - } - if(n === buff_size) { - return n - } - if(zip_outoff < zip_outcnt) { - i = buff_size - n; - if(i > zip_outcnt - zip_outoff) { - i = zip_outcnt - zip_outoff - } - for(j = 0;j < i;j++) { - buff[off + n + j] = zip_outbuf[zip_outoff + j] - } - zip_outoff += i; - n += i; - if(zip_outcnt === zip_outoff) { - zip_outcnt = zip_outoff = 0 - } - } - return n - }; - var zip_deflate_internal = function(buff, off, buff_size) { - var n; - if(!zip_initflag) { - zip_init_deflate(); - zip_initflag = true; - if(zip_lookahead === 0) { - zip_complete = true; - return 0 - } - } - n = zip_qcopy(buff, off, buff_size); - if(n === buff_size) { - return buff_size - } - if(zip_complete) { - return n - } - if(zip_compr_level <= 3) { - zip_deflate_fast() - }else { - zip_deflate_better() - } - if(zip_lookahead === 0) { - if(zip_match_available !== 0) { - zip_ct_tally(0, zip_window[zip_strstart - 1] & 255) - } - zip_flush_block(1); - zip_complete = true - } - return n + zip_qcopy(buff, n + off, buff_size - n) - }; - var zip_deflate = function(str, level) { - var i, j; - zip_deflate_data = str; - zip_deflate_pos = 0; - if(String(typeof level) === "undefined") { - level = zip_DEFAULT_LEVEL - } - zip_deflate_start(level); - var buff = new Array(1024); - var aout = [], cbuf = []; - i = zip_deflate_internal(buff, 0, buff.length); - while(i > 0) { - cbuf.length = i; - for(j = 0;j < i;j++) { - cbuf[j] = String.fromCharCode(buff[j]) - } - aout[aout.length] = cbuf.join(""); - i = zip_deflate_internal(buff, 0, buff.length) - } - zip_deflate_data = ""; - return aout.join("") - }; - this.deflate = zip_deflate -}; -core.ByteArray = function ByteArray(data) { - this.pos = 0; - this.data = data; - this.readUInt32LE = function() { - this.pos += 4; - var d = this.data, pos = this.pos; - return d[--pos] << 24 | d[--pos] << 16 | d[--pos] << 8 | d[--pos] - }; - this.readUInt16LE = function() { - this.pos += 2; - var d = this.data, pos = this.pos; - return d[--pos] << 8 | d[--pos] - } -}; -core.ByteArrayWriter = function ByteArrayWriter(encoding) { - var self = this, length = 0, bufferSize = 1024, data = new Uint8Array(new ArrayBuffer(bufferSize)); - function expand(extraLength) { - var newData; - if(extraLength > bufferSize - length) { - bufferSize = Math.max(2 * bufferSize, length + extraLength); - newData = new Uint8Array(new ArrayBuffer(bufferSize)); - newData.set(data); - data = newData - } - } - this.appendByteArrayWriter = function(writer) { - self.appendByteArray(writer.getByteArray()) - }; - this.appendByteArray = function(array) { - var l = array.length; - expand(l); - data.set(array, length); - length += l - }; - this.appendArray = function(array) { - var l = array.length; - expand(l); - data.set(array, length); - length += l - }; - this.appendUInt16LE = function(value) { - self.appendArray([value & 255, value >> 8 & 255]) - }; - this.appendUInt32LE = function(value) { - self.appendArray([value & 255, value >> 8 & 255, value >> 16 & 255, value >> 24 & 255]) - }; - this.appendString = function(string) { - self.appendByteArray(runtime.byteArrayFromString(string, encoding)) - }; - this.getLength = function() { - return length - }; - this.getByteArray = function() { - var a = new Uint8Array(new ArrayBuffer(length)); - a.set(data.subarray(0, length)); - return a - } -}; -core.RawInflate = function RawInflate() { - var zip_WSIZE = 32768; - var zip_STORED_BLOCK = 0; - var zip_STATIC_TREES = 1; - var zip_DYN_TREES = 2; - var zip_lbits = 9; - var zip_dbits = 6; - var zip_INBUFSIZ = 32768; - var zip_INBUF_EXTRA = 64; - var zip_slide; - var zip_wp; - var zip_fixed_tl = null; - var zip_fixed_td; - var zip_fixed_bl, fixed_bd; - var zip_bit_buf; - var zip_bit_len; - var zip_method; - var zip_eof; - var zip_copy_leng; - var zip_copy_dist; - var zip_tl, zip_td; - var zip_bl, zip_bd; - var zip_inflate_data; - var zip_inflate_pos; - var zip_MASK_BITS = new Array(0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535); - var zip_cplens = new Array(3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0); - var zip_cplext = new Array(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99); - var zip_cpdist = new Array(1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577); - var zip_cpdext = new Array(0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13); - var zip_border = new Array(16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15); - var zip_HuftList = function() { - this.next = null; - this.list = null - }; - var zip_HuftNode = function() { - this.e = 0; - this.b = 0; - this.n = 0; - this.t = null - }; - var zip_HuftBuild = function(b, n, s, d, e, mm) { - this.BMAX = 16; - this.N_MAX = 288; - this.status = 0; - this.root = null; - this.m = 0; - var a; - var c = new Array(this.BMAX + 1); - var el; - var f; - var g; - var h; - var i; - var j; - var k; - var lx = new Array(this.BMAX + 1); - var p; - var pidx; - var q; - var r = new zip_HuftNode; - var u = new Array(this.BMAX); - var v = new Array(this.N_MAX); - var w; - var x = new Array(this.BMAX + 1); - var xp; - var y; - var z; - var o; - var tail; - tail = this.root = null; - for(i = 0;i < c.length;i++) { - c[i] = 0 - } - for(i = 0;i < lx.length;i++) { - lx[i] = 0 - } - for(i = 0;i < u.length;i++) { - u[i] = null - } - for(i = 0;i < v.length;i++) { - v[i] = 0 - } - for(i = 0;i < x.length;i++) { - x[i] = 0 - } - el = n > 256 ? b[256] : this.BMAX; - p = b; - pidx = 0; - i = n; - do { - c[p[pidx]]++; - pidx++ - }while(--i > 0); - if(c[0] == n) { - this.root = null; - this.m = 0; - this.status = 0; - return - } - for(j = 1;j <= this.BMAX;j++) { - if(c[j] != 0) { - break - } - } - k = j; - if(mm < j) { - mm = j - } - for(i = this.BMAX;i != 0;i--) { - if(c[i] != 0) { - break - } - } - g = i; - if(mm > i) { - mm = i - } - for(y = 1 << j;j < i;j++, y <<= 1) { - if((y -= c[j]) < 0) { - this.status = 2; - this.m = mm; - return - } - } - if((y -= c[i]) < 0) { - this.status = 2; - this.m = mm; - return - } - c[i] += y; - x[1] = j = 0; - p = c; - pidx = 1; - xp = 2; - while(--i > 0) { - x[xp++] = j += p[pidx++] - } - p = b; - pidx = 0; - i = 0; - do { - if((j = p[pidx++]) != 0) { - v[x[j]++] = i + p = b; + pidx = 0; + i = 0; + do { + j = p[pidx++]; + if(j !== 0) { + v[x[j]++] = i } }while(++i < n); n = x[g]; @@ -2490,18 +2290,23 @@ core.RawInflate = function RawInflate() { w = lx[0] = 0; q = null; z = 0; - for(;k <= g;k++) { + k -= 1; + for(k += 1;k <= g;k++) { a = c[k]; while(a-- > 0) { while(k > w + lx[1 + h]) { w += lx[1 + h]; h++; - z = (z = g - w) > mm ? mm : z; - if((f = 1 << (j = k - w)) > a + 1) { + z = g - w; + z = z > mm ? mm : z; + j = k - w; + f = 1 << j; + if(f > a + 1) { f -= a + 1; xp = k; while(++j < z) { - if((f <<= 1) <= c[++xp]) { + f <<= 1; + if(f <= c[++xp]) { break } f -= c[xp] @@ -2514,12 +2319,12 @@ core.RawInflate = function RawInflate() { lx[1 + h] = j; q = new Array(z); for(o = 0;o < z;o++) { - q[o] = new zip_HuftNode + q[o] = new core.zip_HuftNode } - if(tail == null) { - tail = this.root = new zip_HuftList + if(tail === null) { + tail = this.root = new core.zip_HuftList }else { - tail = tail.next = new zip_HuftList + tail = tail.next = new core.zip_HuftList } tail.next = null; tail.list = q; @@ -2532,7 +2337,7 @@ core.RawInflate = function RawInflate() { j = (i & (1 << w) - 1) >> w - lx[h]; u[h - 1][j].e = r.e; u[h - 1][j].b = r.b; - u[h - 1][j].n = r.n; + u[h - 1][j].n = (r).n; u[h - 1][j].t = r.t } } @@ -2552,46 +2357,44 @@ core.RawInflate = function RawInflate() { for(j = i >> w;j < z;j += f) { q[j].e = r.e; q[j].b = r.b; - q[j].n = r.n; + q[j].n = (r).n; q[j].t = r.t } - for(j = 1 << k - 1;(i & j) != 0;j >>= 1) { + for(j = 1 << k - 1;(i & j) !== 0;j >>= 1) { i ^= j } i ^= j; - while((i & (1 << w) - 1) != x[h]) { + while((i & (1 << w) - 1) !== x[h]) { w -= lx[h]; h-- } } } this.m = lx[1]; - this.status = y != 0 && g != 1 ? 1 : 0 - }; - var zip_GET_BYTE = function() { - if(zip_inflate_data.length == zip_inflate_pos) { + this.status = y !== 0 && g !== 1 ? 1 : 0 + } + function zip_GET_BYTE() { + if(zip_inflate_data.length === zip_inflate_pos) { return-1 } return zip_inflate_data[zip_inflate_pos++] - }; - var zip_NEEDBITS = function(n) { + } + function zip_NEEDBITS(n) { while(zip_bit_len < n) { zip_bit_buf |= zip_GET_BYTE() << zip_bit_len; zip_bit_len += 8 } - }; - var zip_GETBITS = function(n) { + } + function zip_GETBITS(n) { return zip_bit_buf & zip_MASK_BITS[n] - }; - var zip_DUMPBITS = function(n) { + } + function zip_DUMPBITS(n) { zip_bit_buf >>= n; zip_bit_len -= n - }; - var zip_inflate_codes = function(buff, off, size) { - var e; - var t; - var n; - if(size == 0) { + } + function zip_inflate_codes(buff, off, size) { + var e, t, n; + if(size === 0) { return 0 } n = 0; @@ -2600,7 +2403,7 @@ core.RawInflate = function RawInflate() { t = zip_tl.list[zip_GETBITS(zip_bl)]; e = t.e; while(e > 16) { - if(e == 99) { + if(e === 99) { return-1 } zip_DUMPBITS(t.b); @@ -2610,51 +2413,51 @@ core.RawInflate = function RawInflate() { e = t.e } zip_DUMPBITS(t.b); - if(e == 16) { + if(e === 16) { zip_wp &= zip_WSIZE - 1; buff[off + n++] = zip_slide[zip_wp++] = t.n; - if(n == size) { + if(n === size) { return size } - continue - } - if(e == 15) { - break - } - zip_NEEDBITS(e); - zip_copy_leng = t.n + zip_GETBITS(e); - zip_DUMPBITS(e); - zip_NEEDBITS(zip_bd); - t = zip_td.list[zip_GETBITS(zip_bd)]; - e = t.e; - while(e > 16) { - if(e == 99) { - return-1 + }else { + if(e === 15) { + break } - zip_DUMPBITS(t.b); - e -= 16; zip_NEEDBITS(e); - t = t.t[zip_GETBITS(e)]; - e = t.e - } - zip_DUMPBITS(t.b); - zip_NEEDBITS(e); - zip_copy_dist = zip_wp - t.n - zip_GETBITS(e); - zip_DUMPBITS(e); - while(zip_copy_leng > 0 && n < size) { - zip_copy_leng--; - zip_copy_dist &= zip_WSIZE - 1; - zip_wp &= zip_WSIZE - 1; - buff[off + n++] = zip_slide[zip_wp++] = zip_slide[zip_copy_dist++] - } - if(n == size) { - return size + zip_copy_leng = t.n + zip_GETBITS(e); + zip_DUMPBITS(e); + zip_NEEDBITS(zip_bd); + t = zip_td.list[zip_GETBITS(zip_bd)]; + e = t.e; + while(e > 16) { + if(e === 99) { + return-1 + } + zip_DUMPBITS(t.b); + e -= 16; + zip_NEEDBITS(e); + t = t.t[zip_GETBITS(e)]; + e = t.e + } + zip_DUMPBITS(t.b); + zip_NEEDBITS(e); + zip_copy_dist = zip_wp - t.n - zip_GETBITS(e); + zip_DUMPBITS(e); + while(zip_copy_leng > 0 && n < size) { + zip_copy_leng--; + zip_copy_dist &= zip_WSIZE - 1; + zip_wp &= zip_WSIZE - 1; + buff[off + n++] = zip_slide[zip_wp++] = zip_slide[zip_copy_dist++] + } + if(n === size) { + return size + } } } zip_method = -1; return n - }; - var zip_inflate_stored = function(buff, off, size) { + } + function zip_inflate_stored(buff, off, size) { var n; n = zip_bit_len & 7; zip_DUMPBITS(n); @@ -2662,7 +2465,7 @@ core.RawInflate = function RawInflate() { n = zip_GETBITS(16); zip_DUMPBITS(16); zip_NEEDBITS(16); - if(n != (~zip_bit_buf & 65535)) { + if(n !== (~zip_bit_buf & 65535)) { return-1 } zip_DUMPBITS(16); @@ -2675,32 +2478,32 @@ core.RawInflate = function RawInflate() { buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8); zip_DUMPBITS(8) } - if(zip_copy_leng == 0) { + if(zip_copy_leng === 0) { zip_method = -1 } return n - }; + } var zip_fixed_bd; - var zip_inflate_fixed = function(buff, off, size) { - if(zip_fixed_tl == null) { + function zip_inflate_fixed(buff, off, size) { + if(zip_fixed_tl === null) { var i; var l = new Array(288); var h; for(i = 0;i < 144;i++) { l[i] = 8 } - for(;i < 256;i++) { + for(i = 144;i < 256;i++) { l[i] = 9 } - for(;i < 280;i++) { + for(i = 256;i < 280;i++) { l[i] = 7 } - for(;i < 288;i++) { + for(i = 280;i < 288;i++) { l[i] = 8 } zip_fixed_bl = 7; - h = new zip_HuftBuild(l, 288, 257, zip_cplens, zip_cplext, zip_fixed_bl); - if(h.status != 0) { + h = new Zip_HuftBuild(l, 288, 257, zip_cplens, zip_cplext, zip_fixed_bl); + if(h.status !== 0) { alert("HufBuild error: " + h.status); return-1 } @@ -2710,7 +2513,7 @@ core.RawInflate = function RawInflate() { l[i] = 5 } zip_fixed_bd = 5; - h = new zip_HuftBuild(l, 30, 0, zip_cpdist, zip_cpdext, zip_fixed_bd); + h = new Zip_HuftBuild(l, 30, 0, zip_cpdist, zip_cpdext, zip_fixed_bd); if(h.status > 1) { zip_fixed_tl = null; alert("HufBuild error: " + h.status); @@ -2724,8 +2527,8 @@ core.RawInflate = function RawInflate() { zip_bl = zip_fixed_bl; zip_bd = zip_fixed_bd; return zip_inflate_codes(buff, off, size) - }; - var zip_inflate_dynamic = function(buff, off, size) { + } + function zip_inflate_dynamic(buff, off, size) { var i; var j; var l; @@ -2756,12 +2559,12 @@ core.RawInflate = function RawInflate() { ll[zip_border[j]] = zip_GETBITS(3); zip_DUMPBITS(3) } - for(;j < 19;j++) { + for(j = nb;j < 19;j++) { ll[zip_border[j]] = 0 } zip_bl = 7; - h = new zip_HuftBuild(ll, 19, 19, null, null, zip_bl); - if(h.status != 0) { + h = new Zip_HuftBuild(ll, 19, 19, null, null, zip_bl); + if(h.status !== 0) { return-1 } zip_tl = h.root; @@ -2777,7 +2580,7 @@ core.RawInflate = function RawInflate() { if(j < 16) { ll[i++] = l = j }else { - if(j == 16) { + if(j === 16) { zip_NEEDBITS(2); j = 3 + zip_GETBITS(2); zip_DUMPBITS(2); @@ -2788,7 +2591,7 @@ core.RawInflate = function RawInflate() { ll[i++] = l } }else { - if(j == 17) { + if(j === 17) { zip_NEEDBITS(3); j = 3 + zip_GETBITS(3); zip_DUMPBITS(3); @@ -2815,11 +2618,11 @@ core.RawInflate = function RawInflate() { } } zip_bl = zip_lbits; - h = new zip_HuftBuild(ll, nl, 257, zip_cplens, zip_cplext, zip_bl); - if(zip_bl == 0) { + h = new Zip_HuftBuild(ll, nl, 257, zip_cplens, zip_cplext, zip_bl); + if(zip_bl === 0) { h.status = 1 } - if(h.status != 0) { + if(h.status !== 0) { return-1 } zip_tl = h.root; @@ -2828,22 +2631,19 @@ core.RawInflate = function RawInflate() { ll[i] = ll[i + nl] } zip_bd = zip_dbits; - h = new zip_HuftBuild(ll, nd, 0, zip_cpdist, zip_cpdext, zip_bd); + h = new Zip_HuftBuild(ll, nd, 0, zip_cpdist, zip_cpdext, zip_bd); zip_td = h.root; zip_bd = h.m; - if(zip_bd == 0 && nl > 257) { + if(zip_bd === 0 && nl > 257) { return-1 } - if(h.status != 0) { + if(h.status !== 0) { return-1 } return zip_inflate_codes(buff, off, size) - }; - var zip_inflate_start = function() { - var i; - if(zip_slide == null) { - zip_slide = new Array(2 * zip_WSIZE) - } + } + function zip_inflate_start() { + zip_slide.length = 2 * zip_WSIZE; zip_wp = 0; zip_bit_buf = 0; zip_bit_len = 0; @@ -2851,44 +2651,48 @@ core.RawInflate = function RawInflate() { zip_eof = false; zip_copy_leng = zip_copy_dist = 0; zip_tl = null - }; - var zip_inflate_internal = function(buff, off, size) { - var n, i; - n = 0; + } + function zip_inflate_internal(buff, off, size) { + var n = 0, i; while(n < size) { - if(zip_eof && zip_method == -1) { + if(zip_eof && zip_method === -1) { return n } if(zip_copy_leng > 0) { - if(zip_method != zip_STORED_BLOCK) { + if(zip_method !== zip_STORED_BLOCK) { while(zip_copy_leng > 0 && n < size) { zip_copy_leng--; zip_copy_dist &= zip_WSIZE - 1; zip_wp &= zip_WSIZE - 1; - buff[off + n++] = zip_slide[zip_wp++] = zip_slide[zip_copy_dist++] + buff[off + n] = zip_slide[zip_wp] = zip_slide[zip_copy_dist]; + n += 1; + zip_wp += 1; + zip_copy_dist += 1 } }else { while(zip_copy_leng > 0 && n < size) { - zip_copy_leng--; + zip_copy_leng -= 1; zip_wp &= zip_WSIZE - 1; zip_NEEDBITS(8); - buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8); + buff[off + n] = zip_slide[zip_wp] = zip_GETBITS(8); + n += 1; + zip_wp += 1; zip_DUMPBITS(8) } - if(zip_copy_leng == 0) { + if(zip_copy_leng === 0) { zip_method = -1 } } - if(n == size) { + if(n === size) { return n } } - if(zip_method == -1) { + if(zip_method === -1) { if(zip_eof) { break } zip_NEEDBITS(1); - if(zip_GETBITS(1) != 0) { + if(zip_GETBITS(1) !== 0) { zip_eof = true } zip_DUMPBITS(1); @@ -2903,14 +2707,14 @@ core.RawInflate = function RawInflate() { i = zip_inflate_stored(buff, off + n, size - n); break; case 1: - if(zip_tl != null) { + if(zip_tl !== null) { i = zip_inflate_codes(buff, off + n, size - n) }else { i = zip_inflate_fixed(buff, off + n, size - n) } break; case 2: - if(zip_tl != null) { + if(zip_tl !== null) { i = zip_inflate_codes(buff, off + n, size - n) }else { i = zip_inflate_dynamic(buff, off + n, size - n) @@ -2920,7 +2724,7 @@ core.RawInflate = function RawInflate() { i = -1; break } - if(i == -1) { + if(i === -1) { if(zip_eof) { return 0 } @@ -2929,571 +2733,875 @@ core.RawInflate = function RawInflate() { n += i } return n - }; - var zip_inflate = function(data, size) { - var i, j; + } + function zip_inflate(data, size) { zip_inflate_start(); zip_inflate_data = data; zip_inflate_pos = 0; var buff = new Uint8Array(new ArrayBuffer(size)); zip_inflate_internal(buff, 0, size); - zip_inflate_data = null; + zip_inflate_data = new Uint8Array(new ArrayBuffer(0)); return buff - }; + } this.inflate = zip_inflate }; -/* - - Copyright (C) 2012 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -core.LoopWatchDog = function LoopWatchDog(timeout, maxChecks) { - var startTime = Date.now(), checks = 0; - function check() { - var t; - if(timeout) { - t = Date.now(); - if(t - startTime > timeout) { - runtime.log("alert", "watchdog timeout"); - throw"timeout!"; +core.ScheduledTask = function ScheduledTask(fn, delay) { + var timeoutId, scheduled = false, args = []; + function cancel() { + if(scheduled) { + runtime.clearTimeout(timeoutId); + scheduled = false + } + } + function execute() { + cancel(); + fn.apply(undefined, args); + args = null + } + this.trigger = function() { + args = Array.prototype.slice.call(arguments); + if(!scheduled) { + scheduled = true; + timeoutId = runtime.setTimeout(execute, delay) + } + }; + this.triggerImmediate = function() { + args = Array.prototype.slice.call(arguments); + execute() + }; + this.processRequests = function() { + if(scheduled) { + execute() + } + }; + this.cancel = cancel; + this.destroy = function(callback) { + cancel(); + callback() + } +}; +core.NamedFunction; +core.NamedAsyncFunction; +core.UnitTest = function UnitTest() { +}; +core.UnitTest.prototype.setUp = function() { +}; +core.UnitTest.prototype.tearDown = function() { +}; +core.UnitTest.prototype.description = function() { +}; +core.UnitTest.prototype.tests = function() { +}; +core.UnitTest.prototype.asyncTests = function() { +}; +core.UnitTest.provideTestAreaDiv = function() { + var maindoc = runtime.getWindow().document, testarea = maindoc.getElementById("testarea"); + runtime.assert(!testarea, 'Unclean test environment, found a div with id "testarea".'); + testarea = maindoc.createElement("div"); + testarea.setAttribute("id", "testarea"); + maindoc.body.appendChild(testarea); + return(testarea) +}; +core.UnitTest.cleanupTestAreaDiv = function() { + var maindoc = runtime.getWindow().document, testarea = maindoc.getElementById("testarea"); + runtime.assert(!!testarea && testarea.parentNode === maindoc.body, 'Test environment broken, found no div with id "testarea" below body.'); + maindoc.body.removeChild(testarea) +}; +core.UnitTest.createOdtDocument = function(xml, namespaceMap) { + var xmlDoc = ""; + xmlDoc += " 0) { - checks += 1; - if(checks > maxChecks) { - runtime.log("alert", "watchdog loop overflow"); - throw"loop overflow"; + return true + } + function areAttributesEqual(a, b, skipReverseCheck) { + var aatts = a.attributes, n = aatts.length, i, att, v; + for(i = 0;i < n;i += 1) { + att = (aatts.item(i)); + if(att.prefix !== "xmlns" && att.namespaceURI !== "urn:webodf:names:steps") { + v = b.getAttributeNS(att.namespaceURI, att.localName); + if(!b.hasAttributeNS(att.namespaceURI, att.localName)) { + testFailed("Attribute " + att.localName + " with value " + att.value + " was not present"); + return false + } + if(v !== att.value) { + testFailed("Attribute " + att.localName + " was " + v + " should be " + att.value); + return false + } } } + return skipReverseCheck ? true : areAttributesEqual(b, a, true) } - this.check = check -}; -core.Utils = function Utils() { - function hashString(value) { - var hash = 0, i, l; - for(i = 0, l = value.length;i < l;i += 1) { - hash = (hash << 5) - hash + value.charCodeAt(i); - hash |= 0 + function areNodesEqual(a, b) { + var an, bn, atype = a.nodeType, btype = b.nodeType; + if(atype !== btype) { + testFailed("Nodetype '" + atype + "' should be '" + btype + "'"); + return false } - return hash + if(atype === Node.TEXT_NODE) { + if((a).data === (b).data) { + return true + } + testFailed("Textnode data '" + (a).data + "' should be '" + (b).data + "'"); + return false + } + runtime.assert(atype === Node.ELEMENT_NODE, "Only textnodes and elements supported."); + if(a.namespaceURI !== b.namespaceURI) { + testFailed("namespace '" + a.namespaceURI + "' should be '" + b.namespaceURI + "'"); + return false + } + if(a.localName !== b.localName) { + testFailed("localName '" + a.localName + "' should be '" + b.localName + "'"); + return false + } + if(!areAttributesEqual((a), (b), false)) { + return false + } + an = a.firstChild; + bn = b.firstChild; + while(an) { + if(!bn) { + testFailed("Nodetype '" + an.nodeType + "' is unexpected here."); + return false + } + if(!areNodesEqual(an, bn)) { + return false + } + an = an.nextSibling; + bn = bn.nextSibling + } + if(bn) { + testFailed("Nodetype '" + bn.nodeType + "' is missing here."); + return false + } + return true } - this.hashString = hashString; - function mergeObjects(destination, source) { - if(source && Array.isArray(source)) { - destination = (destination || []).concat(source.map(function(obj) { - return mergeObjects({}, obj) - })) - }else { - if(source && typeof source === "object") { - destination = destination || {}; - Object.keys(source).forEach(function(p) { - destination[p] = mergeObjects(destination[p], source[p]) - }) - }else { - destination = source + function isResultCorrect(actual, expected) { + if(expected === 0) { + return actual === expected && 1 / actual === 1 / expected + } + if(actual === expected) { + return true + } + if(typeof expected === "number" && isNaN(expected)) { + return typeof actual === "number" && isNaN(actual) + } + if(Object.prototype.toString.call(expected) === Object.prototype.toString.call([])) { + return areArraysEqual((actual), (expected)) + } + if(typeof expected === "object" && typeof actual === "object") { + if((expected).constructor === Element || (expected).constructor === Node) { + return areNodesEqual((expected), (actual)) } + return areObjectsEqual((expected), (actual)) } - return destination + return false } - this.mergeObjects = mergeObjects -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -(function() { - var browserQuirks; - function getBrowserQuirks() { - var range, directBoundingRect, rangeBoundingRect, testContainer, testElement, detectedQuirks, window, document; - if(browserQuirks === undefined) { - window = runtime.getWindow(); - document = window && window.document; - browserQuirks = {rangeBCRIgnoresElementBCR:false, unscaledRangeClientRects:false}; - if(document) { - testContainer = document.createElement("div"); - testContainer.style.position = "absolute"; - testContainer.style.left = "-99999px"; - testContainer.style.transform = "scale(2)"; - testContainer.style["-webkit-transform"] = "scale(2)"; - testElement = document.createElement("div"); - testContainer.appendChild(testElement); - document.body.appendChild(testContainer); - range = document.createRange(); - range.selectNode(testElement); - browserQuirks.rangeBCRIgnoresElementBCR = range.getClientRects().length === 0; - testElement.appendChild(document.createTextNode("Rect transform test")); - directBoundingRect = testElement.getBoundingClientRect(); - rangeBoundingRect = range.getBoundingClientRect(); - browserQuirks.unscaledRangeClientRects = Math.abs(directBoundingRect.height - rangeBoundingRect.height) > 2; - range.detach(); - document.body.removeChild(testContainer); - detectedQuirks = Object.keys(browserQuirks).map(function(quirk) { - return quirk + ":" + String(browserQuirks[quirk]) - }).join(", "); - runtime.log("Detected browser quirks - " + detectedQuirks) - } + function stringify(v) { + if(v === 0 && 1 / v < 0) { + return"-0" } - return browserQuirks + return String(v) } - core.DomUtils = function DomUtils() { - var sharedRange = null; - function getSharedRange(doc) { - var range; - if(sharedRange) { - range = sharedRange + function shouldBe(t, a, b) { + if(typeof a !== "string" || typeof b !== "string") { + debug("WARN: shouldBe() expects string arguments") + } + var exception, av, bv; + try { + av = eval(a) + }catch(e) { + exception = e + } + bv = eval(b); + if(exception) { + testFailed(a + " should be " + bv + ". Threw exception " + exception) + }else { + if(isResultCorrect(av, bv)) { + testPassed(a + " is " + b) }else { - sharedRange = range = (doc.createRange()) + if(String(typeof av) === String(typeof bv)) { + testFailed(a + " should be " + bv + ". Was " + stringify(av) + ".") + }else { + testFailed(a + " should be " + bv + " (of type " + typeof bv + "). Was " + av + " (of type " + typeof av + ").") + } } - return range } - function findStablePoint(container, offset) { - var c = container; - if(offset < c.childNodes.length) { - c = c.childNodes.item(offset); - offset = 0; - while(c.firstChild) { - c = c.firstChild - } + } + function shouldBeNonNull(t, a) { + var exception, av; + try { + av = eval(a) + }catch(e) { + exception = e + } + if(exception) { + testFailed(a + " should be non-null. Threw exception " + exception) + }else { + if(av !== null) { + testPassed(a + " is non-null.") }else { - while(c.lastChild) { - c = c.lastChild; - offset = c.nodeType === Node.TEXT_NODE ? c.textContent.length : c.childNodes.length - } + testFailed(a + " should be non-null. Was " + av) } - return{container:c, offset:offset} } - function splitBoundaries(range) { - var modifiedNodes = [], end, splitStart, node, text; - if(range.startContainer.nodeType === Node.TEXT_NODE || range.endContainer.nodeType === Node.TEXT_NODE) { - end = range.endContainer && findStablePoint(range.endContainer, range.endOffset); - range.setEnd(end.container, end.offset); - node = range.endContainer; - if(range.endOffset !== 0 && node.nodeType === Node.TEXT_NODE) { - text = (node); - if(range.endOffset !== text.length) { - modifiedNodes.push(text.splitText(range.endOffset)); - modifiedNodes.push(text) - } - } - node = range.startContainer; - if(range.startOffset !== 0 && node.nodeType === Node.TEXT_NODE) { - text = (node); - if(range.startOffset !== text.length) { - splitStart = text.splitText(range.startOffset); - modifiedNodes.push(text); - modifiedNodes.push(splitStart); - range.setStart(splitStart, 0) - } - } + } + function shouldBeNull(t, a) { + shouldBe(t, a, "null") + } + areObjectsEqual = function(a, b) { + var akeys = Object.keys(a), bkeys = Object.keys(b); + akeys.sort(); + bkeys.sort(); + return areArraysEqual(akeys, bkeys) && Object.keys(a).every(function(key) { + var aval = a[key], bval = b[key]; + if(!isResultCorrect(aval, bval)) { + testFailed(aval + " should be " + bval + " for key " + key); + return false } - return modifiedNodes + return true + }) + }; + this.areNodesEqual = areNodesEqual; + this.shouldBeNull = shouldBeNull; + this.shouldBeNonNull = shouldBeNonNull; + this.shouldBe = shouldBe; + this.countFailedTests = function() { + return failedTests + }; + this.name = function(functions) { + var i, fname, nf = [], l = functions.length; + nf.length = l; + for(i = 0;i < l;i += 1) { + fname = Runtime.getFunctionName(functions[i]) || ""; + if(fname === "") { + throw"Found a function without a name."; + } + nf[i] = {f:functions[i], name:fname} } - this.splitBoundaries = splitBoundaries; - function containsRange(container, insideRange) { - return container.compareBoundaryPoints(Range.START_TO_START, insideRange) <= 0 && container.compareBoundaryPoints(Range.END_TO_END, insideRange) >= 0 + return nf + } +}; +core.UnitTester = function UnitTester() { + var failedTests = 0, results = {}; + function link(text, code) { + return"" + text + "" + } + this.runTests = function(TestClass, callback, testNames) { + var testName = Runtime.getFunctionName(TestClass) || "", tname, runner = new core.UnitTestRunner, test = new TestClass(runner), testResults = {}, i, t, tests, lastFailCount, inBrowser = runtime.type() === "BrowserRuntime"; + if(results.hasOwnProperty(testName)) { + runtime.log("Test " + testName + " has already run."); + return } - this.containsRange = containsRange; - function rangesIntersect(range1, range2) { - return range1.compareBoundaryPoints(Range.END_TO_START, range2) <= 0 && range1.compareBoundaryPoints(Range.START_TO_END, range2) >= 0 + if(inBrowser) { + runtime.log("Running " + link(testName, 'runSuite("' + testName + '");') + ": " + test.description() + "") + }else { + runtime.log("Running " + testName + ": " + test.description) } - this.rangesIntersect = rangesIntersect; - function getNodesInRange(range, nodeFilter) { - var document = range.startContainer.ownerDocument, elements = [], rangeRoot = range.commonAncestorContainer, root = (rangeRoot.nodeType === Node.TEXT_NODE ? rangeRoot.parentNode : rangeRoot), n, filterResult, treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ALL, nodeFilter, false); - treeWalker.currentNode = range.startContainer; - n = range.startContainer; - while(n) { - filterResult = nodeFilter(n); - if(filterResult === NodeFilter.FILTER_ACCEPT) { - elements.push(n) - }else { - if(filterResult === NodeFilter.FILTER_REJECT) { - break - } - } - n = n.parentNode - } - elements.reverse(); - n = treeWalker.nextNode(); - while(n) { - elements.push(n); - n = treeWalker.nextNode() + tests = test.tests(); + for(i = 0;i < tests.length;i += 1) { + t = tests[i].f; + tname = tests[i].name; + if(testNames.length && testNames.indexOf(tname) === -1) { + continue } - return elements - } - this.getNodesInRange = getNodesInRange; - function mergeTextNodes(node, nextNode) { - var mergedNode = null, text, nextText; - if(node.nodeType === Node.TEXT_NODE) { - text = (node); - if(text.length === 0) { - text.parentNode.removeChild(text); - if(nextNode.nodeType === Node.TEXT_NODE) { - mergedNode = nextNode - } - }else { - if(nextNode.nodeType === Node.TEXT_NODE) { - nextText = (nextNode); - text.appendData(nextText.data); - nextNode.parentNode.removeChild(nextNode) - } - mergedNode = node - } + if(inBrowser) { + runtime.log("Running " + link(tname, 'runTest("' + testName + '","' + tname + '")') + "") + }else { + runtime.log("Running " + tname) } - return mergedNode + lastFailCount = runner.countFailedTests(); + test.setUp(); + t(); + test.tearDown(); + testResults[tname] = lastFailCount === runner.countFailedTests() } - function normalizeTextNodes(node) { - if(node && node.nextSibling) { - node = mergeTextNodes(node, node.nextSibling) - } - if(node && node.previousSibling) { - mergeTextNodes(node.previousSibling, node) + function runAsyncTests(todo) { + if(todo.length === 0) { + results[testName] = testResults; + failedTests += runner.countFailedTests(); + callback(); + return } + t = todo[0].f; + var fname = todo[0].name; + runtime.log("Running " + fname); + lastFailCount = runner.countFailedTests(); + test.setUp(); + t(function() { + test.tearDown(); + testResults[fname] = lastFailCount === runner.countFailedTests(); + runAsyncTests(todo.slice(1)) + }) } - this.normalizeTextNodes = normalizeTextNodes; - function rangeContainsNode(limits, node) { - var range = node.ownerDocument.createRange(), nodeRange = node.ownerDocument.createRange(), result; - range.setStart(limits.startContainer, limits.startOffset); - range.setEnd(limits.endContainer, limits.endOffset); - nodeRange.selectNodeContents(node); - result = containsRange(range, nodeRange); - range.detach(); - nodeRange.detach(); - return result - } - this.rangeContainsNode = rangeContainsNode; - function mergeIntoParent(targetNode) { - var parent = targetNode.parentNode; - while(targetNode.firstChild) { - parent.insertBefore(targetNode.firstChild, targetNode) - } - parent.removeChild(targetNode); - return parent + runAsyncTests(test.asyncTests()) + }; + this.countFailedTests = function() { + return failedTests + }; + this.results = function() { + return results + } +}; +core.Utils = function Utils() { + function hashString(value) { + var hash = 0, i, l; + for(i = 0, l = value.length;i < l;i += 1) { + hash = (hash << 5) - hash + value.charCodeAt(i); + hash |= 0 } - this.mergeIntoParent = mergeIntoParent; - function removeUnwantedNodes(targetNode, shouldRemove) { - var parent = targetNode.parentNode, node = targetNode.firstChild, next; - while(node) { - next = node.nextSibling; - removeUnwantedNodes(node, shouldRemove); - node = next + return hash + } + this.hashString = hashString; + var mergeObjects; + function mergeItems(destination, source) { + if(source && Array.isArray(source)) { + destination = destination || []; + if(!Array.isArray(destination)) { + throw"Destination is not an array."; } - if(shouldRemove(targetNode)) { - parent = mergeIntoParent(targetNode) + destination = (destination).concat((source).map(function(obj) { + return mergeItems(null, obj) + })) + }else { + if(source && typeof source === "object") { + destination = destination || {}; + if(typeof destination !== "object") { + throw"Destination is not an object."; + } + Object.keys((source)).forEach(function(p) { + destination[p] = mergeItems(destination[p], source[p]) + }) + }else { + destination = source } - return parent - } - this.removeUnwantedNodes = removeUnwantedNodes; - function getElementsByTagNameNS(node, namespace, tagName) { - return Array.prototype.slice.call(node.getElementsByTagNameNS(namespace, tagName)) - } - this.getElementsByTagNameNS = getElementsByTagNameNS; - function rangeIntersectsNode(range, node) { - var nodeRange = node.ownerDocument.createRange(), result; - nodeRange.selectNodeContents(node); - result = rangesIntersect(range, nodeRange); - nodeRange.detach(); - return result - } - 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 getPositionInContainingNode(node, container) { - var offset = 0, n; - while(node.parentNode !== container) { - runtime.assert(node.parentNode !== null, "parent is null"); - node = (node.parentNode) - } - n = container.firstChild; - while(n !== node) { - offset += 1; - n = n.nextSibling - } - return offset + return destination + } + mergeObjects = function(destination, source) { + Object.keys(source).forEach(function(p) { + destination[p] = mergeItems(destination[p], source[p]) + }); + return destination + }; + this.mergeObjects = mergeObjects +}; +/* + + WebODF + Copyright (c) 2010 Jos van den Oever + Licensed under the ... License: + + Project home: http://www.webodf.org/ +*/ +runtime.loadClass("core.RawInflate"); +runtime.loadClass("core.ByteArray"); +runtime.loadClass("core.ByteArrayWriter"); +runtime.loadClass("core.Base64"); +core.Zip = function Zip(url, entriesReadCallback) { + var entries, filesize, nEntries, inflate = (new core.RawInflate).inflate, zip = this, base64 = new core.Base64; + function crc32(data) { + var table = [0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615, 3915621685, 2657392035, 249268274, 2044508324, 3772115230, 2547177864, 162941995, 2125561021, 3887607047, 2428444049, 498536548, 1789927666, 4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639, 325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465, 4195302755, 2366115317, 997073096, 1281953886, 3579855332, 2724688242, 1006888145, 1258607687, 3524101629, 2768942443, 901097722, 1119000684, + 3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665, 651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731, 3485111705, 3099436303, 671266974, 1594198024, 3322730930, 2970347812, 795835527, 1483230225, 3244367275, 3060149565, 1994146192, 31158534, 2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059, 2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813, 2439277719, 3865271297, 1802195444, 476864866, 2238001368, 4066508878, 1812370925, + 453092731, 2181625025, 4111451223, 1706088902, 314042704, 2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405, 1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311, 2765210733, 3554079995, 1131014506, 879679996, 2909243462, 3663771856, 1141124467, 855842277, 2852801631, 3708648649, 1342533948, 654459306, 3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015, 1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873, 3082640443, 3233442989, + 3988292384, 2596254646, 62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523, 3814918930, 2489596804, 225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377, 4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355, 426522225, 1852507879, 4275313526, 2312317920, 282753626, 1742555852, 4189708143, 2394877945, 397917763, 1622183637, 3604390888, 2714866558, 953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859, 3624741850, 2936675148, 906185462, + 1090812512, 3747672003, 2825379669, 829329135, 1181335161, 3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3138078467, 570562233, 1426400815, 3317316542, 2998733608, 733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221, 2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151, 1913087877, 83908371, 2512341634, 3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2094854071, 198958881, 2262029012, 4057260610, 1759359992, 534414190, 2176718541, 4139329115, + 1873836001, 414664567, 2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745, 1634467795, 376229701, 2685067896, 3608007406, 1308918612, 956543938, 2808555105, 3495958263, 1231636301, 1047427035, 2932959818, 3654703836, 1088359270, 936918E3, 2847714899, 3736837829, 1202900863, 817233897, 3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203, 1423857449, 601450431, 3009837614, 3294710456, 1567103746, 711928724, 3020668471, 3272380065, 1510334235, 755167117], crc = + 0, i, iTop = data.length, x = 0, y = 0; + crc = crc ^ -1; + for(i = 0;i < iTop;i += 1) { + y = (crc ^ data[i]) & 255; + x = table[y]; + crc = crc >>> 8 ^ x } - function comparePoints(c1, o1, c2, o2) { - if(c1 === c2) { - return o2 - o1 + return crc ^ -1 + } + function dosTime2Date(dostime) { + var year = (dostime >> 25 & 127) + 1980, month = (dostime >> 21 & 15) - 1, mday = dostime >> 16 & 31, hour = dostime >> 11 & 15, min = dostime >> 5 & 63, sec = (dostime & 31) << 1, d = new Date(year, month, mday, hour, min, sec); + return d + } + function date2DosTime(date) { + var y = date.getFullYear(); + return y < 1980 ? 0 : y - 1980 << 25 | date.getMonth() + 1 << 21 | date.getDate() << 16 | date.getHours() << 11 | date.getMinutes() << 5 | date.getSeconds() >> 1 + } + function ZipEntry(url, stream) { + var sig, namelen, extralen, commentlen, compressionMethod, compressedSize, uncompressedSize, offset, entry = this; + function handleEntryData(data, callback) { + var estream = new core.ByteArray(data), esig = estream.readUInt32LE(), filenamelen, eextralen; + if(esig !== 67324752) { + callback("File entry signature is wrong." + esig.toString() + " " + data.length.toString(), null); + return } - var comparison = c1.compareDocumentPosition(c2); - if(comparison === 2) { - comparison = -1 - }else { - if(comparison === 4) { - comparison = 1 - }else { - if(comparison === 10) { - o1 = getPositionInContainingNode(c1, c2); - comparison = o1 < o2 ? 1 : -1 - }else { - o2 = getPositionInContainingNode(c2, c1); - comparison = o2 < o1 ? -1 : 1 - } + estream.pos += 22; + filenamelen = estream.readUInt16LE(); + eextralen = estream.readUInt16LE(); + estream.pos += filenamelen + eextralen; + if(compressionMethod) { + data = data.subarray(estream.pos, estream.pos + compressedSize); + if(compressedSize !== data.length) { + callback("The amount of compressed bytes read was " + data.length.toString() + " instead of " + compressedSize.toString() + " for " + entry.filename + " in " + url + ".", null); + return } + data = inflate(data, uncompressedSize) + }else { + data = data.subarray(estream.pos, estream.pos + uncompressedSize) } - return comparison - } - this.comparePoints = comparePoints; - function adaptRangeDifferenceToZoomLevel(inputNumber, zoomLevel) { - if(getBrowserQuirks().unscaledRangeClientRects) { - return inputNumber + if(uncompressedSize !== data.length) { + callback("The amount of bytes read was " + data.length.toString() + " instead of " + uncompressedSize.toString() + " for " + entry.filename + " in " + url + ".", null); + return } - return inputNumber / zoomLevel + entry.data = data; + callback(null, data) } - this.adaptRangeDifferenceToZoomLevel = adaptRangeDifferenceToZoomLevel; - function getBoundingClientRect(node) { - var doc = (node.ownerDocument), quirks = getBrowserQuirks(), range, element; - if(quirks.unscaledRangeClientRects === false || quirks.rangeBCRIgnoresElementBCR) { - if(node.nodeType === Node.ELEMENT_NODE) { - element = (node); - return element.getBoundingClientRect() - } + function load(callback) { + if(entry.data !== null) { + callback(null, entry.data); + return } - range = getSharedRange(doc); - range.selectNode(node); - return range.getBoundingClientRect() - } - this.getBoundingClientRect = getBoundingClientRect; - function mapKeyValObjOntoNode(node, properties, nsResolver) { - Object.keys(properties).forEach(function(key) { - var parts = key.split(":"), prefix = parts[0], localName = parts[1], ns = nsResolver(prefix), value = properties[key], element; - if(ns) { - element = node.getElementsByTagNameNS(ns, localName)[0]; - if(!element) { - element = node.ownerDocument.createElementNS(ns, key); - node.appendChild(element) - } - element.textContent = value + var size = compressedSize + 34 + namelen + extralen + 256; + if(size + offset > filesize) { + size = filesize - offset + } + runtime.read(url, offset, size, function(err, data) { + if(err || data === null) { + callback(err, data) }else { - runtime.log("Key ignored: " + key) + handleEntryData(data, callback) } }) } - this.mapKeyValObjOntoNode = mapKeyValObjOntoNode; - function removeKeyElementsFromNode(node, propertyNames, nsResolver) { - propertyNames.forEach(function(propertyName) { - var parts = propertyName.split(":"), prefix = parts[0], localName = parts[1], ns = nsResolver(prefix), element; - if(ns) { - element = node.getElementsByTagNameNS(ns, localName)[0]; - if(element) { - element.parentNode.removeChild(element) - }else { - runtime.log("Element for " + propertyName + " not found.") - } - }else { - runtime.log("Property Name ignored: " + propertyName) - } - }) + this.load = load; + function set(filename, data, compressed, date) { + entry.filename = filename; + entry.data = data; + entry.compressed = compressed; + entry.date = date } - this.removeKeyElementsFromNode = removeKeyElementsFromNode; - function getKeyValRepresentationOfNode(node, prefixResolver) { - var properties = {}, currentSibling = node.firstElementChild, prefix; - while(currentSibling) { - prefix = prefixResolver(currentSibling.namespaceURI); - if(prefix) { - properties[prefix + ":" + currentSibling.localName] = currentSibling.textContent - } - currentSibling = currentSibling.nextElementSibling - } - return properties + this.set = set; + this.error = null; + if(!stream) { + return } - this.getKeyValRepresentationOfNode = getKeyValRepresentationOfNode; - function mapObjOntoNode(node, properties, nsResolver) { - Object.keys(properties).forEach(function(key) { - var parts = key.split(":"), prefix = parts[0], localName = parts[1], ns = nsResolver(prefix), value = properties[key], element; - if(typeof value === "object" && Object.keys(value).length) { - element = node.getElementsByTagNameNS(ns, localName)[0] || node.ownerDocument.createElementNS(ns, key); - node.appendChild(element); - mapObjOntoNode(element, value, nsResolver) - }else { - if(ns) { - node.setAttributeNS(ns, key, value) - } - } - }) + sig = stream.readUInt32LE(); + if(sig !== 33639248) { + this.error = "Central directory entry has wrong signature at position " + (stream.pos - 4).toString() + ' for file "' + url + '": ' + stream.data.length.toString(); + return } - this.mapObjOntoNode = mapObjOntoNode; - function init(self) { - var appVersion, webKitOrSafari, ie, window = runtime.getWindow(); - if(window === null) { + stream.pos += 6; + compressionMethod = stream.readUInt16LE(); + this.date = dosTime2Date(stream.readUInt32LE()); + stream.readUInt32LE(); + compressedSize = stream.readUInt32LE(); + uncompressedSize = stream.readUInt32LE(); + namelen = stream.readUInt16LE(); + extralen = stream.readUInt16LE(); + commentlen = stream.readUInt16LE(); + stream.pos += 8; + offset = stream.readUInt32LE(); + this.filename = runtime.byteArrayToString(stream.data.subarray(stream.pos, stream.pos + namelen), "utf8"); + this.data = null; + stream.pos += namelen + extralen + commentlen + } + function handleCentralDirectory(data, callback) { + var stream = new core.ByteArray(data), i, e; + entries = []; + for(i = 0;i < nEntries;i += 1) { + e = new ZipEntry(url, stream); + if(e.error) { + callback(e.error, zip); return } - appVersion = window.navigator.appVersion.toLowerCase(); - webKitOrSafari = appVersion.indexOf("chrome") === -1 && (appVersion.indexOf("applewebkit") !== -1 || appVersion.indexOf("safari") !== -1); - ie = appVersion.indexOf("msie"); - if(webKitOrSafari || ie) { - self.containsNode = containsNodeForBrokenWebKit + entries[entries.length] = e + } + callback(null, zip) + } + function handleCentralDirectoryEnd(data, callback) { + if(data.length !== 22) { + callback("Central directory length should be 22.", zip); + return + } + var stream = new core.ByteArray(data), sig, disk, cddisk, diskNEntries, cdsSize, cdsOffset; + sig = stream.readUInt32LE(); + if(sig !== 101010256) { + callback("Central directory signature is wrong: " + sig.toString(), zip); + return + } + disk = stream.readUInt16LE(); + if(disk !== 0) { + callback("Zip files with non-zero disk numbers are not supported.", zip); + return + } + cddisk = stream.readUInt16LE(); + if(cddisk !== 0) { + callback("Zip files with non-zero disk numbers are not supported.", zip); + return + } + diskNEntries = stream.readUInt16LE(); + nEntries = stream.readUInt16LE(); + if(diskNEntries !== nEntries) { + callback("Number of entries is inconsistent.", zip); + return + } + cdsSize = stream.readUInt32LE(); + cdsOffset = stream.readUInt16LE(); + cdsOffset = filesize - 22 - cdsSize; + runtime.read(url, cdsOffset, filesize - cdsOffset, function(err, data) { + if(err || data === null) { + callback(err, zip) + }else { + handleCentralDirectory(data, callback) + } + }) + } + function load(filename, callback) { + var entry = null, e, i; + for(i = 0;i < entries.length;i += 1) { + e = entries[i]; + if(e.filename === filename) { + entry = e; + break } } - init(this) - }; - return core.DomUtils -})(); -runtime.loadClass("core.DomUtils"); -core.Cursor = function Cursor(document, memberId) { - var cursorns = "urn:webodf:names:cursor", cursorNode = document.createElementNS(cursorns, "cursor"), anchorNode = document.createElementNS(cursorns, "anchor"), forwardSelection, recentlyModifiedNodes = [], selectedRange = null, isCollapsed, domUtils = new core.DomUtils; - function putIntoTextNode(node, container, offset) { - runtime.assert(Boolean(container), "putCursorIntoTextNode: invalid container"); - var parent = container.parentNode; - runtime.assert(Boolean(parent), "putCursorIntoTextNode: container without parent"); - runtime.assert(offset >= 0 && offset <= container.length, "putCursorIntoTextNode: offset is out of bounds"); - if(offset === 0) { - parent.insertBefore(node, container) - }else { - if(offset === container.length) { - parent.insertBefore(node, container.nextSibling) + if(entry) { + if(entry.data) { + callback(null, entry.data) }else { - container.splitText(offset); - parent.insertBefore(node, container.nextSibling) + entry.load(callback) } + }else { + callback(filename + " not found.", null) } } - function removeNode(node) { - if(node.parentNode) { - recentlyModifiedNodes.push(node.previousSibling); - recentlyModifiedNodes.push(node.nextSibling); - node.parentNode.removeChild(node) - } + function loadAsString(filename, callback) { + load(filename, function(err, data) { + if(err || data === null) { + return callback(err, null) + } + var d = runtime.byteArrayToString(data, "utf8"); + callback(null, d) + }) } - function putNode(node, container, offset) { - if(container.nodeType === Node.TEXT_NODE) { - putIntoTextNode(node, (container), offset) - }else { - if(container.nodeType === Node.ELEMENT_NODE) { - container.insertBefore(node, container.childNodes.item(offset)) + function loadContentXmlAsFragments(filename, handler) { + zip.loadAsString(filename, function(err, data) { + if(err) { + return handler.rootElementReady(err) } - } - recentlyModifiedNodes.push(node.previousSibling); - recentlyModifiedNodes.push(node.nextSibling) + handler.rootElementReady(null, data, true) + }) } - function getStartNode() { - return forwardSelection ? anchorNode : cursorNode + function loadAsDataURL(filename, mimetype, callback) { + load(filename, function(err, data) { + if(err || !data) { + return callback(err, null) + } + var p = data, chunksize = 45E3, i = 0, dataurl; + if(!mimetype) { + if(p[1] === 80 && (p[2] === 78 && p[3] === 71)) { + mimetype = "image/png" + }else { + if(p[0] === 255 && (p[1] === 216 && p[2] === 255)) { + mimetype = "image/jpeg" + }else { + if(p[0] === 71 && (p[1] === 73 && p[2] === 70)) { + mimetype = "image/gif" + }else { + mimetype = "" + } + } + } + } + dataurl = "data:" + mimetype + ";base64,"; + while(i < data.length) { + dataurl += base64.convertUTF8ArrayToBase64(p.subarray(i, Math.min(i + chunksize, p.length))); + i += chunksize + } + callback(null, dataurl) + }) } - function getEndNode() { - return forwardSelection ? cursorNode : anchorNode + function loadAsDOM(filename, callback) { + zip.loadAsString(filename, function(err, xmldata) { + if(err || xmldata === null) { + callback(err, null); + return + } + var parser = new DOMParser, dom = parser.parseFromString(xmldata, "text/xml"); + callback(null, dom) + }) } - this.getNode = function() { - return cursorNode - }; - this.getAnchorNode = function() { - return anchorNode.parentNode ? anchorNode : cursorNode - }; - this.getSelectedRange = function() { - if(isCollapsed) { - selectedRange.setStartBefore(cursorNode); - selectedRange.collapse(true) - }else { - selectedRange.setStartAfter(getStartNode()); - selectedRange.setEndBefore(getEndNode()) + function save(filename, data, compressed, date) { + var i, entry; + for(i = 0;i < entries.length;i += 1) { + entry = entries[i]; + if(entry.filename === filename) { + entry.set(filename, data, compressed, date); + return + } } - return selectedRange - }; - this.setSelectedRange = function(range, isForwardSelection) { - if(selectedRange && selectedRange !== range) { - selectedRange.detach() + entry = new ZipEntry(url); + entry.set(filename, data, compressed, date); + entries.push(entry) + } + function remove(filename) { + var i, entry; + for(i = 0;i < entries.length;i += 1) { + entry = entries[i]; + if(entry.filename === filename) { + entries.splice(i, 1); + return true + } } - selectedRange = range; - forwardSelection = isForwardSelection !== false; - isCollapsed = range.collapsed; - if(range.collapsed) { - removeNode(anchorNode); - removeNode(cursorNode); - putNode(cursorNode, (range.startContainer), range.startOffset) - }else { - removeNode(anchorNode); - removeNode(cursorNode); - putNode(getEndNode(), (range.endContainer), range.endOffset); - putNode(getStartNode(), (range.startContainer), range.startOffset) + return false + } + function writeEntry(entry) { + var data = new core.ByteArrayWriter("utf8"), length = 0; + data.appendArray([80, 75, 3, 4, 20, 0, 0, 0, 0, 0]); + if(entry.data) { + length = entry.data.length } - recentlyModifiedNodes.forEach(domUtils.normalizeTextNodes); - recentlyModifiedNodes.length = 0 + data.appendUInt32LE(date2DosTime(entry.date)); + data.appendUInt32LE(entry.data ? crc32(entry.data) : 0); + data.appendUInt32LE(length); + data.appendUInt32LE(length); + data.appendUInt16LE(entry.filename.length); + data.appendUInt16LE(0); + data.appendString(entry.filename); + if(entry.data) { + data.appendByteArray(entry.data) + } + return data + } + function writeCODEntry(entry, offset) { + var data = new core.ByteArrayWriter("utf8"), length = 0; + data.appendArray([80, 75, 1, 2, 20, 0, 20, 0, 0, 0, 0, 0]); + if(entry.data) { + length = entry.data.length + } + data.appendUInt32LE(date2DosTime(entry.date)); + data.appendUInt32LE(entry.data ? crc32(entry.data) : 0); + data.appendUInt32LE(length); + data.appendUInt32LE(length); + data.appendUInt16LE(entry.filename.length); + data.appendArray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + data.appendUInt32LE(offset); + data.appendString(entry.filename); + return data + } + function loadAllEntries(position, callback) { + if(position === entries.length) { + callback(null); + return + } + var entry = entries[position]; + if(entry.data !== null) { + loadAllEntries(position + 1, callback); + return + } + entry.load(function(err) { + if(err) { + callback(err); + return + } + loadAllEntries(position + 1, callback) + }) + } + function createByteArray(successCallback, errorCallback) { + loadAllEntries(0, function(err) { + if(err) { + errorCallback(err); + return + } + var i, e, codoffset, codsize, data = new core.ByteArrayWriter("utf8"), offsets = [0]; + for(i = 0;i < entries.length;i += 1) { + data.appendByteArrayWriter(writeEntry(entries[i])); + offsets.push(data.getLength()) + } + codoffset = data.getLength(); + for(i = 0;i < entries.length;i += 1) { + e = entries[i]; + data.appendByteArrayWriter(writeCODEntry(e, offsets[i])) + } + codsize = data.getLength() - codoffset; + data.appendArray([80, 75, 5, 6, 0, 0, 0, 0]); + data.appendUInt16LE(entries.length); + data.appendUInt16LE(entries.length); + data.appendUInt32LE(codsize); + data.appendUInt32LE(codoffset); + data.appendArray([0, 0]); + successCallback(data.getByteArray()) + }) + } + function writeAs(newurl, callback) { + createByteArray(function(data) { + runtime.writeFile(newurl, data, callback) + }, callback) + } + function write(callback) { + writeAs(url, callback) + } + this.load = load; + this.save = save; + this.remove = remove; + this.write = write; + this.writeAs = writeAs; + this.createByteArray = createByteArray; + this.loadContentXmlAsFragments = loadContentXmlAsFragments; + this.loadAsString = loadAsString; + this.loadAsDOM = loadAsDOM; + this.loadAsDataURL = loadAsDataURL; + this.getEntries = function() { + return entries.slice() }; - this.hasForwardSelection = function() { - return forwardSelection + filesize = -1; + if(entriesReadCallback === null) { + entries = []; + return + } + runtime.getFileSize(url, function(size) { + filesize = size; + if(filesize < 0) { + entriesReadCallback("File '" + url + "' cannot be read.", zip) + }else { + runtime.read(url, filesize - 22, 22, function(err, data) { + if(err || (entriesReadCallback === null || data === null)) { + entriesReadCallback(err, zip) + }else { + handleCentralDirectoryEnd(data, entriesReadCallback) + } + }) + } + }) +}; +gui.Avatar = function Avatar(parentElement, avatarInitiallyVisible) { + var self = this, handle, image, pendingImageUrl, displayShown = "block", displayHidden = "none"; + this.setColor = function(color) { + image.style.borderColor = color }; - this.remove = function() { - removeNode(cursorNode); - recentlyModifiedNodes.forEach(domUtils.normalizeTextNodes); - recentlyModifiedNodes.length = 0 + this.setImageUrl = function(url) { + if(self.isVisible()) { + image.src = url + }else { + pendingImageUrl = url + } + }; + this.isVisible = function() { + return handle.style.display === displayShown + }; + this.show = function() { + if(pendingImageUrl) { + image.src = pendingImageUrl; + pendingImageUrl = undefined + } + handle.style.display = displayShown + }; + this.hide = function() { + handle.style.display = displayHidden + }; + this.markAsFocussed = function(isFocussed) { + handle.className = isFocussed ? "active" : "" + }; + this.destroy = function(callback) { + parentElement.removeChild(handle); + callback() }; function init() { - cursorNode.setAttributeNS(cursorns, "memberId", memberId); - anchorNode.setAttributeNS(cursorns, "memberId", memberId) + var document = (parentElement.ownerDocument), htmlns = document.documentElement.namespaceURI; + handle = (document.createElementNS(htmlns, "div")); + image = (document.createElementNS(htmlns, "img")); + image.width = 64; + image.height = 64; + handle.appendChild(image); + handle.style.width = "64px"; + handle.style.height = "70px"; + handle.style.position = "absolute"; + handle.style.top = "-80px"; + handle.style.left = "-34px"; + handle.style.display = avatarInitiallyVisible ? displayShown : displayHidden; + parentElement.appendChild(handle) + } + init() +}; +gui.EditInfoHandle = function EditInfoHandle(parentElement) { + var edits = [], handle, document = (parentElement.ownerDocument), htmlns = document.documentElement.namespaceURI, editinfons = "urn:webodf:names:editinfo"; + function renderEdits() { + var i, infoDiv, colorSpan, authorSpan, timeSpan; + handle.innerHTML = ""; + for(i = 0;i < edits.length;i += 1) { + infoDiv = document.createElementNS(htmlns, "div"); + infoDiv.className = "editInfo"; + colorSpan = document.createElementNS(htmlns, "span"); + colorSpan.className = "editInfoColor"; + colorSpan.setAttributeNS(editinfons, "editinfo:memberid", edits[i].memberid); + authorSpan = document.createElementNS(htmlns, "span"); + authorSpan.className = "editInfoAuthor"; + authorSpan.setAttributeNS(editinfons, "editinfo:memberid", edits[i].memberid); + timeSpan = document.createElementNS(htmlns, "span"); + timeSpan.className = "editInfoTime"; + timeSpan.setAttributeNS(editinfons, "editinfo:memberid", edits[i].memberid); + timeSpan.innerHTML = edits[i].time; + infoDiv.appendChild(colorSpan); + infoDiv.appendChild(authorSpan); + infoDiv.appendChild(timeSpan); + handle.appendChild(infoDiv) + } + } + this.setEdits = function(editArray) { + edits = editArray; + renderEdits() + }; + this.show = function() { + handle.style.display = "block" + }; + this.hide = function() { + handle.style.display = "none" + }; + this.destroy = function(callback) { + parentElement.removeChild(handle); + callback() + }; + function init() { + handle = (document.createElementNS(htmlns, "div")); + handle.setAttribute("class", "editInfoHandle"); + handle.style.display = "none"; + parentElement.appendChild(handle) } init() }; /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -3528,2708 +3636,2666 @@ core.Cursor = function Cursor(document, memberId) { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -core.EventNotifier = function EventNotifier(eventIds) { - var eventListener = {}; - this.emit = function(eventId, args) { - var i, subscribers; - runtime.assert(eventListener.hasOwnProperty(eventId), 'unknown event fired "' + eventId + '"'); - subscribers = eventListener[eventId]; - for(i = 0;i < subscribers.length;i += 1) { - subscribers[i](args) +gui.KeyboardHandler = function KeyboardHandler() { + var modifier = gui.KeyboardHandler.Modifier, defaultBinding = null, bindings = {}; + function getModifiers(e) { + var modifiers = modifier.None; + if(e.metaKey) { + modifiers |= modifier.Meta } - }; - this.subscribe = function(eventId, cb) { - runtime.assert(eventListener.hasOwnProperty(eventId), 'tried to subscribe to unknown event "' + eventId + '"'); - eventListener[eventId].push(cb); - runtime.log('event "' + eventId + '" subscribed.') - }; - this.unsubscribe = function(eventId, cb) { - var cbIndex; - runtime.assert(eventListener.hasOwnProperty(eventId), 'tried to unsubscribe from unknown event "' + eventId + '"'); - cbIndex = eventListener[eventId].indexOf(cb); - runtime.assert(cbIndex !== -1, 'tried to unsubscribe unknown callback from event "' + eventId + '"'); - if(cbIndex !== -1) { - eventListener[eventId].splice(cbIndex, 1) + if(e.ctrlKey) { + modifiers |= modifier.Ctrl } - runtime.log('event "' + eventId + '" unsubscribed.') - }; - function init() { - var i, eventId; - for(i = 0;i < eventIds.length;i += 1) { - eventId = eventIds[i]; - runtime.assert(!eventListener.hasOwnProperty(eventId), 'Duplicated event ids: "' + eventId + '" registered more than once.'); - eventListener[eventId] = [] + if(e.altKey) { + modifiers |= modifier.Alt + } + if(e.shiftKey) { + modifiers |= modifier.Shift + } + return modifiers + } + function getKeyCombo(keyCode, modifiers) { + if(!modifiers) { + modifiers = modifier.None + } + return keyCode + ":" + modifiers + } + this.setDefault = function(callback) { + defaultBinding = callback + }; + this.bind = function(keyCode, modifiers, callback) { + var keyCombo = getKeyCombo(keyCode, modifiers); + runtime.assert(bindings.hasOwnProperty(keyCombo) === false, "tried to overwrite the callback handler of key combo: " + keyCombo); + bindings[keyCombo] = callback + }; + this.unbind = function(keyCode, modifiers) { + var keyCombo = getKeyCombo(keyCode, modifiers); + delete bindings[keyCombo] + }; + this.reset = function() { + defaultBinding = null; + bindings = {} + }; + this.handleEvent = function(e) { + var keyCombo = getKeyCombo(e.keyCode, getModifiers(e)), callback = bindings[keyCombo], handled = false; + if(callback) { + handled = callback() + }else { + if(defaultBinding !== null) { + handled = defaultBinding(e) + } + } + if(handled) { + if(e.preventDefault) { + e.preventDefault() + }else { + e.returnValue = false + } } } - init() -}; -core.UnitTest = function UnitTest() { -}; -core.UnitTest.prototype.setUp = function() { -}; -core.UnitTest.prototype.tearDown = function() { -}; -core.UnitTest.prototype.description = function() { -}; -core.UnitTest.prototype.tests = function() { -}; -core.UnitTest.prototype.asyncTests = function() { }; -core.UnitTest.provideTestAreaDiv = function() { - var maindoc = runtime.getWindow().document, testarea = maindoc.getElementById("testarea"); - runtime.assert(!testarea, 'Unclean test environment, found a div with id "testarea".'); - testarea = maindoc.createElement("div"); - testarea.setAttribute("id", "testarea"); - maindoc.body.appendChild(testarea); - return testarea +gui.KeyboardHandler.Modifier = {None:0, Meta:1, Ctrl:2, Alt:4, CtrlAlt:6, Shift:8, MetaShift:9, CtrlShift:10, AltShift:12}; +gui.KeyboardHandler.KeyCode = {Backspace:8, Tab:9, Clear:12, Enter:13, End:35, Home:36, Left:37, Up:38, Right:39, Down:40, Delete:46, A:65, B:66, C:67, D:68, E:69, F:70, G:71, H:72, I:73, J:74, K:75, L:76, M:77, N:78, O:79, P:80, Q:81, R:82, S:83, T:84, U:85, V:86, W:87, X:88, Y:89, Z:90}; +(function() { + return gui.KeyboardHandler +})(); +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +odf.Namespaces = {namespaceMap:{db:"urn:oasis:names:tc:opendocument:xmlns:database:1.0", dc:"http://purl.org/dc/elements/1.1/", dr3d:"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0", draw:"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0", chart:"urn:oasis:names:tc:opendocument:xmlns:chart:1.0", fo:"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0", form:"urn:oasis:names:tc:opendocument:xmlns:form:1.0", meta:"urn:oasis:names:tc:opendocument:xmlns:meta:1.0", number:"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0", +office:"urn:oasis:names:tc:opendocument:xmlns:office:1.0", presentation:"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0", style:"urn:oasis:names:tc:opendocument:xmlns:style:1.0", svg:"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0", table:"urn:oasis:names:tc:opendocument:xmlns:table:1.0", text:"urn:oasis:names:tc:opendocument:xmlns:text:1.0", xlink:"http://www.w3.org/1999/xlink", xml:"http://www.w3.org/XML/1998/namespace"}, prefixMap:{}, dbns:"urn:oasis:names:tc:opendocument:xmlns:database:1.0", +dcns:"http://purl.org/dc/elements/1.1/", dr3dns:"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0", drawns:"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0", chartns:"urn:oasis:names:tc:opendocument:xmlns:chart:1.0", fons:"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0", formns:"urn:oasis:names:tc:opendocument:xmlns:form:1.0", numberns:"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0", officens:"urn:oasis:names:tc:opendocument:xmlns:office:1.0", presentationns:"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0", +stylens:"urn:oasis:names:tc:opendocument:xmlns:style:1.0", svgns:"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0", tablens:"urn:oasis:names:tc:opendocument:xmlns:table:1.0", textns:"urn:oasis:names:tc:opendocument:xmlns:text:1.0", xlinkns:"http://www.w3.org/1999/xlink", xmlns:"http://www.w3.org/XML/1998/namespace"}; +(function() { + var map = odf.Namespaces.namespaceMap, pmap = odf.Namespaces.prefixMap, prefix; + for(prefix in map) { + if(map.hasOwnProperty(prefix)) { + pmap[map[prefix]] = prefix + } + } +})(); +odf.Namespaces.forEachPrefix = function forEachPrefix(cb) { + var ns = odf.Namespaces.namespaceMap, prefix; + for(prefix in ns) { + if(ns.hasOwnProperty(prefix)) { + cb(prefix, ns[prefix]) + } + } }; -core.UnitTest.cleanupTestAreaDiv = function() { - var maindoc = runtime.getWindow().document, testarea = maindoc.getElementById("testarea"); - runtime.assert(!!testarea && testarea.parentNode === maindoc.body, 'Test environment broken, found no div with id "testarea" below body.'); - maindoc.body.removeChild(testarea) +odf.Namespaces.lookupNamespaceURI = function lookupNamespaceURI(prefix) { + var r = null; + if(odf.Namespaces.namespaceMap.hasOwnProperty(prefix)) { + r = (odf.Namespaces.namespaceMap[prefix]) + } + return r }; -core.UnitTest.createOdtDocument = function(xml, namespaceMap) { - var xmlDoc = ""; - xmlDoc += " + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("core.DomUtils"); +runtime.loadClass("odf.Namespaces"); +odf.OdfUtils = function OdfUtils() { + var textns = odf.Namespaces.textns, drawns = odf.Namespaces.drawns, whitespaceOnly = /^\s*$/, domUtils = new core.DomUtils; + function isImage(e) { + var name = e && e.localName; + return name === "image" && e.namespaceURI === drawns } - function testFailed(msg) { - failedTests += 1; - runtime.log("fail", msg) + this.isImage = isImage; + function isCharacterFrame(e) { + return e !== null && (e.nodeType === Node.ELEMENT_NODE && (e.localName === "frame" && (e.namespaceURI === drawns && (e).getAttributeNS(textns, "anchor-type") === "as-char"))) } - function testPassed(msg) { - runtime.log("pass", msg) + this.isCharacterFrame = isCharacterFrame; + this.isTextSpan = function(e) { + var name = e && e.localName; + return name === "span" && e.namespaceURI === textns + }; + function isParagraph(e) { + var name = e && e.localName; + return(name === "p" || name === "h") && e.namespaceURI === textns } - function areArraysEqual(a, b) { - var i; - try { - if(a.length !== b.length) { - testFailed("array of length " + a.length + " should be " + b.length + " long"); - return false - } - for(i = 0;i < a.length;i += 1) { - if(a[i] !== b[i]) { - testFailed(a[i] + " should be " + b[i] + " at array index " + i); - return false - } - } - }catch(ex) { - return false + this.isParagraph = isParagraph; + function getParagraphElement(node) { + while(node && !isParagraph(node)) { + node = node.parentNode } - return true + return node } - function areAttributesEqual(a, b, skipReverseCheck) { - var aatts = a.attributes, n = aatts.length, i, att, v; - for(i = 0;i < n;i += 1) { - att = aatts.item(i); - if(att.prefix !== "xmlns" && att.namespaceURI !== "urn:webodf:names:steps") { - v = b.getAttributeNS(att.namespaceURI, att.localName); - if(!b.hasAttributeNS(att.namespaceURI, att.localName)) { - testFailed("Attribute " + att.localName + " with value " + att.value + " was not present"); - return false - } - if(v !== att.value) { - testFailed("Attribute " + att.localName + " was " + v + " should be " + att.value); - return false - } + this.getParagraphElement = getParagraphElement; + this.isWithinTrackedChanges = function(node, container) { + while(node && node !== container) { + if(node.namespaceURI === textns && node.localName === "tracked-changes") { + return true } + node = node.parentNode } - return skipReverseCheck ? true : areAttributesEqual(b, a, true) + return false + }; + this.isListItem = function(e) { + var name = e && e.localName; + return name === "list-item" && e.namespaceURI === textns + }; + this.isLineBreak = function(e) { + var name = e && e.localName; + return name === "line-break" && e.namespaceURI === textns + }; + function isODFWhitespace(text) { + return/^[ \t\r\n]+$/.test(text) } - function areNodesEqual(a, b) { - if(a.nodeType !== b.nodeType) { - testFailed("Nodetype '" + a.nodeType + "' should be '" + b.nodeType + "'"); + this.isODFWhitespace = isODFWhitespace; + function isGroupingElement(n) { + if(n === null || n.nodeType !== Node.ELEMENT_NODE) { return false } - if(a.nodeType === Node.TEXT_NODE) { - if(a.data === b.data) { - return true + var e = (n), localName = e.localName; + return/^(span|p|h|a|meta)$/.test(localName) && e.namespaceURI === textns || localName === "span" && e.className === "annotationHighlight" + } + this.isGroupingElement = isGroupingElement; + function isCharacterElement(e) { + var n = e && e.localName, ns, r = false; + if(n) { + ns = e.namespaceURI; + if(ns === textns) { + r = n === "s" || (n === "tab" || n === "line-break") + }else { + r = isCharacterFrame(e) } - testFailed("Textnode data '" + a.data + "' should be '" + b.data + "'"); - return false - } - runtime.assert(a.nodeType === Node.ELEMENT_NODE, "Only textnodes and elements supported."); - if(a.namespaceURI !== b.namespaceURI) { - testFailed("namespace '" + a.namespaceURI + "' should be '" + b.namespaceURI + "'"); - return false } - if(a.localName !== b.localName) { - testFailed("localName '" + a.localName + "' should be '" + b.localName + "'"); - return false - } - if(!areAttributesEqual((a), (b), false)) { - return false - } - var an = a.firstChild, bn = b.firstChild; - while(an) { - if(!bn) { - testFailed("Nodetype '" + an.nodeType + "' is unexpected here."); - return false - } - if(!areNodesEqual(an, bn)) { - return false + return r + } + this.isCharacterElement = isCharacterElement; + function isSpaceElement(e) { + var n = e && e.localName, ns, r = false; + if(n) { + ns = e.namespaceURI; + if(ns === textns) { + r = n === "s" } - an = an.nextSibling; - bn = bn.nextSibling - } - if(bn) { - testFailed("Nodetype '" + bn.nodeType + "' is missing here."); - return false } - return true + return r } - function isResultCorrect(actual, expected) { - if(expected === 0) { - return actual === expected && 1 / actual === 1 / expected - } - if(actual === expected) { - return true - } - if(typeof expected === "number" && isNaN(expected)) { - return typeof actual === "number" && isNaN(actual) - } - if(Object.prototype.toString.call(expected) === Object.prototype.toString.call([])) { - return areArraysEqual((actual), (expected)) - } - if(typeof expected === "object" && typeof actual === "object") { - if(expected.constructor === Element || expected.constructor === Node) { - return areNodesEqual((expected), (actual)) - } - return areObjectsEqual(expected, actual) + this.isSpaceElement = isSpaceElement; + function firstChild(node) { + while(node.firstChild !== null && isGroupingElement(node)) { + node = node.firstChild } - return false + return node } - function stringify(v) { - if(v === 0 && 1 / v < 0) { - return"-0" + this.firstChild = firstChild; + function lastChild(node) { + while(node.lastChild !== null && isGroupingElement(node)) { + node = node.lastChild } - return String(v) + return node } - function shouldBe(t, a, b) { - if(typeof a !== "string" || typeof b !== "string") { - debug("WARN: shouldBe() expects string arguments") + this.lastChild = lastChild; + function previousNode(node) { + while(!isParagraph(node) && node.previousSibling === null) { + node = (node.parentNode) } - var exception, av, bv; - try { - av = eval(a) - }catch(e) { - exception = e + return isParagraph(node) ? null : lastChild((node.previousSibling)) + } + this.previousNode = previousNode; + function nextNode(node) { + while(!isParagraph(node) && node.nextSibling === null) { + node = (node.parentNode) } - bv = eval(b); - if(exception) { - testFailed(a + " should be " + bv + ". Threw exception " + exception) - }else { - if(isResultCorrect(av, bv)) { - testPassed(a + " is " + b) + return isParagraph(node) ? null : firstChild((node.nextSibling)) + } + this.nextNode = nextNode; + function scanLeftForNonSpace(node) { + var r = false, text; + while(node) { + if(node.nodeType === Node.TEXT_NODE) { + text = (node); + if(text.length === 0) { + node = previousNode(text) + }else { + return!isODFWhitespace(text.data.substr(text.length - 1, 1)) + } }else { - if(String(typeof av) === String(typeof bv)) { - testFailed(a + " should be " + bv + ". Was " + stringify(av) + ".") + if(isCharacterElement(node)) { + r = isSpaceElement(node) === false; + node = null }else { - testFailed(a + " should be " + bv + " (of type " + typeof bv + "). Was " + av + " (of type " + typeof av + ").") + node = previousNode(node) } } } + return r } - function shouldBeNonNull(t, a) { - var exception, av; - try { - av = eval(a) - }catch(e) { - exception = e + this.scanLeftForNonSpace = scanLeftForNonSpace; + function lookLeftForCharacter(node) { + var text, r = 0, tl = 0; + if(node.nodeType === Node.TEXT_NODE) { + tl = (node).length } - if(exception) { - testFailed(a + " should be non-null. Threw exception " + exception) - }else { - if(av !== null) { - testPassed(a + " is non-null.") + if(tl > 0) { + text = (node).data; + if(!isODFWhitespace(text.substr(tl - 1, 1))) { + r = 1 }else { - testFailed(a + " should be non-null. Was " + av) + if(tl === 1) { + r = scanLeftForNonSpace(previousNode(node)) ? 2 : 0 + }else { + r = isODFWhitespace(text.substr(tl - 2, 1)) ? 0 : 2 + } } - } - } - function shouldBeNull(t, a) { - shouldBe(t, a, "null") - } - areObjectsEqual = function(a, b) { - var akeys = Object.keys(a), bkeys = Object.keys(b); - akeys.sort(); - bkeys.sort(); - return areArraysEqual(akeys, bkeys) && Object.keys(a).every(function(key) { - var aval = a[key], bval = b[key]; - if(!isResultCorrect(aval, bval)) { - testFailed(aval + " should be " + bval + " for key " + key); - return false + }else { + if(isCharacterElement(node)) { + r = 1 } - return true - }) - }; - this.areNodesEqual = areNodesEqual; - this.shouldBeNull = shouldBeNull; - this.shouldBeNonNull = shouldBeNonNull; - this.shouldBe = shouldBe; - this.countFailedTests = function() { - return failedTests - } -}; -core.UnitTester = function UnitTester() { - var failedTests = 0, results = {}; - function link(text, code) { - return"" + text + "" + } + return r } - this.runTests = function(TestClass, callback, testNames) { - var testName = Runtime.getFunctionName(TestClass) || "", tname, runner = new core.UnitTestRunner, test = new TestClass(runner), testResults = {}, i, t, tests, lastFailCount, testNameString = "testName", inBrowser = runtime.type() === "BrowserRuntime"; - if(results.hasOwnProperty(testName)) { - runtime.log("Test " + testName + " has already run."); - return + this.lookLeftForCharacter = lookLeftForCharacter; + function lookRightForCharacter(node) { + var r = false, l = 0; + if(node && node.nodeType === Node.TEXT_NODE) { + l = (node).length } - if(inBrowser) { - runtime.log("Running " + link(testName, 'runSuite("' + testName + '");') + ": " + test.description() + "") + if(l > 0) { + r = !isODFWhitespace((node).data.substr(0, 1)) }else { - runtime.log("Running " + testName + ": " + test.description) - } - tests = test.tests(); - for(i = 0;i < tests.length;i += 1) { - t = tests[i]; - tname = Runtime.getFunctionName(t) || t[testNameString]; - if(testNames.length && testNames.indexOf(tname) === -1) { - continue + if(isCharacterElement(node)) { + r = true } - if(inBrowser) { - runtime.log("Running " + link(tname, 'runTest("' + testName + '","' + tname + '")') + "") + } + return r + } + this.lookRightForCharacter = lookRightForCharacter; + function scanLeftForAnyCharacter(node) { + var r = false, l; + node = node && lastChild(node); + while(node) { + if(node.nodeType === Node.TEXT_NODE) { + l = (node).length }else { - runtime.log("Running " + tname) + l = 0 } - lastFailCount = runner.countFailedTests(); - test.setUp(); - t(); - test.tearDown(); - testResults[tname] = lastFailCount === runner.countFailedTests() - } - function runAsyncTests(todo) { - if(todo.length === 0) { - results[testName] = testResults; - failedTests += runner.countFailedTests(); - callback(); - return + if(l > 0 && !isODFWhitespace((node).data)) { + r = true; + break } - t = todo[0]; - var fname = Runtime.getFunctionName(t); - runtime.log("Running " + fname); - lastFailCount = runner.countFailedTests(); - test.setUp(); - t(function() { - test.tearDown(); - testResults[fname] = lastFailCount === runner.countFailedTests(); - runAsyncTests(todo.slice(1)) - }) + if(isCharacterElement(node)) { + r = true; + break + } + node = previousNode(node) } - runAsyncTests(test.asyncTests()) - }; - this.countFailedTests = function() { - return failedTests - }; - this.results = function() { - return results + return r } -}; -core.PositionIterator = function PositionIterator(root, whatToShow, filter, expandEntityReferences) { - var self = this, walker, currentPos, nodeFilter, TEXT_NODE = Node.TEXT_NODE, ELEMENT_NODE = Node.ELEMENT_NODE, FILTER_ACCEPT = NodeFilter.FILTER_ACCEPT, FILTER_REJECT = NodeFilter.FILTER_REJECT; - function EmptyTextNodeFilter() { - this.acceptNode = function(node) { - var text = (node); - if(!node || node.nodeType === TEXT_NODE && text.length === 0) { - return FILTER_REJECT + this.scanLeftForAnyCharacter = scanLeftForAnyCharacter; + function scanRightForAnyCharacter(node) { + var r = false, l; + node = node && firstChild(node); + while(node) { + if(node.nodeType === Node.TEXT_NODE) { + l = (node).length + }else { + l = 0 } - return FILTER_ACCEPT + if(l > 0 && !isODFWhitespace((node).data)) { + r = true; + break + } + if(isCharacterElement(node)) { + r = true; + break + } + node = nextNode(node) } + return r } - function FilteredEmptyTextNodeFilter(filter) { - this.acceptNode = function(node) { - var text = (node); - if(!node || node.nodeType === TEXT_NODE && text.length === 0) { - return FILTER_REJECT - } - return filter.acceptNode(node) + this.scanRightForAnyCharacter = scanRightForAnyCharacter; + function isTrailingWhitespace(textnode, offset) { + if(!isODFWhitespace(textnode.data.substr(offset))) { + return false } + return!scanRightForAnyCharacter(nextNode(textnode)) } - this.nextPosition = function() { - var currentNode = walker.currentNode, nodeType = currentNode.nodeType, text = (currentNode); - if(currentNode === root) { + this.isTrailingWhitespace = isTrailingWhitespace; + function isSignificantWhitespace(textNode, offset) { + var text = textNode.data, result; + if(!isODFWhitespace(text[offset])) { return false } - if(currentPos === 0 && nodeType === ELEMENT_NODE) { - if(walker.firstChild() === null) { - currentPos = 1 + if(isCharacterElement(textNode.parentNode)) { + return false + } + if(offset > 0) { + if(!isODFWhitespace(text[offset - 1])) { + result = true } }else { - if(nodeType === TEXT_NODE && currentPos + 1 < text.length) { - currentPos += 1 - }else { - if(walker.nextSibling() !== null) { - currentPos = 0 - }else { - if(walker.parentNode()) { - currentPos = 1 - }else { - return false - } - } + if(scanLeftForNonSpace(previousNode(textNode))) { + result = true } } - return true + if(result === true) { + return isTrailingWhitespace(textNode, offset) ? false : true + } + return false + } + this.isSignificantWhitespace = isSignificantWhitespace; + this.isDowngradableSpaceElement = function(node) { + if(node.namespaceURI === textns && node.localName === "s") { + return scanLeftForNonSpace(previousNode(node)) && scanRightForAnyCharacter(nextNode(node)) + } + return false }; - function setAtEnd() { - var text = (walker.currentNode), type = text.nodeType; - if(type === TEXT_NODE) { - currentPos = text.length - 1 - }else { - currentPos = type === ELEMENT_NODE ? 1 : 0 + function getFirstNonWhitespaceChild(node) { + var child = node && node.firstChild; + while(child && (child.nodeType === Node.TEXT_NODE && whitespaceOnly.test(child.nodeValue))) { + child = child.nextSibling } + return child } - function previousNode() { - if(walker.previousSibling() === null) { - if(!walker.parentNode() || walker.currentNode === root) { - walker.firstChild(); - return false + this.getFirstNonWhitespaceChild = getFirstNonWhitespaceChild; + function parseLength(length) { + var re = /(-?[0-9]*[0-9][0-9]*(\.[0-9]*)?|0+\.[0-9]*[1-9][0-9]*|\.[0-9]*[1-9][0-9]*)((cm)|(mm)|(in)|(pt)|(pc)|(px)|(%))/, m = re.exec(length); + if(!m) { + return null + } + return{value:parseFloat(m[1]), unit:m[3]} + } + this.parseLength = parseLength; + function parsePositiveLength(length) { + var result = parseLength(length); + if(result && (result.value <= 0 || result.unit === "%")) { + return null + } + return result + } + function parseNonNegativeLength(length) { + var result = parseLength(length); + if(result && (result.value < 0 || result.unit === "%")) { + return null + } + return result + } + this.parseNonNegativeLength = parseNonNegativeLength; + function parsePercentage(length) { + var result = parseLength(length); + if(result && result.unit !== "%") { + return null + } + return result + } + function parseFoFontSize(fontSize) { + return parsePositiveLength(fontSize) || parsePercentage(fontSize) + } + this.parseFoFontSize = parseFoFontSize; + function parseFoLineHeight(lineHeight) { + return parseNonNegativeLength(lineHeight) || parsePercentage(lineHeight) + } + this.parseFoLineHeight = parseFoLineHeight; + function item(a, i) { + return a[i] + } + function getImpactedParagraphs(range) { + var i, l, e, outerContainer = (range.commonAncestorContainer), impactedParagraphs = [], filtered = []; + if(outerContainer.nodeType === Node.ELEMENT_NODE) { + impactedParagraphs = domUtils.getElementsByTagNameNS(outerContainer, textns, "p").concat(domUtils.getElementsByTagNameNS(outerContainer, textns, "h")) + } + while(outerContainer && !isParagraph(outerContainer)) { + outerContainer = outerContainer.parentNode + } + if(outerContainer) { + impactedParagraphs.push(outerContainer) + } + l = impactedParagraphs.length; + for(i = 0;i < l;i += 1) { + e = item(impactedParagraphs, i); + if(domUtils.rangeIntersectsNode(range, e)) { + filtered.push(e) } - currentPos = 0 - }else { - setAtEnd() + } + return filtered + } + this.getImpactedParagraphs = getImpactedParagraphs; + function isAcceptedNode(node) { + switch(node.namespaceURI) { + case odf.Namespaces.drawns: + ; + case odf.Namespaces.svgns: + ; + case odf.Namespaces.dr3dns: + return false; + case odf.Namespaces.textns: + switch(node.localName) { + case "note-body": + ; + case "ruby-text": + return false + } + break; + case odf.Namespaces.officens: + switch(node.localName) { + case "annotation": + ; + case "binary-data": + ; + case "event-listeners": + return false + } + break; + default: + switch(node.localName) { + case "editinfo": + return false + } + break } return true } - this.previousPosition = function() { - var moved = true, currentNode = walker.currentNode; - if(currentPos === 0) { - moved = previousNode() - }else { - if(currentNode.nodeType === TEXT_NODE) { - currentPos -= 1 + function isSignificantTextContent(textNode) { + return Boolean(getParagraphElement(textNode) && (!isODFWhitespace(textNode.textContent) || isSignificantWhitespace(textNode, 0))) + } + function includeNode(range, nodeRange, includePartial) { + return includePartial && domUtils.rangesIntersect(range, nodeRange) || domUtils.containsRange(range, nodeRange) + } + function getTextNodes(range, includePartial) { + var document = range.startContainer.ownerDocument, nodeRange = document.createRange(), textNodes; + function nodeFilter(node) { + nodeRange.selectNodeContents(node); + if(node.nodeType === Node.TEXT_NODE) { + if(includeNode(range, nodeRange, includePartial)) { + return isSignificantTextContent((node)) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT + } }else { - if(walker.lastChild() !== null) { - setAtEnd() - }else { - if(currentNode === root) { - moved = false - }else { - currentPos = 0 + if(domUtils.rangesIntersect(range, nodeRange)) { + if(isAcceptedNode(node)) { + return NodeFilter.FILTER_SKIP } } } + return NodeFilter.FILTER_REJECT } - return moved - }; - this.previousNode = previousNode; - this.container = function() { - var n = (walker.currentNode), t = n.nodeType; - if(currentPos === 0 && t !== TEXT_NODE) { - n = (n.parentNode) - } - return n - }; - this.rightNode = function() { - var n = walker.currentNode, text = (n), nodeType = n.nodeType; - if(nodeType === TEXT_NODE && currentPos === text.length) { - n = n.nextSibling; - while(n && nodeFilter(n) !== FILTER_ACCEPT) { - n = n.nextSibling + textNodes = domUtils.getNodesInRange(range, nodeFilter); + nodeRange.detach(); + return textNodes + } + this.getTextNodes = getTextNodes; + this.getTextElements = function(range, includePartial, includeInsignificantWhitespace) { + var document = range.startContainer.ownerDocument, nodeRange = document.createRange(), elements; + function nodeFilter(node) { + nodeRange.selectNodeContents(node); + if(isCharacterElement(node.parentNode)) { + return NodeFilter.FILTER_REJECT } - }else { - if(nodeType === ELEMENT_NODE && currentPos === 1) { - n = null + if(node.nodeType === Node.TEXT_NODE) { + if(includeNode(range, nodeRange, includePartial)) { + if(includeInsignificantWhitespace || isSignificantTextContent((node))) { + return NodeFilter.FILTER_ACCEPT + } + } + }else { + if(isCharacterElement(node)) { + if(includeNode(range, nodeRange, includePartial)) { + return NodeFilter.FILTER_ACCEPT + } + }else { + if(isAcceptedNode(node) || isGroupingElement(node)) { + return NodeFilter.FILTER_SKIP + } + } } + return NodeFilter.FILTER_REJECT } - return n + elements = domUtils.getNodesInRange(range, nodeFilter); + nodeRange.detach(); + return elements }; - this.leftNode = function() { - var n = walker.currentNode; - if(currentPos === 0) { - n = n.previousSibling; - while(n && nodeFilter(n) !== FILTER_ACCEPT) { - n = n.previousSibling - } - }else { - if(n.nodeType === ELEMENT_NODE) { - n = n.lastChild; - while(n && nodeFilter(n) !== FILTER_ACCEPT) { - n = n.previousSibling + this.getParagraphElements = function(range) { + var document = range.startContainer.ownerDocument, nodeRange = document.createRange(), elements; + function nodeFilter(node) { + nodeRange.selectNodeContents(node); + if(isParagraph(node)) { + if(domUtils.rangesIntersect(range, nodeRange)) { + return NodeFilter.FILTER_ACCEPT + } + }else { + if(isAcceptedNode(node) || isGroupingElement(node)) { + return NodeFilter.FILTER_SKIP } } + return NodeFilter.FILTER_REJECT } - return n - }; - this.getCurrentNode = function() { - var n = (walker.currentNode); - return n + elements = domUtils.getNodesInRange(range, nodeFilter); + nodeRange.detach(); + return elements }; - this.unfilteredDomOffset = function() { - if(walker.currentNode.nodeType === TEXT_NODE) { - return currentPos - } - var c = 0, n = walker.currentNode; - if(currentPos === 1) { - n = n.lastChild - }else { - n = n.previousSibling - } - while(n) { - c += 1; - n = n.previousSibling - } - return c - }; - this.getPreviousSibling = function() { - var currentNode = walker.currentNode, sibling = walker.previousSibling(); - walker.currentNode = currentNode; - return sibling - }; - this.getNextSibling = function() { - var currentNode = walker.currentNode, sibling = walker.nextSibling(); - walker.currentNode = currentNode; - return sibling - }; - this.setUnfilteredPosition = function(container, offset) { - var filterResult, node, text; - runtime.assert(container !== null && container !== undefined, "PositionIterator.setUnfilteredPosition called without container"); - walker.currentNode = container; - if(container.nodeType === TEXT_NODE) { - currentPos = offset; - text = (container); - runtime.assert(offset <= text.length, "Error in setPosition: " + offset + " > " + text.length); - runtime.assert(offset >= 0, "Error in setPosition: " + offset + " < 0"); - if(offset === text.length) { - if(walker.nextSibling()) { - currentPos = 0 - }else { - if(walker.parentNode()) { - currentPos = 1 - }else { - runtime.assert(false, "Error in setUnfilteredPosition: position not valid.") - } - } - } - return true - } - filterResult = nodeFilter(container); - node = container.parentNode; - while(node && (node !== root && filterResult === FILTER_ACCEPT)) { - filterResult = nodeFilter(node); - if(filterResult !== FILTER_ACCEPT) { - walker.currentNode = node + this.getImageElements = function(range) { + var document = range.startContainer.ownerDocument, nodeRange = document.createRange(), elements; + function nodeFilter(node) { + nodeRange.selectNodeContents(node); + if(isImage(node) && domUtils.containsRange(range, nodeRange)) { + return NodeFilter.FILTER_ACCEPT } - node = node.parentNode - } - if(offset < container.childNodes.length && filterResult !== NodeFilter.FILTER_REJECT) { - walker.currentNode = (container.childNodes.item(offset)); - filterResult = nodeFilter(walker.currentNode); - currentPos = 0 - }else { - currentPos = 1 - } - if(filterResult === NodeFilter.FILTER_REJECT) { - currentPos = 1 - } - if(filterResult !== FILTER_ACCEPT) { - return self.nextPosition() - } - runtime.assert(nodeFilter(walker.currentNode) === FILTER_ACCEPT, "PositionIterater.setUnfilteredPosition call resulted in an non-visible node being set"); - return true - }; - this.moveToEnd = function() { - walker.currentNode = root; - currentPos = 1 - }; - this.moveToEndOfNode = function(node) { - var text; - if(node.nodeType === TEXT_NODE) { - text = (node); - self.setUnfilteredPosition(text, text.length) - }else { - walker.currentNode = node; - currentPos = 1 - } - }; - this.getNodeFilter = function() { - return nodeFilter - }; - function init() { - var f; - if(filter) { - f = new FilteredEmptyTextNodeFilter(filter) - }else { - f = new EmptyTextNodeFilter - } - nodeFilter = (f.acceptNode); - nodeFilter.acceptNode = nodeFilter; - whatToShow = whatToShow || 4294967295; - runtime.assert(root.nodeType !== Node.TEXT_NODE, "Internet Explorer doesn't allow tree walker roots to be text nodes"); - walker = root.ownerDocument.createTreeWalker(root, whatToShow, nodeFilter, expandEntityReferences); - currentPos = 0; - if(walker.firstChild() === null) { - currentPos = 1 + return NodeFilter.FILTER_SKIP } + elements = domUtils.getNodesInRange(range, nodeFilter); + nodeRange.detach(); + return elements } - init() }; -runtime.loadClass("core.PositionIterator"); -core.PositionFilter = function PositionFilter() { +/* + + Copyright (C) 2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +ops.Server = function Server() { }; -core.PositionFilter.FilterResult = {FILTER_ACCEPT:1, FILTER_REJECT:2, FILTER_SKIP:3}; -core.PositionFilter.prototype.acceptPosition = function(point) { +ops.Server.prototype.connect = function(timeout, cb) { }; -(function() { - return core.PositionFilter -})(); -runtime.loadClass("core.PositionFilter"); -core.PositionFilterChain = function PositionFilterChain() { - var filterChain = {}, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, FILTER_REJECT = core.PositionFilter.FilterResult.FILTER_REJECT; - this.acceptPosition = function(iterator) { - var filterName; - for(filterName in filterChain) { - if(filterChain.hasOwnProperty(filterName)) { - if(filterChain[filterName].acceptPosition(iterator) === FILTER_REJECT) { - return FILTER_REJECT +ops.Server.prototype.networkStatus = function() { +}; +ops.Server.prototype.login = function(login, password, successCb, failCb) { +}; +ops.Server.prototype.joinSession = function(userId, sessionId, successCb, failCb) { +}; +ops.Server.prototype.leaveSession = function(sessionId, memberId, successCb, failCb) { +}; +ops.Server.prototype.getGenesisUrl = function(sessionId) { +}; +xmldom.LSSerializerFilter = function LSSerializerFilter() { +}; +xmldom.LSSerializerFilter.prototype.acceptNode = function(node) { +}; +xmldom.XPathIterator = function XPathIterator() { +}; +xmldom.XPathIterator.prototype.next = function() { +}; +xmldom.XPathIterator.prototype.reset = function() { +}; +xmldom.XPathAtom; +function createXPathSingleton() { + var createXPathPathIterator, parsePredicates; + function isSmallestPositive(a, b, c) { + return a !== -1 && ((a < b || b === -1) && (a < c || c === -1)) + } + function parseXPathStep(xpath, pos, end, steps) { + var location = "", predicates = [], brapos = xpath.indexOf("[", pos), slapos = xpath.indexOf("/", pos), eqpos = xpath.indexOf("=", pos); + if(isSmallestPositive(slapos, brapos, eqpos)) { + location = xpath.substring(pos, slapos); + pos = slapos + 1 + }else { + if(isSmallestPositive(brapos, slapos, eqpos)) { + location = xpath.substring(pos, brapos); + pos = parsePredicates(xpath, brapos, predicates) + }else { + if(isSmallestPositive(eqpos, slapos, brapos)) { + location = xpath.substring(pos, eqpos); + pos = eqpos + }else { + location = xpath.substring(pos, end); + pos = end } } } - return FILTER_ACCEPT - }; - this.addFilter = function(filterName, filterInstance) { - filterChain[filterName] = filterInstance - }; - this.removeFilter = function(filterName) { - delete filterChain[filterName] - } -}; -core.ScheduledTask = function ScheduledTask(fn, delay) { - var timeoutId, scheduled = false; - function execute() { - fn(); - scheduled = false + steps.push({location:location, predicates:predicates}); + return pos } - function cancel() { - if(scheduled) { - runtime.clearTimeout(timeoutId); - scheduled = false + function parseXPath(xpath) { + var steps = [], p = 0, end = xpath.length, value; + while(p < end) { + p = parseXPathStep(xpath, p, end, steps); + if(p < end && xpath[p] === "=") { + value = xpath.substring(p + 1, end); + if(value.length > 2 && (value[0] === "'" || value[0] === '"')) { + value = value.slice(1, value.length - 1) + }else { + try { + value = parseInt(value, 10) + }catch(ignore) { + } + } + p = end + } } + return{steps:steps, value:value} } - this.trigger = function() { - if(!scheduled) { - timeoutId = runtime.setTimeout(execute, delay) + parsePredicates = function parsePredicates(xpath, start, predicates) { + var pos = start, l = xpath.length, depth = 0; + while(pos < l) { + if(xpath[pos] === "]") { + depth -= 1; + if(depth <= 0) { + predicates.push(parseXPath(xpath.substring(start, pos))) + } + }else { + if(xpath[pos] === "[") { + if(depth <= 0) { + start = pos + 1 + } + depth += 1 + } + } + pos += 1 } + return pos }; - this.triggerImmediate = function() { - cancel(); - execute() - }; - this.processRequests = function() { - if(scheduled) { - cancel(); - execute() + function XPathNodeIterator() { + var node = null, done = false; + this.setNode = function setNode(n) { + node = n + }; + this.reset = function() { + done = false + }; + this.next = function next() { + var val = done ? null : node; + done = true; + return val } - }; - this.cancel = cancel; - this.destroy = function(callback) { - cancel(); - callback() } -}; -core.Async = function Async() { - this.forEach = function(items, f, callback) { - var i, l = items.length, itemsDone = 0; - function end(err) { - if(itemsDone !== l) { - if(err) { - itemsDone = l; - callback(err) - }else { - itemsDone += 1; - if(itemsDone === l) { - callback(null) - } + function AttributeIterator(it, namespace, localName) { + this.reset = function reset() { + it.reset() + }; + this.next = function next() { + var node = it.next(); + while(node) { + if(node.nodeType === Node.ELEMENT_NODE) { + node = (node).getAttributeNodeNS(namespace, localName) + } + if(node) { + return node } + node = it.next() } + return node } - for(i = 0;i < l;i += 1) { - f(items[i], end) - } - }; - this.destroyAll = function(items, callback) { - function destroy(itemIndex, err) { - if(err) { - callback(err) - }else { - if(itemIndex < items.length) { - items[itemIndex](function(err) { - destroy(itemIndex + 1, err) - }) + } + function AllChildElementIterator(it, recurse) { + var root = it.next(), node = null; + this.reset = function reset() { + it.reset(); + root = it.next(); + node = null + }; + this.next = function next() { + while(root) { + if(node) { + if(recurse && node.firstChild) { + node = node.firstChild + }else { + while(!node.nextSibling && node !== root) { + node = node.parentNode + } + if(node === root) { + root = it.next() + }else { + node = node.nextSibling + } + } }else { - callback() + do { + node = root.firstChild; + if(!node) { + root = it.next() + } + }while(root && !node) + } + if(node && node.nodeType === Node.ELEMENT_NODE) { + return node } } + return null } - destroy(0, undefined) } -}; -/* - - WebODF - Copyright (c) 2010 Jos van den Oever - Licensed under the ... License: - - Project home: http://www.webodf.org/ -*/ -runtime.loadClass("core.RawInflate"); -runtime.loadClass("core.ByteArray"); -runtime.loadClass("core.ByteArrayWriter"); -runtime.loadClass("core.Base64"); -core.Zip = function Zip(url, entriesReadCallback) { - var entries, filesize, nEntries, inflate = (new core.RawInflate).inflate, zip = this, base64 = new core.Base64; - function crc32(data) { - var table = [0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615, 3915621685, 2657392035, 249268274, 2044508324, 3772115230, 2547177864, 162941995, 2125561021, 3887607047, 2428444049, 498536548, 1789927666, 4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639, 325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465, 4195302755, 2366115317, 997073096, 1281953886, 3579855332, 2724688242, 1006888145, 1258607687, 3524101629, 2768942443, 901097722, 1119000684, - 3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665, 651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731, 3485111705, 3099436303, 671266974, 1594198024, 3322730930, 2970347812, 795835527, 1483230225, 3244367275, 3060149565, 1994146192, 31158534, 2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059, 2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813, 2439277719, 3865271297, 1802195444, 476864866, 2238001368, 4066508878, 1812370925, - 453092731, 2181625025, 4111451223, 1706088902, 314042704, 2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405, 1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311, 2765210733, 3554079995, 1131014506, 879679996, 2909243462, 3663771856, 1141124467, 855842277, 2852801631, 3708648649, 1342533948, 654459306, 3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015, 1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873, 3082640443, 3233442989, - 3988292384, 2596254646, 62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523, 3814918930, 2489596804, 225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377, 4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355, 426522225, 1852507879, 4275313526, 2312317920, 282753626, 1742555852, 4189708143, 2394877945, 397917763, 1622183637, 3604390888, 2714866558, 953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859, 3624741850, 2936675148, 906185462, - 1090812512, 3747672003, 2825379669, 829329135, 1181335161, 3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3138078467, 570562233, 1426400815, 3317316542, 2998733608, 733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221, 2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151, 1913087877, 83908371, 2512341634, 3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2094854071, 198958881, 2262029012, 4057260610, 1759359992, 534414190, 2176718541, 4139329115, - 1873836001, 414664567, 2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745, 1634467795, 376229701, 2685067896, 3608007406, 1308918612, 956543938, 2808555105, 3495958263, 1231636301, 1047427035, 2932959818, 3654703836, 1088359270, 936918E3, 2847714899, 3736837829, 1202900863, 817233897, 3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203, 1423857449, 601450431, 3009837614, 3294710456, 1567103746, 711928724, 3020668471, 3272380065, 1510334235, 755167117], crc = - 0, i, iTop = data.length, x = 0, y = 0; - crc = crc ^ -1; - for(i = 0;i < iTop;i += 1) { - y = (crc ^ data[i]) & 255; - x = table[y]; - crc = crc >>> 8 ^ x + function ConditionIterator(it, condition) { + this.reset = function reset() { + it.reset() + }; + this.next = function next() { + var n = it.next(); + while(n && !condition(n)) { + n = it.next() + } + return n } - return crc ^ -1 } - function dosTime2Date(dostime) { - var year = (dostime >> 25 & 127) + 1980, month = (dostime >> 21 & 15) - 1, mday = dostime >> 16 & 31, hour = dostime >> 11 & 15, min = dostime >> 5 & 63, sec = (dostime & 31) << 1, d = new Date(year, month, mday, hour, min, sec); - return d + function createNodenameFilter(it, name, namespaceResolver) { + var s = name.split(":", 2), namespace = namespaceResolver(s[0]), localName = s[1]; + return new ConditionIterator(it, function(node) { + return node.localName === localName && node.namespaceURI === namespace + }) } - function date2DosTime(date) { - var y = date.getFullYear(); - return y < 1980 ? 0 : y - 1980 << 25 | date.getMonth() + 1 << 21 | date.getDate() << 16 | date.getHours() << 11 | date.getMinutes() << 5 | date.getSeconds() >> 1 + function createPredicateFilteredIterator(it, p, namespaceResolver) { + var nit = new XPathNodeIterator, pit = createXPathPathIterator(nit, p, namespaceResolver), value = p.value; + if(value === undefined) { + return new ConditionIterator(it, function(node) { + nit.setNode(node); + pit.reset(); + return pit.next() !== null + }) + } + return new ConditionIterator(it, function(node) { + nit.setNode(node); + pit.reset(); + var n = pit.next(); + return n ? n.nodeValue === value : false + }) } - function ZipEntry(url, stream) { - var sig, namelen, extralen, commentlen, compressionMethod, compressedSize, uncompressedSize, offset, entry = this; - function handleEntryData(data, callback) { - var estream = new core.ByteArray(data), esig = estream.readUInt32LE(), filenamelen, eextralen; - if(esig !== 67324752) { - callback("File entry signature is wrong." + esig.toString() + " " + data.length.toString(), null); - return - } - estream.pos += 22; - filenamelen = estream.readUInt16LE(); - eextralen = estream.readUInt16LE(); - estream.pos += filenamelen + eextralen; - if(compressionMethod) { - data = data.subarray(estream.pos, estream.pos + compressedSize); - if(compressedSize !== data.length) { - callback("The amount of compressed bytes read was " + data.length.toString() + " instead of " + compressedSize.toString() + " for " + entry.filename + " in " + url + ".", null); - return - } - data = inflate(data, uncompressedSize) + function item(p, i) { + return p[i] + } + createXPathPathIterator = function createXPathPathIterator(it, xpath, namespaceResolver) { + var i, j, step, location, s, p, ns; + for(i = 0;i < xpath.steps.length;i += 1) { + step = xpath.steps[i]; + location = step.location; + if(location === "") { + it = new AllChildElementIterator(it, false) }else { - data = data.subarray(estream.pos, estream.pos + uncompressedSize) + if(location[0] === "@") { + s = location.substr(1).split(":", 2); + ns = namespaceResolver(s[0]); + if(!ns) { + throw"No namespace associated with the prefix " + s[0]; + } + it = new AttributeIterator(it, ns, s[1]) + }else { + if(location !== ".") { + it = new AllChildElementIterator(it, false); + if(location.indexOf(":") !== -1) { + it = createNodenameFilter(it, location, namespaceResolver) + } + } + } } - if(uncompressedSize !== data.length) { - callback("The amount of bytes read was " + data.length.toString() + " instead of " + uncompressedSize.toString() + " for " + entry.filename + " in " + url + ".", null); - return + for(j = 0;j < step.predicates.length;j += 1) { + p = item(step.predicates, j); + it = createPredicateFilteredIterator(it, p, namespaceResolver) } - entry.data = data; - callback(null, data) } - function load(callback) { - if(entry.data !== undefined) { - callback(null, entry.data); - return - } - var size = compressedSize + 34 + namelen + extralen + 256; - if(size + offset > filesize) { - size = filesize - offset - } - runtime.read(url, offset, size, function(err, data) { - if(err || data === null) { - callback(err, data) - }else { - handleEntryData(data, callback) - } - }) + return it + }; + function fallback(node, xpath, namespaceResolver) { + var it = new XPathNodeIterator, i, nodelist, parsedXPath; + it.setNode(node); + parsedXPath = parseXPath(xpath); + it = createXPathPathIterator(it, parsedXPath, namespaceResolver); + nodelist = []; + i = it.next(); + while(i) { + nodelist.push(i); + i = it.next() } - this.load = load; - function set(filename, data, compressed, date) { - entry.filename = filename; - entry.data = data; - entry.compressed = compressed; - entry.date = date + return nodelist + } + function getODFElementsWithXPath(node, xpath, namespaceResolver) { + var doc = node.ownerDocument, nodes, elements = [], n = null; + if(!doc || typeof doc.evaluate !== "function") { + elements = fallback(node, xpath, namespaceResolver) + }else { + nodes = doc.evaluate(xpath, node, namespaceResolver, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null); + n = nodes.iterateNext(); + while(n !== null) { + if(n.nodeType === Node.ELEMENT_NODE) { + elements.push(n) + } + n = nodes.iterateNext() + } } - this.set = set; - this.error = null; - if(!stream) { - return - } - sig = stream.readUInt32LE(); - if(sig !== 33639248) { - this.error = "Central directory entry has wrong signature at position " + (stream.pos - 4).toString() + ' for file "' + url + '": ' + stream.data.length.toString(); - return - } - stream.pos += 6; - compressionMethod = stream.readUInt16LE(); - this.date = dosTime2Date(stream.readUInt32LE()); - stream.readUInt32LE(); - compressedSize = stream.readUInt32LE(); - uncompressedSize = stream.readUInt32LE(); - namelen = stream.readUInt16LE(); - extralen = stream.readUInt16LE(); - commentlen = stream.readUInt16LE(); - stream.pos += 8; - offset = stream.readUInt32LE(); - this.filename = runtime.byteArrayToString(stream.data.subarray(stream.pos, stream.pos + namelen), "utf8"); - stream.pos += namelen + extralen + commentlen + return elements } - function handleCentralDirectory(data, callback) { - var stream = new core.ByteArray(data), i, e; - entries = []; - for(i = 0;i < nEntries;i += 1) { - e = new ZipEntry(url, stream); - if(e.error) { - callback(e.error, zip); - return + return{getODFElementsWithXPath:getODFElementsWithXPath} +} +xmldom.XPath = createXPathSingleton(); +runtime.loadClass("core.DomUtils"); +core.Cursor = function Cursor(document, memberId) { + var cursorns = "urn:webodf:names:cursor", cursorNode = document.createElementNS(cursorns, "cursor"), anchorNode = document.createElementNS(cursorns, "anchor"), forwardSelection, recentlyModifiedNodes = [], selectedRange = (document.createRange()), isCollapsed, domUtils = new core.DomUtils; + function putIntoTextNode(node, container, offset) { + runtime.assert(Boolean(container), "putCursorIntoTextNode: invalid container"); + var parent = container.parentNode; + runtime.assert(Boolean(parent), "putCursorIntoTextNode: container without parent"); + runtime.assert(offset >= 0 && offset <= container.length, "putCursorIntoTextNode: offset is out of bounds"); + if(offset === 0) { + parent.insertBefore(node, container) + }else { + if(offset === container.length) { + parent.insertBefore(node, container.nextSibling) + }else { + container.splitText(offset); + parent.insertBefore(node, container.nextSibling) } - entries[entries.length] = e } - callback(null, zip) } - function handleCentralDirectoryEnd(data, callback) { - if(data.length !== 22) { - callback("Central directory length should be 22.", zip); - return - } - var stream = new core.ByteArray(data), sig, disk, cddisk, diskNEntries, cdsSize, cdsOffset; - sig = stream.readUInt32LE(); - if(sig !== 101010256) { - callback("Central directory signature is wrong: " + sig.toString(), zip); - return - } - disk = stream.readUInt16LE(); - if(disk !== 0) { - callback("Zip files with non-zero disk numbers are not supported.", zip); - return - } - cddisk = stream.readUInt16LE(); - if(cddisk !== 0) { - callback("Zip files with non-zero disk numbers are not supported.", zip); - return - } - diskNEntries = stream.readUInt16LE(); - nEntries = stream.readUInt16LE(); - if(diskNEntries !== nEntries) { - callback("Number of entries is inconsistent.", zip); - return + function removeNode(node) { + if(node.parentNode) { + recentlyModifiedNodes.push(node.previousSibling); + recentlyModifiedNodes.push(node.nextSibling); + node.parentNode.removeChild(node) } - cdsSize = stream.readUInt32LE(); - cdsOffset = stream.readUInt16LE(); - cdsOffset = filesize - 22 - cdsSize; - runtime.read(url, cdsOffset, filesize - cdsOffset, function(err, data) { - if(err || data === null) { - callback(err, zip) - }else { - handleCentralDirectory(data, callback) - } - }) } - function load(filename, callback) { - var entry = null, e, i; - for(i = 0;i < entries.length;i += 1) { - e = entries[i]; - if(e.filename === filename) { - entry = e; - break - } - } - if(entry) { - if(entry.data) { - callback(null, entry.data) - }else { - entry.load(callback) - } + function putNode(node, container, offset) { + if(container.nodeType === Node.TEXT_NODE) { + putIntoTextNode(node, (container), offset) }else { - callback(filename + " not found.", null) - } - } - function loadAsString(filename, callback) { - load(filename, function(err, data) { - if(err || data === null) { - return callback(err, null) - } - var d = runtime.byteArrayToString(data, "utf8"); - callback(null, d) - }) - } - function loadContentXmlAsFragments(filename, handler) { - zip.loadAsString(filename, function(err, data) { - if(err) { - return handler.rootElementReady(err) - } - handler.rootElementReady(null, data, true) - }) - } - function loadAsDataURL(filename, mimetype, callback) { - load(filename, function(err, data) { - if(err || !data) { - return callback(err, null) - } - var p = data, chunksize = 45E3, i = 0, dataurl; - if(!mimetype) { - if(p[1] === 80 && (p[2] === 78 && p[3] === 71)) { - mimetype = "image/png" - }else { - if(p[0] === 255 && (p[1] === 216 && p[2] === 255)) { - mimetype = "image/jpeg" - }else { - if(p[0] === 71 && (p[1] === 73 && p[2] === 70)) { - mimetype = "image/gif" - }else { - mimetype = "" - } - } - } - } - dataurl = "data:" + mimetype + ";base64,"; - while(i < data.length) { - dataurl += base64.convertUTF8ArrayToBase64(p.subarray(i, Math.min(i + chunksize, p.length))); - i += chunksize + if(container.nodeType === Node.ELEMENT_NODE) { + container.insertBefore(node, container.childNodes.item(offset)) } - callback(null, dataurl) - }) + } + recentlyModifiedNodes.push(node.previousSibling); + recentlyModifiedNodes.push(node.nextSibling) } - function loadAsDOM(filename, callback) { - zip.loadAsString(filename, function(err, xmldata) { - if(err || xmldata === null) { - callback(err, null); - return - } - var parser = new DOMParser, dom = parser.parseFromString(xmldata, "text/xml"); - callback(null, dom) - }) + function getStartNode() { + return forwardSelection ? anchorNode : cursorNode } - function save(filename, data, compressed, date) { - var i, entry; - for(i = 0;i < entries.length;i += 1) { - entry = entries[i]; - if(entry.filename === filename) { - entry.set(filename, data, compressed, date); - return - } - } - entry = new ZipEntry(url); - entry.set(filename, data, compressed, date); - entries.push(entry) + function getEndNode() { + return forwardSelection ? cursorNode : anchorNode } - function remove(filename) { - var i, entry; - for(i = 0;i < entries.length;i += 1) { - entry = entries[i]; - if(entry.filename === filename) { - entries.splice(i, 1); - return true - } + this.getNode = function() { + return cursorNode + }; + this.getAnchorNode = function() { + return anchorNode.parentNode ? anchorNode : cursorNode + }; + this.getSelectedRange = function() { + if(isCollapsed) { + selectedRange.setStartBefore(cursorNode); + selectedRange.collapse(true) + }else { + selectedRange.setStartAfter(getStartNode()); + selectedRange.setEndBefore(getEndNode()) } - return false - } - function writeEntry(entry) { - var data = new core.ByteArrayWriter("utf8"), length = 0; - data.appendArray([80, 75, 3, 4, 20, 0, 0, 0, 0, 0]); - if(entry.data) { - length = entry.data.length + return selectedRange + }; + this.setSelectedRange = function(range, isForwardSelection) { + if(selectedRange && selectedRange !== range) { + selectedRange.detach() } - data.appendUInt32LE(date2DosTime(entry.date)); - data.appendUInt32LE(crc32(entry.data)); - data.appendUInt32LE(length); - data.appendUInt32LE(length); - data.appendUInt16LE(entry.filename.length); - data.appendUInt16LE(0); - data.appendString(entry.filename); - if(entry.data) { - data.appendByteArray(entry.data) + selectedRange = range; + forwardSelection = isForwardSelection !== false; + isCollapsed = range.collapsed; + if(range.collapsed) { + removeNode(anchorNode); + removeNode(cursorNode); + putNode(cursorNode, (range.startContainer), range.startOffset) + }else { + removeNode(anchorNode); + removeNode(cursorNode); + putNode(getEndNode(), (range.endContainer), range.endOffset); + putNode(getStartNode(), (range.startContainer), range.startOffset) } - return data + recentlyModifiedNodes.forEach(domUtils.normalizeTextNodes); + recentlyModifiedNodes.length = 0 + }; + this.hasForwardSelection = function() { + return forwardSelection + }; + this.remove = function() { + removeNode(cursorNode); + recentlyModifiedNodes.forEach(domUtils.normalizeTextNodes); + recentlyModifiedNodes.length = 0 + }; + function init() { + cursorNode.setAttributeNS(cursorns, "memberId", memberId); + anchorNode.setAttributeNS(cursorns, "memberId", memberId) } - function writeCODEntry(entry, offset) { - var data = new core.ByteArrayWriter("utf8"), length = 0; - data.appendArray([80, 75, 1, 2, 20, 0, 20, 0, 0, 0, 0, 0]); - if(entry.data) { - length = entry.data.length + init() +}; +runtime.loadClass("core.PositionIterator"); +core.PositionFilter = function PositionFilter() { +}; +core.PositionFilter.FilterResult = {FILTER_ACCEPT:1, FILTER_REJECT:2, FILTER_SKIP:3}; +core.PositionFilter.prototype.acceptPosition = function(point) { +}; +(function() { + return core.PositionFilter +})(); +runtime.loadClass("core.PositionFilter"); +core.PositionFilterChain = function PositionFilterChain() { + var filterChain = {}, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, FILTER_REJECT = core.PositionFilter.FilterResult.FILTER_REJECT; + this.acceptPosition = function(iterator) { + var filterName; + for(filterName in filterChain) { + if(filterChain.hasOwnProperty(filterName)) { + if(filterChain[filterName].acceptPosition(iterator) === FILTER_REJECT) { + return FILTER_REJECT + } + } } - data.appendUInt32LE(date2DosTime(entry.date)); - data.appendUInt32LE(crc32(entry.data)); - data.appendUInt32LE(length); - data.appendUInt32LE(length); - data.appendUInt16LE(entry.filename.length); - data.appendArray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - data.appendUInt32LE(offset); - data.appendString(entry.filename); - return data + return FILTER_ACCEPT + }; + this.addFilter = function(filterName, filterInstance) { + filterChain[filterName] = filterInstance + }; + this.removeFilter = function(filterName) { + delete filterChain[filterName] } - function loadAllEntries(position, callback) { - if(position === entries.length) { - callback(null); - return +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +gui.AnnotatableCanvas = function AnnotatableCanvas() { +}; +gui.AnnotatableCanvas.prototype.refreshSize = function() { +}; +gui.AnnotatableCanvas.prototype.getZoomLevel = function() { +}; +gui.AnnotatableCanvas.prototype.getSizer = function() { +}; +gui.AnnotationViewManager = function AnnotationViewManager(canvas, odfFragment, annotationsPane) { + 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."); + function wrapAnnotation(annotation) { + var annotationWrapper = doc.createElement("div"), annotationNote = doc.createElement("div"), connectorHorizontal = doc.createElement("div"), connectorAngular = doc.createElement("div"), removeButton = doc.createElement("div"), annotationNode = annotation.node; + annotationWrapper.className = "annotationWrapper"; + annotationNode.parentNode.insertBefore(annotationWrapper, annotationNode); + annotationNote.className = "annotationNote"; + annotationNote.appendChild(annotationNode); + removeButton.className = "annotationRemoveButton"; + annotationNote.appendChild(removeButton); + connectorHorizontal.className = "annotationConnector horizontal"; + connectorAngular.className = "annotationConnector angular"; + annotationWrapper.appendChild(annotationNote); + annotationWrapper.appendChild(connectorHorizontal); + annotationWrapper.appendChild(connectorAngular) + } + function unwrapAnnotation(annotation) { + var annotationNode = annotation.node, annotationWrapper = annotationNode.parentNode.parentNode; + if(annotationWrapper.localName === "div") { + annotationWrapper.parentNode.insertBefore(annotationNode, annotationWrapper); + annotationWrapper.parentNode.removeChild(annotationWrapper) } - var entry = entries[position]; - if(entry.data !== undefined) { - loadAllEntries(position + 1, callback); - return + } + function highlightAnnotation(annotation) { + var annotationNode = annotation.node, annotationEnd = annotation.end, range = doc.createRange(), textNodes; + if(annotationEnd) { + range.setStart(annotationNode, annotationNode.childNodes.length); + range.setEnd(annotationEnd, 0); + textNodes = odfUtils.getTextNodes(range, false); + textNodes.forEach(function(n) { + var container = doc.createElement("span"), v = annotationNode.getAttributeNS(odf.Namespaces.officens, "name"); + container.className = "annotationHighlight"; + container.setAttribute("annotation", v); + n.parentNode.insertBefore(container, n); + container.appendChild(n) + }) } - entry.load(function(err) { - if(err) { - callback(err); - return - } - loadAllEntries(position + 1, callback) - }) + range.detach() } - function createByteArray(successCallback, errorCallback) { - loadAllEntries(0, function(err) { - if(err) { - errorCallback(err); - return - } - var data = new core.ByteArrayWriter("utf8"), i, e, codoffset, codsize, offsets = [0]; - for(i = 0;i < entries.length;i += 1) { - data.appendByteArrayWriter(writeEntry(entries[i])); - offsets.push(data.getLength()) - } - codoffset = data.getLength(); - for(i = 0;i < entries.length;i += 1) { - e = entries[i]; - data.appendByteArrayWriter(writeCODEntry(e, offsets[i])) + function unhighlightAnnotation(annotation) { + var annotationName = annotation.node.getAttributeNS(odf.Namespaces.officens, "name"), highlightSpans = doc.querySelectorAll('span.annotationHighlight[annotation="' + annotationName + '"]'), i, container; + for(i = 0;i < highlightSpans.length;i += 1) { + container = highlightSpans.item(i); + while(container.firstChild) { + container.parentNode.insertBefore(container.firstChild, container) } - codsize = data.getLength() - codoffset; - data.appendArray([80, 75, 5, 6, 0, 0, 0, 0]); - data.appendUInt16LE(entries.length); - data.appendUInt16LE(entries.length); - data.appendUInt32LE(codsize); - data.appendUInt32LE(codoffset); - data.appendArray([0, 0]); - successCallback(data.getByteArray()) - }) - } - function writeAs(newurl, callback) { - createByteArray(function(data) { - runtime.writeFile(newurl, data, callback) - }, callback) + container.parentNode.removeChild(container) + } } - function write(callback) { - writeAs(url, callback) + function lineDistance(point1, point2) { + var xs = 0, ys = 0; + xs = point2.x - point1.x; + xs = xs * xs; + ys = point2.y - point1.y; + ys = ys * ys; + return Math.sqrt(xs + ys) } - this.load = load; - this.save = save; - this.remove = remove; - this.write = write; - this.writeAs = writeAs; - this.createByteArray = createByteArray; - this.loadContentXmlAsFragments = loadContentXmlAsFragments; - this.loadAsString = loadAsString; - this.loadAsDOM = loadAsDOM; - this.loadAsDataURL = loadAsDataURL; - this.getEntries = function() { - return entries.slice() - }; - filesize = -1; - if(entriesReadCallback === null) { - entries = []; - return + function renderAnnotation(annotation) { + var annotationNote = annotation.node.parentElement, connectorHorizontal = annotationNote.nextElementSibling, connectorAngular = connectorHorizontal.nextElementSibling, annotationWrapper = annotationNote.parentElement, connectorAngle = 0, previousAnnotation = annotations[annotations.indexOf(annotation) - 1], previousRect, zoomLevel = canvas.getZoomLevel(); + annotationNote.style.left = (annotationsPane.getBoundingClientRect().left - annotationWrapper.getBoundingClientRect().left) / zoomLevel + "px"; + annotationNote.style.width = annotationsPane.getBoundingClientRect().width / zoomLevel + "px"; + connectorHorizontal.style.width = parseFloat(annotationNote.style.left) - CONNECTOR_MARGIN + "px"; + if(previousAnnotation) { + previousRect = previousAnnotation.node.parentElement.getBoundingClientRect(); + if((annotationWrapper.getBoundingClientRect().top - previousRect.bottom) / zoomLevel <= NOTE_MARGIN) { + annotationNote.style.top = Math.abs(annotationWrapper.getBoundingClientRect().top - previousRect.bottom) / zoomLevel + NOTE_MARGIN + "px" + }else { + annotationNote.style.top = "0px" + } + } + connectorAngular.style.left = connectorHorizontal.getBoundingClientRect().width / zoomLevel + "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) / (zoomLevel * parseFloat(connectorAngular.style.width))); + connectorAngular.style.transform = "rotate(" + connectorAngle + "rad)"; + connectorAngular.style.MozTransform = "rotate(" + connectorAngle + "rad)"; + connectorAngular.style.WebkitTransform = "rotate(" + connectorAngle + "rad)"; + connectorAngular.style.msTransform = "rotate(" + connectorAngle + "rad)" } - runtime.getFileSize(url, function(size) { - filesize = size; - if(filesize < 0) { - entriesReadCallback("File '" + url + "' cannot be read.", zip) + function showAnnotationsPane(show) { + var sizer = canvas.getSizer(); + if(show) { + annotationsPane.style.display = "inline-block"; + sizer.style.paddingRight = window.getComputedStyle(annotationsPane).width }else { - runtime.read(url, filesize - 22, 22, function(err, data) { - if(err || (entriesReadCallback === null || data === null)) { - entriesReadCallback(err, zip) - }else { - handleCentralDirectoryEnd(data, entriesReadCallback) - } - }) + annotationsPane.style.display = "none"; + sizer.style.paddingRight = 0 } - }) -}; -core.CSSUnits = function CSSUnits() { - var self = this, sizemap = {"in":1, "cm":2.54, "mm":25.4, "pt":72, "pc":12}; - this.convert = function(value, oldUnit, newUnit) { - return value * sizemap[newUnit] / sizemap[oldUnit] - }; - this.convertMeasure = function(measure, newUnit) { - var value, oldUnit, newMeasure; - if(measure && newUnit) { - value = parseFloat(measure); - oldUnit = measure.replace(value.toString(), ""); - newMeasure = self.convert(value, oldUnit, newUnit).toString() - }else { - newMeasure = "" + canvas.refreshSize() + } + function sortAnnotations() { + annotations.sort(function(a, b) { + if(a.node.compareDocumentPosition(b.node) === Node.DOCUMENT_POSITION_FOLLOWING) { + return-1 + } + return 1 + }) + } + function rerenderAnnotations() { + var i; + for(i = 0;i < annotations.length;i += 1) { + renderAnnotation(annotations[i]) } - return newMeasure - }; - this.getUnits = function(measure) { - return measure.substr(measure.length - 2, measure.length) } -}; -xmldom.LSSerializerFilter = function LSSerializerFilter() { -}; -if(typeof Object.create !== "function") { - Object["create"] = function(o) { - var F = function() { - }; - F.prototype = o; - return new F + this.rerenderAnnotations = rerenderAnnotations; + function addAnnotation(annotation) { + showAnnotationsPane(true); + annotations.push({node:annotation.node, end:annotation.end}); + sortAnnotations(); + wrapAnnotation(annotation); + if(annotation.end) { + highlightAnnotation(annotation) + } + rerenderAnnotations() } -} -xmldom.LSSerializer = function LSSerializer() { - var self = this; - function Namespaces(nsmap) { - function invertMap(map) { - var m = {}, i; - for(i in map) { - if(map.hasOwnProperty(i)) { - m[map[i]] = i - } - } - return m + this.addAnnotation = addAnnotation; + function forgetAnnotation(annotation) { + var index = annotations.indexOf(annotation); + unwrapAnnotation(annotation); + unhighlightAnnotation(annotation); + if(index !== -1) { + annotations.splice(index, 1) } - var current = nsmap || {}, currentrev = invertMap(nsmap), levels = [current], levelsrev = [currentrev], level = 0; - this.push = function() { - level += 1; - current = levels[level] = Object.create(current); - currentrev = levelsrev[level] = Object.create(currentrev) - }; - this.pop = function() { - levels[level] = undefined; - levelsrev[level] = undefined; - level -= 1; - current = levels[level]; - currentrev = levelsrev[level] - }; - this.getLocalNamespaceDefinitions = function() { - return currentrev - }; - this.getQName = function(node) { - var ns = node.namespaceURI, i = 0, p; - if(!ns) { - return node.localName - } - p = currentrev[ns]; - if(p) { - return p + ":" + node.localName - } - do { - if(p || !node.prefix) { - p = "ns" + i; - i += 1 - }else { - p = node.prefix - } - if(current[p] === ns) { - break - } - if(!current[p]) { - current[p] = ns; - currentrev[ns] = p; - break - } - p = null - }while(p === null); - return p + ":" + node.localName + if(annotations.length === 0) { + showAnnotationsPane(false) } } - function escapeContent(value) { - return value.replace(/&/g, "&").replace(//g, ">").replace(/'/g, "'").replace(/"/g, """) + function forgetAnnotations() { + while(annotations.length) { + forgetAnnotation(annotations[0]) + } } - function serializeAttribute(qname, attr) { - var escapedValue = typeof attr.value === "string" ? escapeContent(attr.value) : attr.value, s = qname + '="' + escapedValue + '"'; - return s + this.forgetAnnotations = forgetAnnotations +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("core.Cursor"); +runtime.loadClass("core.DomUtils"); +runtime.loadClass("core.PositionIterator"); +runtime.loadClass("core.PositionFilter"); +runtime.loadClass("core.LoopWatchDog"); +runtime.loadClass("odf.OdfUtils"); +gui.SelectionMover = function SelectionMover(cursor, rootNode) { + var odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils, positionIterator, cachedXOffset, timeoutHandle, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; + function getIteratorAtCursor() { + positionIterator.setUnfilteredPosition(cursor.getNode(), 0); + return positionIterator } - function startElement(ns, qname, element) { - var s = "", atts = (element.attributes), length, i, attr, attstr = "", accept, prefix, nsmap; - s += "<" + qname; - length = atts.length; - for(i = 0;i < length;i += 1) { - attr = (atts.item(i)); - if(attr.namespaceURI !== "http://www.w3.org/2000/xmlns/") { - accept = self.filter ? self.filter.acceptNode(attr) : NodeFilter.FILTER_ACCEPT; - if(accept === NodeFilter.FILTER_ACCEPT) { - attstr += " " + serializeAttribute(ns.getQName(attr), attr) - } - } + function getMaximumNodePosition(node) { + return node.nodeType === Node.TEXT_NODE ? node.textContent.length : node.childNodes.length + } + function getClientRect(clientRectangles, useRightEdge) { + var rectangle, simplifiedRectangle = null; + if(clientRectangles && clientRectangles.length > 0) { + rectangle = useRightEdge ? clientRectangles.item(clientRectangles.length - 1) : clientRectangles.item(0) } - nsmap = ns.getLocalNamespaceDefinitions(); - for(i in nsmap) { - if(nsmap.hasOwnProperty(i)) { - prefix = nsmap[i]; - if(!prefix) { - s += ' xmlns="' + i + '"' + if(rectangle) { + simplifiedRectangle = {top:rectangle.top, left:useRightEdge ? rectangle.right : rectangle.left, bottom:rectangle.bottom} + } + return simplifiedRectangle + } + function getVisibleRect(container, offset, range, useRightEdge) { + var rectangle, nodeType = container.nodeType; + range.setStart(container, offset); + range.collapse(!useRightEdge); + rectangle = getClientRect(range.getClientRects(), useRightEdge === true); + if(!rectangle && offset > 0) { + range.setStart(container, offset - 1); + range.setEnd(container, offset); + rectangle = getClientRect(range.getClientRects(), true) + } + if(!rectangle) { + if(nodeType === Node.ELEMENT_NODE && (offset > 0 && (container).childNodes.length >= offset)) { + rectangle = getVisibleRect(container, offset - 1, range, true) + }else { + if(container.nodeType === Node.TEXT_NODE && offset > 0) { + rectangle = getVisibleRect(container, offset - 1, range, true) }else { - if(prefix !== "xmlns") { - s += " xmlns:" + nsmap[i] + '="' + i + '"' + if(container.previousSibling) { + rectangle = getVisibleRect(container.previousSibling, getMaximumNodePosition(container.previousSibling), range, true) + }else { + if(container.parentNode && container.parentNode !== rootNode) { + rectangle = getVisibleRect(container.parentNode, 0, range, false) + }else { + range.selectNode(rootNode); + rectangle = getClientRect(range.getClientRects(), false) + } } } } } - s += attstr + ">"; - return s + runtime.assert(Boolean(rectangle), "No visible rectangle found"); + return(rectangle) } - function serializeNode(ns, node) { - var s = "", accept = self.filter ? self.filter.acceptNode(node) : NodeFilter.FILTER_ACCEPT, child, qname; - if(accept === NodeFilter.FILTER_ACCEPT && node.nodeType === Node.ELEMENT_NODE) { - ns.push(); - qname = ns.getQName(node); - s += startElement(ns, qname, node) + function doMove(positions, extend, move) { + var left = positions, iterator = getIteratorAtCursor(), initialRect, range = (rootNode.ownerDocument.createRange()), selectionRange = cursor.getSelectedRange().cloneRange(), newRect, horizontalMovement, o, c, isForwardSelection; + initialRect = getVisibleRect(iterator.container(), iterator.unfilteredDomOffset(), range); + while(left > 0 && move()) { + left -= 1 } - if(accept === NodeFilter.FILTER_ACCEPT || accept === NodeFilter.FILTER_SKIP) { - child = node.firstChild; - while(child) { - s += serializeNode(ns, child); - child = child.nextSibling - } - if(node.nodeValue) { - s += escapeContent(node.nodeValue) + if(extend) { + c = iterator.container(); + o = iterator.unfilteredDomOffset(); + if(domUtils.comparePoints((selectionRange.startContainer), selectionRange.startOffset, c, o) === -1) { + selectionRange.setStart(c, o); + isForwardSelection = false + }else { + selectionRange.setEnd(c, o) } + }else { + selectionRange.setStart(iterator.container(), iterator.unfilteredDomOffset()); + selectionRange.collapse(true) } - if(qname) { - s += ""; - ns.pop() - } - return s - } - this.filter = null; - this.writeToString = function(node, nsmap) { - if(!node) { - return"" + cursor.setSelectedRange(selectionRange, isForwardSelection); + iterator = getIteratorAtCursor(); + newRect = getVisibleRect(iterator.container(), iterator.unfilteredDomOffset(), range); + horizontalMovement = newRect.top === initialRect.top ? true : false; + if(horizontalMovement || cachedXOffset === undefined) { + cachedXOffset = newRect.left } - var ns = new Namespaces(nsmap); - return serializeNode(ns, node) + runtime.clearTimeout(timeoutHandle); + timeoutHandle = runtime.setTimeout(function() { + cachedXOffset = undefined + }, 2E3); + range.detach(); + return positions - left } -}; -xmldom.RelaxNGParser = function RelaxNGParser() { - var self = this, rngns = "http://relaxng.org/ns/structure/1.0", xmlnsns = "http://www.w3.org/2000/xmlns/", start, nsmap = {"http://www.w3.org/XML/1998/namespace":"xml"}, parse; - function RelaxNGParseError(error, context) { - this.message = function() { - if(context) { - error += context.nodeType === 1 ? " Element " : " Node "; - error += context.nodeName; - if(context.nodeValue) { - error += " with value '" + context.nodeValue + "'" - } - error += "." + this.movePointForward = function(positions, extend) { + return doMove(positions, extend || false, positionIterator.nextPosition) + }; + this.movePointBackward = function(positions, extend) { + return doMove(positions, extend || false, positionIterator.previousPosition) + }; + function isPositionWalkable(filter) { + var iterator = getIteratorAtCursor(); + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + iterator.setUnfilteredPosition(cursor.getAnchorNode(), 0); + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + return true } - return error } + return false } - function splitToDuos(e) { - if(e.e.length <= 2) { - return e + function countSteps(iterator, steps, filter) { + var watch = new core.LoopWatchDog(1E4), positions = 0, positionsCount = 0, increment = steps >= 0 ? 1 : -1, delegate = (steps >= 0 ? iterator.nextPosition : iterator.previousPosition); + while(steps !== 0 && delegate()) { + watch.check(); + positionsCount += increment; + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + steps -= increment; + positions += positionsCount; + positionsCount = 0 + } } - var o = {name:e.name, e:e.e.slice(0, 2)}; - return splitToDuos({name:e.name, e:[o].concat(e.e.slice(2))}) + return positions } - function splitQName(name) { - var r = name.split(":", 2), prefix = "", i; - if(r.length === 1) { - r = ["", r[0]] - }else { - prefix = r[0] - } - for(i in nsmap) { - if(nsmap[i] === prefix) { - r[0] = i + function convertForwardStepsBetweenFilters(stepsFilter1, filter1, filter2) { + var iterator = getIteratorAtCursor(), watch = new core.LoopWatchDog(1E4), pendingStepsFilter2 = 0, stepsFilter2 = 0; + while(stepsFilter1 > 0 && iterator.nextPosition()) { + watch.check(); + if(filter2.acceptPosition(iterator) === FILTER_ACCEPT) { + pendingStepsFilter2 += 1; + if(filter1.acceptPosition(iterator) === FILTER_ACCEPT) { + stepsFilter2 += pendingStepsFilter2; + pendingStepsFilter2 = 0; + stepsFilter1 -= 1 + } } } - return r + return stepsFilter2 } - function splitQNames(def) { - var i, l = def.names ? def.names.length : 0, name, localnames = [], namespaces = []; - for(i = 0;i < l;i += 1) { - name = splitQName(def.names[i]); - namespaces[i] = name[0]; - localnames[i] = name[1] + function convertBackwardStepsBetweenFilters(stepsFilter1, filter1, filter2) { + var iterator = getIteratorAtCursor(), watch = new core.LoopWatchDog(1E4), pendingStepsFilter2 = 0, stepsFilter2 = 0; + while(stepsFilter1 > 0 && iterator.previousPosition()) { + watch.check(); + if(filter2.acceptPosition(iterator) === FILTER_ACCEPT) { + pendingStepsFilter2 += 1; + if(filter1.acceptPosition(iterator) === FILTER_ACCEPT) { + stepsFilter2 += pendingStepsFilter2; + pendingStepsFilter2 = 0; + stepsFilter1 -= 1 + } + } } - def.localnames = localnames; - def.namespaces = namespaces + return stepsFilter2 } - function trim(str) { - str = str.replace(/^\s\s*/, ""); - var ws = /\s/, i = str.length - 1; - while(ws.test(str.charAt(i))) { - i -= 1 - } - return str.slice(0, i + 1) + function countStepsPublic(steps, filter) { + var iterator = getIteratorAtCursor(); + return countSteps(iterator, steps, filter) } - function copyAttributes(atts, name, names) { - var a = {}, i, att; - for(i = 0;i < atts.length;i += 1) { - att = atts.item(i); - if(!att.namespaceURI) { - if(att.localName === "name" && (name === "element" || name === "attribute")) { - names.push(att.value) - } - if(att.localName === "name" || (att.localName === "combine" || att.localName === "type")) { - att.value = trim(att.value) - } - a[att.localName] = att.value - }else { - if(att.namespaceURI === xmlnsns) { - nsmap[att.value] = att.localName - } + function countPositionsToClosestStep(container, offset, filter) { + var iterator = getIteratorAtCursor(), paragraphNode = odfUtils.getParagraphElement(iterator.getCurrentNode()), count = 0; + iterator.setUnfilteredPosition(container, offset); + if(filter.acceptPosition(iterator) !== FILTER_ACCEPT) { + count = countSteps(iterator, -1, filter); + if(count === 0 || paragraphNode && paragraphNode !== odfUtils.getParagraphElement(iterator.getCurrentNode())) { + iterator.setUnfilteredPosition(container, offset); + count = countSteps(iterator, 1, filter) } } - return a + return count } - function parseChildren(c, e, elements, names) { - var text = "", ce; - while(c) { - if(c.nodeType === Node.ELEMENT_NODE && c.namespaceURI === rngns) { - ce = parse(c, elements, e); - if(ce) { - if(ce.name === "name") { - names.push(nsmap[ce.a.ns] + ":" + ce.text); - e.push(ce) - }else { - if(ce.name === "choice" && (ce.names && ce.names.length)) { - names = names.concat(ce.names); - delete ce.names; - e.push(ce) - }else { - e.push(ce) - } + function countLineSteps(filter, direction, iterator) { + var c = iterator.container(), steps = 0, bestContainer = null, bestOffset, bestXDiff = 10, xDiff, bestCount = 0, top, left, lastTop, rect, range = (rootNode.ownerDocument.createRange()), watch = new core.LoopWatchDog(1E4); + rect = getVisibleRect(c, iterator.unfilteredDomOffset(), range); + top = rect.top; + if(cachedXOffset === undefined) { + left = rect.left + }else { + left = cachedXOffset + } + lastTop = top; + while((direction < 0 ? iterator.previousPosition() : iterator.nextPosition()) === true) { + watch.check(); + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + steps += 1; + c = iterator.container(); + rect = getVisibleRect(c, iterator.unfilteredDomOffset(), range); + if(rect.top !== top) { + if(rect.top !== lastTop && lastTop !== top) { + break + } + lastTop = rect.top; + xDiff = Math.abs(left - rect.left); + if(bestContainer === null || xDiff < bestXDiff) { + bestContainer = c; + bestOffset = iterator.unfilteredDomOffset(); + bestXDiff = xDiff; + bestCount = steps } - } - }else { - if(c.nodeType === Node.TEXT_NODE) { - text += c.nodeValue } } - c = c.nextSibling } - return text + if(bestContainer !== null) { + iterator.setUnfilteredPosition(bestContainer, (bestOffset)); + steps = bestCount + }else { + steps = 0 + } + range.detach(); + return steps } - function combineDefines(combine, name, e, siblings) { - var i, ce; - for(i = 0;siblings && i < siblings.length;i += 1) { - ce = siblings[i]; - if(ce.name === "define" && (ce.a && ce.a.name === name)) { - ce.e = [{name:combine, e:ce.e.concat(e)}]; - return ce + function countLinesSteps(lines, filter) { + var iterator = getIteratorAtCursor(), stepCount = 0, steps = 0, direction = lines < 0 ? -1 : 1; + lines = Math.abs(lines); + while(lines > 0) { + stepCount += countLineSteps(filter, direction, iterator); + if(stepCount === 0) { + break } + steps += stepCount; + lines -= 1 } - return null + return steps * direction } - parse = function parse(element, elements, siblings) { - var e = [], a, ce, i, text, name = element.localName, names = []; - a = copyAttributes(element.attributes, name, names); - a.combine = a.combine || undefined; - text = parseChildren(element.firstChild, e, elements, names); - if(name !== "value" && name !== "param") { - text = /^\s*([\s\S]*\S)?\s*$/.exec(text)[1] - } - if(name === "value" && a.type === undefined) { - a.type = "token"; - a.datatypeLibrary = "" - } - if((name === "attribute" || name === "element") && a.name !== undefined) { - i = splitQName(a.name); - e = [{name:"name", text:i[1], a:{ns:i[0]}}].concat(e); - delete a.name - } - if(name === "name" || (name === "nsName" || name === "value")) { - if(a.ns === undefined) { - a.ns = "" - } + function countStepsToLineBoundary(direction, filter) { + var fnNextPos, increment, lastRect, rect, onSameLine, iterator = getIteratorAtCursor(), paragraphNode = odfUtils.getParagraphElement(iterator.getCurrentNode()), steps = 0, range = (rootNode.ownerDocument.createRange()); + if(direction < 0) { + fnNextPos = iterator.previousPosition; + increment = -1 }else { - delete a.ns + fnNextPos = iterator.nextPosition; + increment = 1 } - if(name === "name") { - i = splitQName(text); - a.ns = i[0]; - text = i[1] + lastRect = getVisibleRect(iterator.container(), iterator.unfilteredDomOffset(), range); + while(fnNextPos.call(iterator)) { + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + if(odfUtils.getParagraphElement(iterator.getCurrentNode()) !== paragraphNode) { + break + } + rect = getVisibleRect(iterator.container(), iterator.unfilteredDomOffset(), range); + if(rect.bottom !== lastRect.bottom) { + onSameLine = rect.top >= lastRect.top && rect.bottom < lastRect.bottom || rect.top <= lastRect.top && rect.bottom > lastRect.bottom; + if(!onSameLine) { + break + } + } + steps += increment; + lastRect = rect + } } - if(e.length > 1 && (name === "define" || (name === "oneOrMore" || (name === "zeroOrMore" || (name === "optional" || (name === "list" || name === "mixed")))))) { - e = [{name:"group", e:splitToDuos({name:"group", e:e}).e}] + range.detach(); + return steps + } + function countStepsToPosition(targetNode, targetOffset, filter) { + runtime.assert(targetNode !== null, "SelectionMover.countStepsToPosition called with element===null"); + var iterator = getIteratorAtCursor(), c = iterator.container(), o = iterator.unfilteredDomOffset(), steps = 0, watch = new core.LoopWatchDog(1E4), comparison; + iterator.setUnfilteredPosition(targetNode, targetOffset); + while(filter.acceptPosition(iterator) !== FILTER_ACCEPT && iterator.previousPosition()) { + watch.check() } - if(e.length > 2 && name === "element") { - e = [e[0]].concat({name:"group", e:splitToDuos({name:"group", e:e.slice(1)}).e}) + targetNode = iterator.container(); + runtime.assert(Boolean(targetNode), "SelectionMover.countStepsToPosition: positionIterator.container() returned null"); + targetOffset = iterator.unfilteredDomOffset(); + iterator.setUnfilteredPosition(c, o); + while(filter.acceptPosition(iterator) !== FILTER_ACCEPT && iterator.previousPosition()) { + watch.check() } - if(e.length === 1 && name === "attribute") { - e.push({name:"text", text:text}) - } - if(e.length === 1 && (name === "choice" || (name === "group" || name === "interleave"))) { - name = e[0].name; - names = e[0].names; - a = e[0].a; - text = e[0].text; - e = e[0].e - }else { - if(e.length > 2 && (name === "choice" || (name === "group" || name === "interleave"))) { - e = splitToDuos({name:name, e:e}).e - } - } - if(name === "mixed") { - name = "interleave"; - e = [e[0], {name:"text"}] - } - if(name === "optional") { - name = "choice"; - e = [e[0], {name:"empty"}] - } - if(name === "zeroOrMore") { - name = "choice"; - e = [{name:"oneOrMore", e:[e[0]]}, {name:"empty"}] - } - if(name === "define" && a.combine) { - ce = combineDefines(a.combine, a.name, e, siblings); - if(ce) { - return - } - } - ce = {name:name}; - if(e && e.length > 0) { - ce.e = e - } - for(i in a) { - if(a.hasOwnProperty(i)) { - ce.a = a; - break - } - } - if(text !== undefined) { - ce.text = text - } - if(names && names.length > 0) { - ce.names = names - } - if(name === "element") { - ce.id = elements.length; - elements.push(ce); - ce = {name:"elementref", id:ce.id} - } - return ce - }; - function resolveDefines(def, defines) { - var i = 0, e, defs, end, name = def.name; - while(def.e && i < def.e.length) { - e = def.e[i]; - if(e.name === "ref") { - defs = defines[e.a.name]; - if(!defs) { - throw e.a.name + " was not defined."; - } - end = def.e.slice(i + 1); - def.e = def.e.slice(0, i); - def.e = def.e.concat(defs.e); - def.e = def.e.concat(end) - }else { - i += 1; - resolveDefines(e, defines) - } - } - e = def.e; - if(name === "choice") { - if(!e || (!e[1] || e[1].name === "empty")) { - if(!e || (!e[0] || e[0].name === "empty")) { - delete def.e; - def.name = "empty" - }else { - e[1] = e[0]; - e[0] = {name:"empty"} - } - } - } - if(name === "group" || name === "interleave") { - if(e[0].name === "empty") { - if(e[1].name === "empty") { - delete def.e; - def.name = "empty" - }else { - name = def.name = e[1].name; - def.names = e[1].names; - e = def.e = e[1].e + comparison = domUtils.comparePoints(targetNode, targetOffset, iterator.container(), iterator.unfilteredDomOffset()); + if(comparison < 0) { + while(iterator.nextPosition()) { + watch.check(); + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + steps += 1 } - }else { - if(e[1].name === "empty") { - name = def.name = e[0].name; - def.names = e[0].names; - e = def.e = e[0].e + if(iterator.container() === targetNode && iterator.unfilteredDomOffset() === targetOffset) { + return steps } } - } - if(name === "oneOrMore" && e[0].name === "empty") { - delete def.e; - def.name = "empty" - } - if(name === "attribute") { - splitQNames(def) - } - if(name === "interleave") { - if(e[0].name === "interleave") { - if(e[1].name === "interleave") { - e = def.e = e[0].e.concat(e[1].e) - }else { - e = def.e = [e[1]].concat(e[0].e) - } - }else { - if(e[1].name === "interleave") { - e = def.e = [e[0]].concat(e[1].e) + }else { + if(comparison > 0) { + while(iterator.previousPosition()) { + watch.check(); + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + steps -= 1; + if(iterator.container() === targetNode && iterator.unfilteredDomOffset() === targetOffset) { + break + } + } } } } + return steps } - function resolveElements(def, elements) { - var i = 0, e; - while(def.e && i < def.e.length) { - e = def.e[i]; - if(e.name === "elementref") { - e.id = e.id || 0; - def.e[i] = elements[e.id] - }else { - if(e.name !== "element") { - resolveElements(e, elements) - } - } - i += 1 - } + this.getStepCounter = function() { + return{countSteps:countStepsPublic, convertForwardStepsBetweenFilters:convertForwardStepsBetweenFilters, convertBackwardStepsBetweenFilters:convertBackwardStepsBetweenFilters, countLinesSteps:countLinesSteps, countStepsToLineBoundary:countStepsToLineBoundary, countStepsToPosition:countStepsToPosition, isPositionWalkable:isPositionWalkable, countPositionsToNearestStep:countPositionsToClosestStep} + }; + function init() { + positionIterator = gui.SelectionMover.createPositionIterator(rootNode); + var range = rootNode.ownerDocument.createRange(); + range.setStart(positionIterator.container(), positionIterator.unfilteredDomOffset()); + range.collapse(true); + cursor.setSelectedRange(range) } - function main(dom, callback) { - var elements = [], grammar = parse(dom && dom.documentElement, elements, undefined), i, e, defines = {}; - for(i = 0;i < grammar.e.length;i += 1) { - e = grammar.e[i]; - if(e.name === "define") { - defines[e.a.name] = e - }else { - if(e.name === "start") { - start = e - } - } - } - if(!start) { - return[new RelaxNGParseError("No Relax NG start element was found.")] - } - resolveDefines(start, defines); - for(i in defines) { - if(defines.hasOwnProperty(i)) { - resolveDefines(defines[i], defines) + init() +}; +gui.SelectionMover.createPositionIterator = function(rootNode) { + function CursorFilter() { + this.acceptNode = function(node) { + if(!node || (node.namespaceURI === "urn:webodf:names:cursor" || node.namespaceURI === "urn:webodf:names:editinfo")) { + return NodeFilter.FILTER_REJECT } + return NodeFilter.FILTER_ACCEPT } - for(i = 0;i < elements.length;i += 1) { - resolveDefines(elements[i], defines) - } - if(callback) { - self.rootPattern = callback(start.e[0], elements) + } + var filter = new CursorFilter; + return new core.PositionIterator(rootNode, 5, filter, false) +}; +(function() { + return gui.SelectionMover +})(); +/* + + Copyright (C) 2013 KO GmbH + + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("odf.Namespaces"); +runtime.loadClass("core.DomUtils"); +odf.MetadataManager = function MetadataManager(metaElement) { + var domUtils = new core.DomUtils, metadata = {}; + function setMetadata(setProperties, removedProperties) { + if(setProperties) { + Object.keys(setProperties).forEach(function(key) { + metadata[key] = setProperties[key] + }); + domUtils.mapKeyValObjOntoNode(metaElement, setProperties, odf.Namespaces.lookupNamespaceURI) } - resolveElements(start, elements); - for(i = 0;i < elements.length;i += 1) { - resolveElements(elements[i], elements) + if(removedProperties) { + removedProperties.forEach(function(name) { + delete metadata[name] + }); + domUtils.removeKeyElementsFromNode(metaElement, removedProperties, odf.Namespaces.lookupNamespaceURI) } - self.start = start; - self.elements = elements; - self.nsmap = nsmap; - return null } - this.parseRelaxNGDOM = main + this.setMetadata = setMetadata; + this.incrementEditingCycles = function() { + var cycles = parseInt(metadata["meta:editing-cycles"] || 0, 10) + 1; + setMetadata({"meta:editing-cycles":cycles}, null) + }; + function init() { + metadata = domUtils.getKeyValRepresentationOfNode(metaElement, odf.Namespaces.lookupPrefix) + } + init() }; -runtime.loadClass("xmldom.RelaxNGParser"); -xmldom.RelaxNG = function RelaxNG() { - var xmlnsns = "http://www.w3.org/2000/xmlns/", createChoice, createInterleave, createGroup, createAfter, createOneOrMore, createValue, createAttribute, createNameClass, createData, makePattern, notAllowed = {type:"notAllowed", nullable:false, hash:"notAllowed", textDeriv:function() { - return notAllowed - }, startTagOpenDeriv:function() { - return notAllowed - }, attDeriv:function() { - return notAllowed - }, startTagCloseDeriv:function() { - return notAllowed - }, endTagDeriv:function() { - return notAllowed - }}, empty = {type:"empty", nullable:true, hash:"empty", textDeriv:function() { - return notAllowed - }, startTagOpenDeriv:function() { - return notAllowed - }, attDeriv:function() { - return notAllowed - }, startTagCloseDeriv:function() { - return empty - }, endTagDeriv:function() { - return notAllowed - }}, text = {type:"text", nullable:true, hash:"text", textDeriv:function() { - return text - }, startTagOpenDeriv:function() { - return notAllowed - }, attDeriv:function() { - return notAllowed - }, startTagCloseDeriv:function() { - return text - }, endTagDeriv:function() { - return notAllowed - }}, applyAfter, childDeriv, rootPattern; - function memoize0arg(func) { - return function() { - var cache; - return function() { - if(cache === undefined) { - cache = func() - } - return cache - } - }() - } - function memoize1arg(type, func) { - return function() { - var cache = {}, cachecount = 0; - return function(a) { - var ahash = a.hash || a.toString(), v; - v = cache[ahash]; - if(v !== undefined) { - return v - } - cache[ahash] = v = func(a); - v.hash = type + cachecount.toString(); - cachecount += 1; - return v +/* + + Copyright (C) 2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +odf.OdfNodeFilter = function OdfNodeFilter() { + this.acceptNode = function(node) { + var result; + if(node.namespaceURI === "http://www.w3.org/1999/xhtml") { + result = NodeFilter.FILTER_SKIP + }else { + if(node.namespaceURI && node.namespaceURI.match(/^urn:webodf:/)) { + result = NodeFilter.FILTER_REJECT + }else { + result = NodeFilter.FILTER_ACCEPT } - }() + } + return result } - function memoizeNode(func) { - return function() { - var cache = {}; - return function(node) { - var v, m; - m = cache[node.localName]; - if(m === undefined) { - cache[node.localName] = m = {} +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("odf.Namespaces"); +runtime.loadClass("odf.OdfUtils"); +runtime.loadClass("xmldom.XPath"); +runtime.loadClass("core.CSSUnits"); +odf.StyleTreeNode = function StyleTreeNode(element) { + this.derivedStyles = {}; + this.element = element +}; +odf.Style2CSS = function Style2CSS() { + var drawns = odf.Namespaces.drawns, fons = odf.Namespaces.fons, officens = odf.Namespaces.officens, stylens = odf.Namespaces.stylens, svgns = odf.Namespaces.svgns, tablens = odf.Namespaces.tablens, textns = odf.Namespaces.textns, xlinkns = odf.Namespaces.xlinkns, presentationns = odf.Namespaces.presentationns, familynamespaceprefixes = {"graphic":"draw", "drawing-page":"draw", "paragraph":"text", "presentation":"presentation", "ruby":"text", "section":"text", "table":"table", "table-cell":"table", + "table-column":"table", "table-row":"table", "text":"text", "list":"text", "page":"office"}, familytagnames = {"graphic":["circle", "connected", "control", "custom-shape", "ellipse", "frame", "g", "line", "measure", "page", "page-thumbnail", "path", "polygon", "polyline", "rect", "regular-polygon"], "paragraph":["alphabetical-index-entry-template", "h", "illustration-index-entry-template", "index-source-style", "object-index-entry-template", "p", "table-index-entry-template", "table-of-content-entry-template", + "user-index-entry-template"], "presentation":["caption", "circle", "connector", "control", "custom-shape", "ellipse", "frame", "g", "line", "measure", "page-thumbnail", "path", "polygon", "polyline", "rect", "regular-polygon"], "drawing-page":["caption", "circle", "connector", "control", "page", "custom-shape", "ellipse", "frame", "g", "line", "measure", "page-thumbnail", "path", "polygon", "polyline", "rect", "regular-polygon"], "ruby":["ruby", "ruby-text"], "section":["alphabetical-index", "bibliography", + "illustration-index", "index-title", "object-index", "section", "table-of-content", "table-index", "user-index"], "table":["background", "table"], "table-cell":["body", "covered-table-cell", "even-columns", "even-rows", "first-column", "first-row", "last-column", "last-row", "odd-columns", "odd-rows", "table-cell"], "table-column":["table-column"], "table-row":["table-row"], "text":["a", "index-entry-chapter", "index-entry-link-end", "index-entry-link-start", "index-entry-page-number", "index-entry-span", + "index-entry-tab-stop", "index-entry-text", "index-title-template", "linenumbering-configuration", "list-level-style-number", "list-level-style-bullet", "outline-level-style", "span"], "list":["list-item"]}, textPropertySimpleMapping = [[fons, "color", "color"], [fons, "background-color", "background-color"], [fons, "font-weight", "font-weight"], [fons, "font-style", "font-style"]], bgImageSimpleMapping = [[stylens, "repeat", "background-repeat"]], paragraphPropertySimpleMapping = [[fons, "background-color", + "background-color"], [fons, "text-align", "text-align"], [fons, "text-indent", "text-indent"], [fons, "padding", "padding"], [fons, "padding-left", "padding-left"], [fons, "padding-right", "padding-right"], [fons, "padding-top", "padding-top"], [fons, "padding-bottom", "padding-bottom"], [fons, "border-left", "border-left"], [fons, "border-right", "border-right"], [fons, "border-top", "border-top"], [fons, "border-bottom", "border-bottom"], [fons, "margin", "margin"], [fons, "margin-left", "margin-left"], + [fons, "margin-right", "margin-right"], [fons, "margin-top", "margin-top"], [fons, "margin-bottom", "margin-bottom"], [fons, "border", "border"]], graphicPropertySimpleMapping = [[fons, "background-color", "background-color"], [fons, "min-height", "min-height"], [drawns, "stroke", "border"], [svgns, "stroke-color", "border-color"], [svgns, "stroke-width", "border-width"], [fons, "border", "border"], [fons, "border-left", "border-left"], [fons, "border-right", "border-right"], [fons, "border-top", + "border-top"], [fons, "border-bottom", "border-bottom"]], tablecellPropertySimpleMapping = [[fons, "background-color", "background-color"], [fons, "border-left", "border-left"], [fons, "border-right", "border-right"], [fons, "border-top", "border-top"], [fons, "border-bottom", "border-bottom"], [fons, "border", "border"]], tablecolumnPropertySimpleMapping = [[stylens, "column-width", "width"]], tablerowPropertySimpleMapping = [[stylens, "row-height", "height"], [fons, "keep-together", null]], tablePropertySimpleMapping = + [[stylens, "width", "width"], [fons, "margin-left", "margin-left"], [fons, "margin-right", "margin-right"], [fons, "margin-top", "margin-top"], [fons, "margin-bottom", "margin-bottom"]], pageContentPropertySimpleMapping = [[fons, "background-color", "background-color"], [fons, "padding", "padding"], [fons, "padding-left", "padding-left"], [fons, "padding-right", "padding-right"], [fons, "padding-top", "padding-top"], [fons, "padding-bottom", "padding-bottom"], [fons, "border", "border"], [fons, + "border-left", "border-left"], [fons, "border-right", "border-right"], [fons, "border-top", "border-top"], [fons, "border-bottom", "border-bottom"], [fons, "margin", "margin"], [fons, "margin-left", "margin-left"], [fons, "margin-right", "margin-right"], [fons, "margin-top", "margin-top"], [fons, "margin-bottom", "margin-bottom"]], pageSizePropertySimpleMapping = [[fons, "page-width", "width"], [fons, "page-height", "height"]], borderPropertyMap = {"border":true, "border-left":true, "border-right":true, + "border-top":true, "border-bottom":true, "stroke-width":true}, fontFaceDeclsMap = {}, utils = new odf.OdfUtils, documentType, odfRoot, defaultFontSize, xpath = xmldom.XPath, cssUnits = new core.CSSUnits; + function getStyleMap(stylesnode) { + var node, name, family, style, stylemap = {}; + if(!stylesnode) { + return stylemap + } + node = stylesnode.firstElementChild; + while(node) { + if(node.namespaceURI === stylens && (node.localName === "style" || node.localName === "default-style")) { + family = node.getAttributeNS(stylens, "family") + }else { + if(node.namespaceURI === textns && node.localName === "list-style") { + family = "list" }else { - v = m[node.namespaceURI]; - if(v !== undefined) { - return v + if(node.namespaceURI === stylens && (node.localName === "page-layout" || node.localName === "default-page-layout")) { + family = "page" + }else { + family = undefined } } - m[node.namespaceURI] = v = func(node); - return v } - }() - } - function memoize2arg(type, fastfunc, func) { - return function() { - var cache = {}, cachecount = 0; - return function(a, b) { - var v = fastfunc && fastfunc(a, b), ahash, bhash, m; - if(v !== undefined) { - return v + if(family) { + name = node.getAttributeNS(stylens, "name"); + if(!name) { + name = "" } - ahash = a.hash || a.toString(); - bhash = b.hash || b.toString(); - m = cache[ahash]; - if(m === undefined) { - cache[ahash] = m = {} + if(stylemap.hasOwnProperty(family)) { + style = stylemap[family] }else { - v = m[bhash]; - if(v !== undefined) { - return v - } + stylemap[family] = style = {} } - m[bhash] = v = func(a, b); - v.hash = type + cachecount.toString(); - cachecount += 1; - return v + style[name] = node } - }() + node = node.nextElementSibling + } + return stylemap } - function unorderedMemoize2arg(type, fastfunc, func) { - return function() { - var cache = {}, cachecount = 0; - return function(a, b) { - var v = fastfunc && fastfunc(a, b), ahash, bhash, m; - if(v !== undefined) { - return v - } - ahash = a.hash || a.toString(); - bhash = b.hash || b.toString(); - if(ahash < bhash) { - m = ahash; - ahash = bhash; - bhash = m; - m = a; - a = b; - b = m - } - m = cache[ahash]; - if(m === undefined) { - cache[ahash] = m = {} - }else { - v = m[bhash]; - if(v !== undefined) { - return v - } + function findStyle(stylestree, name) { + if(stylestree.hasOwnProperty(name)) { + return stylestree[name] + } + var n, style = null; + for(n in stylestree) { + if(stylestree.hasOwnProperty(n)) { + style = findStyle(stylestree[n].derivedStyles, name); + if(style) { + break } - m[bhash] = v = func(a, b); - v.hash = type + cachecount.toString(); - cachecount += 1; - return v } - }() + } + return style } - function getUniqueLeaves(leaves, pattern) { - if(pattern.p1.type === "choice") { - getUniqueLeaves(leaves, pattern.p1) - }else { - leaves[pattern.p1.hash] = pattern.p1 + function addStyleToStyleTree(stylename, stylesmap, stylestree) { + var style, parentname, parentstyle; + if(!stylesmap.hasOwnProperty(stylename)) { + return null } - if(pattern.p2.type === "choice") { - getUniqueLeaves(leaves, pattern.p2) + style = new odf.StyleTreeNode(stylesmap[stylename]); + parentname = style.element.getAttributeNS(stylens, "parent-style-name"); + parentstyle = null; + if(parentname) { + parentstyle = findStyle(stylestree, parentname) || addStyleToStyleTree(parentname, stylesmap, stylestree) + } + if(parentstyle) { + parentstyle.derivedStyles[stylename] = style }else { - leaves[pattern.p2.hash] = pattern.p2 + stylestree[stylename] = style } + delete stylesmap[stylename]; + return style } - createChoice = memoize2arg("choice", function(p1, p2) { - if(p1 === notAllowed) { - return p2 + function addStyleMapToStyleTree(stylesmap, stylestree) { + var name; + for(name in stylesmap) { + if(stylesmap.hasOwnProperty(name)) { + addStyleToStyleTree(name, stylesmap, stylestree) + } } - if(p2 === notAllowed) { - return p1 + } + function createSelector(family, name) { + var prefix = familynamespaceprefixes[family], namepart, selector; + if(prefix === undefined) { + return null } - if(p1 === p2) { - return p1 + if(name) { + namepart = "[" + prefix + '|style-name="' + name + '"]' + }else { + namepart = "" } - }, function(p1, p2) { - function makeChoice(p1, p2) { - return{type:"choice", p1:p1, p2:p2, nullable:p1.nullable || p2.nullable, textDeriv:function(context, text) { - return createChoice(p1.textDeriv(context, text), p2.textDeriv(context, text)) - }, startTagOpenDeriv:memoizeNode(function(node) { - return createChoice(p1.startTagOpenDeriv(node), p2.startTagOpenDeriv(node)) - }), attDeriv:function(context, attribute) { - return createChoice(p1.attDeriv(context, attribute), p2.attDeriv(context, attribute)) - }, startTagCloseDeriv:memoize0arg(function() { - return createChoice(p1.startTagCloseDeriv(), p2.startTagCloseDeriv()) - }), endTagDeriv:memoize0arg(function() { - return createChoice(p1.endTagDeriv(), p2.endTagDeriv()) - })} - } - var leaves = {}, i; - getUniqueLeaves(leaves, {p1:p1, p2:p2}); - p1 = undefined; - p2 = undefined; - for(i in leaves) { - if(leaves.hasOwnProperty(i)) { - if(p1 === undefined) { - p1 = leaves[i] - }else { - if(p2 === undefined) { - p2 = leaves[i] - }else { - p2 = createChoice(p2, leaves[i]) - } - } + if(prefix === "presentation") { + prefix = "draw"; + if(name) { + namepart = '[presentation|style-name="' + name + '"]' + }else { + namepart = "" } } - return makeChoice(p1, p2) - }); - createInterleave = unorderedMemoize2arg("interleave", function(p1, p2) { - if(p1 === notAllowed || p2 === notAllowed) { - return notAllowed - } - if(p1 === empty) { - return p2 - } - if(p2 === empty) { - return p1 - } - }, function(p1, p2) { - return{type:"interleave", p1:p1, p2:p2, nullable:p1.nullable && p2.nullable, textDeriv:function(context, text) { - return createChoice(createInterleave(p1.textDeriv(context, text), p2), createInterleave(p1, p2.textDeriv(context, text))) - }, startTagOpenDeriv:memoizeNode(function(node) { - return createChoice(applyAfter(function(p) { - return createInterleave(p, p2) - }, p1.startTagOpenDeriv(node)), applyAfter(function(p) { - return createInterleave(p1, p) - }, p2.startTagOpenDeriv(node))) - }), attDeriv:function(context, attribute) { - return createChoice(createInterleave(p1.attDeriv(context, attribute), p2), createInterleave(p1, p2.attDeriv(context, attribute))) - }, startTagCloseDeriv:memoize0arg(function() { - return createInterleave(p1.startTagCloseDeriv(), p2.startTagCloseDeriv()) - })} - }); - createGroup = memoize2arg("group", function(p1, p2) { - if(p1 === notAllowed || p2 === notAllowed) { - return notAllowed - } - if(p1 === empty) { - return p2 - } - if(p2 === empty) { - return p1 + selector = prefix + "|" + familytagnames[family].join(namepart + "," + prefix + "|") + namepart; + return selector + } + function getSelectors(family, name, node) { + var selectors = [], ss, derivedStyles = node.derivedStyles, n; + ss = createSelector(family, name); + if(ss !== null) { + selectors.push(ss) } - }, function(p1, p2) { - return{type:"group", p1:p1, p2:p2, nullable:p1.nullable && p2.nullable, textDeriv:function(context, text) { - var p = createGroup(p1.textDeriv(context, text), p2); - if(p1.nullable) { - return createChoice(p, p2.textDeriv(context, text)) - } - return p - }, startTagOpenDeriv:function(node) { - var x = applyAfter(function(p) { - return createGroup(p, p2) - }, p1.startTagOpenDeriv(node)); - if(p1.nullable) { - return createChoice(x, p2.startTagOpenDeriv(node)) + for(n in derivedStyles) { + if(derivedStyles.hasOwnProperty(n)) { + ss = getSelectors(family, n, derivedStyles[n]); + selectors = selectors.concat(ss) } - return x - }, attDeriv:function(context, attribute) { - return createChoice(createGroup(p1.attDeriv(context, attribute), p2), createGroup(p1, p2.attDeriv(context, attribute))) - }, startTagCloseDeriv:memoize0arg(function() { - return createGroup(p1.startTagCloseDeriv(), p2.startTagCloseDeriv()) - })} - }); - createAfter = memoize2arg("after", function(p1, p2) { - if(p1 === notAllowed || p2 === notAllowed) { - return notAllowed - } - }, function(p1, p2) { - return{type:"after", p1:p1, p2:p2, nullable:false, textDeriv:function(context, text) { - return createAfter(p1.textDeriv(context, text), p2) - }, startTagOpenDeriv:memoizeNode(function(node) { - return applyAfter(function(p) { - return createAfter(p, p2) - }, p1.startTagOpenDeriv(node)) - }), attDeriv:function(context, attribute) { - return createAfter(p1.attDeriv(context, attribute), p2) - }, startTagCloseDeriv:memoize0arg(function() { - return createAfter(p1.startTagCloseDeriv(), p2) - }), endTagDeriv:memoize0arg(function() { - return p1.nullable ? p2 : notAllowed - })} - }); - createOneOrMore = memoize1arg("oneormore", function(p) { - if(p === notAllowed) { - return notAllowed } - return{type:"oneOrMore", p:p, nullable:p.nullable, textDeriv:function(context, text) { - return createGroup(p.textDeriv(context, text), createChoice(this, empty)) - }, startTagOpenDeriv:function(node) { - var oneOrMore = this; - return applyAfter(function(pf) { - return createGroup(pf, createChoice(oneOrMore, empty)) - }, p.startTagOpenDeriv(node)) - }, attDeriv:function(context, attribute) { - var oneOrMore = this; - return createGroup(p.attDeriv(context, attribute), createChoice(oneOrMore, empty)) - }, startTagCloseDeriv:memoize0arg(function() { - return createOneOrMore(p.startTagCloseDeriv()) - })} - }); - function createElement(nc, p) { - return{type:"element", nc:nc, nullable:false, textDeriv:function() { - return notAllowed - }, startTagOpenDeriv:function(node) { - if(nc.contains(node)) { - return createAfter(p, empty) - } - return notAllowed - }, attDeriv:function() { - return notAllowed - }, startTagCloseDeriv:function() { - return this - }} - } - function valueMatch(context, pattern, text) { - return pattern.nullable && /^\s+$/.test(text) || pattern.textDeriv(context, text).nullable + return selectors } - createAttribute = memoize2arg("attribute", undefined, function(nc, p) { - return{type:"attribute", nullable:false, nc:nc, p:p, attDeriv:function(context, attribute) { - if(nc.contains(attribute) && valueMatch(context, p, attribute.nodeValue)) { - return empty + function getDirectChild(node, ns, name) { + var e = node && node.firstElementChild; + while(e) { + if(e.namespaceURI === ns && e.localName === name) { + break } - return notAllowed - }, startTagCloseDeriv:function() { - return notAllowed - }} - }); - function createList() { - return{type:"list", nullable:false, hash:"list", textDeriv:function() { - return empty - }} + e = e.nextElementSibling + } + return e } - createValue = memoize1arg("value", function(value) { - return{type:"value", nullable:false, value:value, textDeriv:function(context, text) { - return text === value ? empty : notAllowed - }, attDeriv:function() { - return notAllowed - }, startTagCloseDeriv:function() { - return this - }} - }); - createData = memoize1arg("data", function(type) { - return{type:"data", nullable:false, dataType:type, textDeriv:function() { - return empty - }, attDeriv:function() { - return notAllowed - }, startTagCloseDeriv:function() { - return this - }} - }); - applyAfter = function applyAfter(f, p) { - var result; - if(p.type === "after") { - result = createAfter(p.p1, f(p.p2)) + function fixBorderWidth(value) { + var index = value.indexOf(" "), width, theRestOfBorderAttributes; + if(index !== -1) { + width = value.substring(0, index); + theRestOfBorderAttributes = value.substring(index) }else { - if(p.type === "choice") { - result = createChoice(applyAfter(f, p.p1), applyAfter(f, p.p2)) - }else { - result = p - } - } - return result - }; - function attsDeriv(context, pattern, attributes, position) { - if(pattern === notAllowed) { - return notAllowed - } - if(position >= attributes.length) { - return pattern - } - if(position === 0) { - position = 0 + width = value; + theRestOfBorderAttributes = "" } - var a = attributes.item(position); - while(a.namespaceURI === xmlnsns) { - position += 1; - if(position >= attributes.length) { - return pattern - } - a = attributes.item(position) + width = utils.parseLength(width); + if(width && (width.unit === "pt" && width.value < 0.75)) { + value = "0.75pt" + theRestOfBorderAttributes } - a = attsDeriv(context, pattern.attDeriv(context, attributes.item(position)), attributes, position + 1); - return a + return value } - function childrenDeriv(context, pattern, walker) { - var element = walker.currentNode, childNode = walker.firstChild(), childNodes = [], i, p; - while(childNode) { - if(childNode.nodeType === Node.ELEMENT_NODE) { - childNodes.push(childNode) - }else { - if(childNode.nodeType === Node.TEXT_NODE && !/^\s*$/.test(childNode.nodeValue)) { - childNodes.push(childNode.nodeValue) + function applySimpleMapping(props, mapping) { + var rule = "", i, r, value; + for(i = 0;i < mapping.length;i += 1) { + r = mapping[i]; + value = props.getAttributeNS(r[0], r[1]); + if(value) { + value = value.trim(); + if(borderPropertyMap.hasOwnProperty(r[1])) { + value = fixBorderWidth(value) } - } - childNode = walker.nextSibling() - } - if(childNodes.length === 0) { - childNodes = [""] - } - p = pattern; - for(i = 0;p !== notAllowed && i < childNodes.length;i += 1) { - childNode = childNodes[i]; - if(typeof childNode === "string") { - if(/^\s*$/.test(childNode)) { - p = createChoice(p, p.textDeriv(context, childNode)) - }else { - p = p.textDeriv(context, childNode) + if(r[2]) { + rule += r[2] + ":" + value + ";" } - }else { - walker.currentNode = childNode; - p = childDeriv(context, p, walker) } } - walker.currentNode = element; - return p + return rule } - childDeriv = function childDeriv(context, pattern, walker) { - var childNode = walker.currentNode, p; - p = pattern.startTagOpenDeriv(childNode); - p = attsDeriv(context, p, childNode.attributes, 0); - p = p.startTagCloseDeriv(); - p = childrenDeriv(context, p, walker); - p = p.endTagDeriv(); - return p - }; - function addNames(name, ns, pattern) { - if(pattern.e[0].a) { - name.push(pattern.e[0].text); - ns.push(pattern.e[0].a.ns) - }else { - addNames(name, ns, pattern.e[0]) + function getFontSize(styleNode) { + var props = getDirectChild(styleNode, stylens, "text-properties"); + if(props) { + return utils.parseFoFontSize(props.getAttributeNS(fons, "font-size")) } - if(pattern.e[1].a) { - name.push(pattern.e[1].text); - ns.push(pattern.e[1].a.ns) + return null + } + function getParentStyleNode(styleNode) { + var parentStyleName = "", parentStyleFamily = "", parentStyleNode = null, xp; + if(styleNode.localName === "default-style") { + return null + } + parentStyleName = styleNode.getAttributeNS(stylens, "parent-style-name"); + parentStyleFamily = styleNode.getAttributeNS(stylens, "family"); + if(parentStyleName) { + xp = "//style:*[@style:name='" + parentStyleName + "'][@style:family='" + parentStyleFamily + "']" }else { - addNames(name, ns, pattern.e[1]) + xp = "//style:default-style[@style:family='" + parentStyleFamily + "']" } + parentStyleNode = xpath.getODFElementsWithXPath((odfRoot), xp, odf.Namespaces.lookupNamespaceURI)[0]; + return parentStyleNode } - createNameClass = function createNameClass(pattern) { - var name, ns, hash, i, result; - if(pattern.name === "name") { - name = pattern.text; - ns = pattern.a.ns; - result = {name:name, ns:ns, hash:"{" + ns + "}" + name, contains:function(node) { - return node.namespaceURI === ns && node.localName === name - }} - }else { - if(pattern.name === "choice") { - name = []; - ns = []; - addNames(name, ns, pattern); - hash = ""; - for(i = 0;i < name.length;i += 1) { - hash += "{" + ns[i] + "}" + name[i] + "," - } - result = {hash:hash, contains:function(node) { - var j; - for(j = 0;j < name.length;j += 1) { - if(name[j] === node.localName && ns[j] === node.namespaceURI) { - return true - } - } - return false - }} - }else { - result = {hash:"anyName", contains:function() { - return true - }} - } + function getTextProperties(props) { + var rule = "", fontName, fontSize, value, textDecoration = "", fontSizeRule = "", sizeMultiplier = 1, parentStyle; + rule += applySimpleMapping(props, textPropertySimpleMapping); + value = props.getAttributeNS(stylens, "text-underline-style"); + if(value === "solid") { + textDecoration += " underline" } - return result - }; - function resolveElement(pattern, elements) { - var element, p, i, hash; - hash = "element" + pattern.id.toString(); - p = elements[pattern.id] = {hash:hash}; - element = createElement(createNameClass(pattern.e[0]), makePattern(pattern.e[1], elements)); - for(i in element) { - if(element.hasOwnProperty(i)) { - p[i] = element[i] - } + value = props.getAttributeNS(stylens, "text-line-through-style"); + if(value === "solid") { + textDecoration += " line-through" } - return p - } - makePattern = function makePattern(pattern, elements) { - var p, i; - if(pattern.name === "elementref") { - p = pattern.id || 0; - pattern = elements[p]; - if(pattern.name !== undefined) { - return resolveElement(pattern, elements) - } - return pattern + if(textDecoration.length) { + textDecoration = "text-decoration:" + textDecoration + ";"; + rule += textDecoration } - switch(pattern.name) { - case "empty": - return empty; - case "notAllowed": - return notAllowed; - case "text": - return text; - case "choice": - return createChoice(makePattern(pattern.e[0], elements), makePattern(pattern.e[1], elements)); - case "interleave": - p = makePattern(pattern.e[0], elements); - for(i = 1;i < pattern.e.length;i += 1) { - p = createInterleave(p, makePattern(pattern.e[i], elements)) - } - return p; - case "group": - return createGroup(makePattern(pattern.e[0], elements), makePattern(pattern.e[1], elements)); - case "oneOrMore": - return createOneOrMore(makePattern(pattern.e[0], elements)); - case "attribute": - return createAttribute(createNameClass(pattern.e[0]), makePattern(pattern.e[1], elements)); - case "value": - return createValue(pattern.text); - case "data": - p = pattern.a && pattern.a.type; - if(p === undefined) { - p = "" - } - return createData(p); - case "list": - return createList() + fontName = props.getAttributeNS(stylens, "font-name") || props.getAttributeNS(fons, "font-family"); + if(fontName) { + value = fontFaceDeclsMap[fontName]; + rule += "font-family: " + (value || fontName) + ";" } - throw"No support for " + pattern.name; - }; - this.makePattern = function(pattern, elements) { - var copy = {}, i; - for(i in elements) { - if(elements.hasOwnProperty(i)) { - copy[i] = elements[i] + parentStyle = props.parentElement; + fontSize = getFontSize(parentStyle); + if(!fontSize) { + return rule + } + while(parentStyle) { + fontSize = getFontSize(parentStyle); + if(fontSize) { + if(fontSize.unit !== "%") { + fontSizeRule = "font-size: " + fontSize.value * sizeMultiplier + fontSize.unit + ";"; + break + } + sizeMultiplier *= fontSize.value / 100 } + parentStyle = getParentStyleNode(parentStyle) } - i = makePattern(pattern, copy); - return i - }; - this.validate = function validate(walker, callback) { - var errors; - walker.currentNode = walker.root; - errors = childDeriv(null, rootPattern, walker); - if(!errors.nullable) { - runtime.log("Error in Relax NG validation: " + errors); - callback(["Error in Relax NG validation: " + errors]) - }else { - callback(null) + if(!fontSizeRule) { + fontSizeRule = "font-size: " + parseFloat(defaultFontSize) * sizeMultiplier + cssUnits.getUnits(defaultFontSize) + ";" } - }; - this.init = function init(rootPattern1) { - rootPattern = rootPattern1 + rule += fontSizeRule; + return rule } -}; -runtime.loadClass("xmldom.RelaxNGParser"); -xmldom.RelaxNG2 = function RelaxNG2() { - var start, validateNonEmptyPattern, nsmap; - function RelaxNGParseError(error, context) { - this.message = function() { - if(context) { - error += context.nodeType === Node.ELEMENT_NODE ? " Element " : " Node "; - error += context.nodeName; - if(context.nodeValue) { - error += " with value '" + context.nodeValue + "'" - } - error += "." + function getParagraphProperties(props) { + var rule = "", bgimage, url, lineHeight; + rule += applySimpleMapping(props, paragraphPropertySimpleMapping); + bgimage = getDirectChild(props, stylens, "background-image"); + if(bgimage) { + url = bgimage.getAttributeNS(xlinkns, "href"); + if(url) { + rule += "background-image: url('odfkit:" + url + "');"; + rule += applySimpleMapping(bgimage, bgImageSimpleMapping) } - return error } - } - function validateOneOrMore(elementdef, walker, element) { - var node, i = 0, err; - do { - node = walker.currentNode; - err = validateNonEmptyPattern(elementdef.e[0], walker, element); - i += 1 - }while(!err && node !== walker.currentNode); - if(i > 1) { - walker.currentNode = node; - return null + lineHeight = props.getAttributeNS(fons, "line-height"); + if(lineHeight && lineHeight !== "normal") { + lineHeight = utils.parseFoLineHeight(lineHeight); + if(lineHeight.unit !== "%") { + rule += "line-height: " + lineHeight.value + lineHeight.unit + ";" + }else { + rule += "line-height: " + lineHeight.value / 100 + ";" + } } - return err + return rule } - function qName(node) { - return nsmap[node.namespaceURI] + ":" + node.localName + function matchToRgb(m, r, g, b) { + return r + r + g + g + b + b } - function isWhitespace(node) { - return node && (node.nodeType === Node.TEXT_NODE && /^\s+$/.test(node.nodeValue)) + function hexToRgb(hex) { + var result, shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, matchToRgb); + result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? {r:parseInt(result[1], 16), g:parseInt(result[2], 16), b:parseInt(result[3], 16)} : null } - function validatePattern(elementdef, walker, element, data) { - if(elementdef.name === "empty") { - return null - } - return validateNonEmptyPattern(elementdef, walker, element, data) + function isNumber(n) { + return!isNaN(parseFloat(n)) } - function validateAttribute(elementdef, walker, element) { - if(elementdef.e.length !== 2) { - throw"Attribute with wrong # of elements: " + elementdef.e.length; - } - var att, a, l = elementdef.localnames.length, i; - for(i = 0;i < l;i += 1) { - a = element.getAttributeNS(elementdef.namespaces[i], elementdef.localnames[i]); - if(a === "" && !element.hasAttributeNS(elementdef.namespaces[i], elementdef.localnames[i])) { - a = undefined + function getGraphicProperties(props) { + var rule = "", alpha, bgcolor, fill; + rule += applySimpleMapping(props, graphicPropertySimpleMapping); + alpha = props.getAttributeNS(drawns, "opacity"); + fill = props.getAttributeNS(drawns, "fill"); + bgcolor = props.getAttributeNS(drawns, "fill-color"); + if(fill === "solid" || fill === "hatch") { + if(bgcolor && bgcolor !== "none") { + alpha = isNumber(alpha) ? parseFloat(alpha) / 100 : 1; + bgcolor = hexToRgb(bgcolor); + if(bgcolor) { + rule += "background-color: rgba(" + bgcolor.r + "," + bgcolor.g + "," + bgcolor.b + "," + alpha + ");" + } + }else { + rule += "background: none;" } - if(att !== undefined && a !== undefined) { - return[new RelaxNGParseError("Attribute defined too often.", element)] + }else { + if(fill === "none") { + rule += "background: none;" } - att = a - } - if(att === undefined) { - return[new RelaxNGParseError("Attribute not found: " + elementdef.names, element)] } - return validatePattern(elementdef.e[1], walker, element, att) + return rule } - function validateTop(elementdef, walker, element) { - return validatePattern(elementdef, walker, element) - } - function validateElement(elementdef, walker) { - if(elementdef.e.length !== 2) { - throw"Element with wrong # of elements: " + elementdef.e.length; + function getDrawingPageProperties(props) { + var rule = ""; + rule += applySimpleMapping(props, graphicPropertySimpleMapping); + if(props.getAttributeNS(presentationns, "background-visible") === "true") { + rule += "background: none;" } - var node = walker.currentNode, type = node ? node.nodeType : 0, error = null; - while(type > Node.ELEMENT_NODE) { - if(type !== Node.COMMENT_NODE && (type !== Node.TEXT_NODE || !/^\s+$/.test(walker.currentNode.nodeValue))) { - return[new RelaxNGParseError("Not allowed node of type " + type + ".")] + return rule + } + function getTableCellProperties(props) { + var rule = ""; + rule += applySimpleMapping(props, tablecellPropertySimpleMapping); + return rule + } + function getTableRowProperties(props) { + var rule = ""; + rule += applySimpleMapping(props, tablerowPropertySimpleMapping); + return rule + } + function getTableColumnProperties(props) { + var rule = ""; + rule += applySimpleMapping(props, tablecolumnPropertySimpleMapping); + return rule + } + function getTableProperties(props) { + var rule = "", borderModel; + rule += applySimpleMapping(props, tablePropertySimpleMapping); + borderModel = props.getAttributeNS(tablens, "border-model"); + if(borderModel === "collapsing") { + rule += "border-collapse:collapse;" + }else { + if(borderModel === "separating") { + rule += "border-collapse:separate;" } - node = walker.nextSibling(); - type = node ? node.nodeType : 0 } - if(!node) { - return[new RelaxNGParseError("Missing element " + elementdef.names)] + return rule + } + function addStyleRule(sheet, family, name, node) { + var selectors = getSelectors(family, name, node), selector = selectors.join(","), rule = "", properties; + properties = getDirectChild(node.element, stylens, "text-properties"); + if(properties) { + rule += getTextProperties(properties) } - if(elementdef.names && elementdef.names.indexOf(qName(node)) === -1) { - return[new RelaxNGParseError("Found " + node.nodeName + " instead of " + elementdef.names + ".", node)] + properties = getDirectChild(node.element, stylens, "paragraph-properties"); + if(properties) { + rule += getParagraphProperties(properties) } - if(walker.firstChild()) { - error = validateTop(elementdef.e[1], walker, node); - while(walker.nextSibling()) { - type = walker.currentNode.nodeType; - if(!isWhitespace(walker.currentNode) && type !== Node.COMMENT_NODE) { - return[new RelaxNGParseError("Spurious content.", walker.currentNode)] - } - } - if(walker.parentNode() !== node) { - return[new RelaxNGParseError("Implementation error.")] - } + properties = getDirectChild(node.element, stylens, "graphic-properties"); + if(properties) { + rule += getGraphicProperties(properties) + } + properties = getDirectChild(node.element, stylens, "drawing-page-properties"); + if(properties) { + rule += getDrawingPageProperties(properties) + } + properties = getDirectChild(node.element, stylens, "table-cell-properties"); + if(properties) { + rule += getTableCellProperties(properties) + } + properties = getDirectChild(node.element, stylens, "table-row-properties"); + if(properties) { + rule += getTableRowProperties(properties) + } + properties = getDirectChild(node.element, stylens, "table-column-properties"); + if(properties) { + rule += getTableColumnProperties(properties) + } + properties = getDirectChild(node.element, stylens, "table-properties"); + if(properties) { + rule += getTableProperties(properties) + } + if(rule.length === 0) { + return + } + rule = selector + "{" + rule + "}"; + try { + sheet.insertRule(rule, sheet.cssRules.length) + }catch(e) { + throw e; + } + } + function getNumberRule(node) { + var style = node.getAttributeNS(stylens, "num-format"), suffix = node.getAttributeNS(stylens, "num-suffix") || "", prefix = node.getAttributeNS(stylens, "num-prefix") || "", stylemap = {1:"decimal", "a":"lower-latin", "A":"upper-latin", "i":"lower-roman", "I":"upper-roman"}, content = ""; + if(prefix) { + content += ' "' + prefix + '"' + } + if(stylemap.hasOwnProperty(style)) { + content += " counter(list, " + stylemap[style] + ")" }else { - error = validateTop(elementdef.e[1], walker, node) + if(style) { + content += ' "' + style + '"' + }else { + content += " ''" + } } - node = walker.nextSibling(); - return error + return"content:" + content + ' "' + suffix + '"' } - function validateChoice(elementdef, walker, element, data) { - if(elementdef.e.length !== 2) { - throw"Choice with wrong # of options: " + elementdef.e.length; + function getImageRule() { + return"content: none;" + } + function getBulletRule(node) { + var bulletChar = node.getAttributeNS(textns, "bullet-char"); + return"content: '" + bulletChar + "';" + } + function addListStyleRule(sheet, name, node, itemrule) { + var selector = 'text|list[text|style-name="' + name + '"]', level = node.getAttributeNS(textns, "level"), itemSelector, listItemRule, listLevelProps = getDirectChild(node, stylens, "list-level-properties"), listLevelLabelAlign = getDirectChild(listLevelProps, stylens, "list-level-label-alignment"), bulletIndent, listIndent, bulletWidth, rule; + if(listLevelLabelAlign) { + bulletIndent = listLevelLabelAlign.getAttributeNS(fons, "text-indent"); + listIndent = listLevelLabelAlign.getAttributeNS(fons, "margin-left") } - var node = walker.currentNode, err; - if(elementdef.e[0].name === "empty") { - err = validateNonEmptyPattern(elementdef.e[1], walker, element, data); - if(err) { - walker.currentNode = node + if(!bulletIndent) { + bulletIndent = "-0.6cm" + } + if(bulletIndent.charAt(0) === "-") { + bulletWidth = bulletIndent.substring(1) + }else { + bulletWidth = "-" + bulletIndent + } + level = level && parseInt(level, 10); + while(level > 1) { + selector += " > text|list-item > text|list"; + level -= 1 + } + if(listIndent) { + itemSelector = selector; + itemSelector += " > text|list-item > *:not(text|list):first-child"; + listItemRule = itemSelector + "{"; + listItemRule += "margin-left:" + listIndent + ";"; + listItemRule += "}"; + try { + sheet.insertRule(listItemRule, sheet.cssRules.length) + }catch(e1) { + runtime.log("cannot load rule: " + listItemRule) } - return null } - err = validatePattern(elementdef.e[0], walker, element, data); - if(err) { - walker.currentNode = node; - err = validateNonEmptyPattern(elementdef.e[1], walker, element, data) + selector += " > text|list-item > *:not(text|list):first-child:before"; + rule = selector + "{" + itemrule + ";"; + rule += "counter-increment:list;"; + rule += "margin-left:" + bulletIndent + ";"; + rule += "width:" + bulletWidth + ";"; + rule += "display:inline-block}"; + try { + sheet.insertRule(rule, sheet.cssRules.length) + }catch(e2) { + runtime.log("cannot load rule: " + rule) } - return err } - function validateInterleave(elementdef, walker, element) { - var l = elementdef.e.length, n = [l], err, i, todo = l, donethisround, node, subnode, e; - while(todo > 0) { - donethisround = 0; - node = walker.currentNode; - for(i = 0;i < l;i += 1) { - subnode = walker.currentNode; - if(n[i] !== true && n[i] !== subnode) { - e = elementdef.e[i]; - err = validateNonEmptyPattern(e, walker, element); - if(err) { - walker.currentNode = subnode; - if(n[i] === undefined) { - n[i] = false - } - }else { - if(subnode === walker.currentNode || (e.name === "oneOrMore" || e.name === "choice" && (e.e[0].name === "oneOrMore" || e.e[1].name === "oneOrMore"))) { - donethisround += 1; - n[i] = subnode - }else { - donethisround += 1; - n[i] = true - } + function addPageStyleRules(sheet, node) { + var rule = "", imageProps, url, contentLayoutRule = "", pageSizeRule = "", props = getDirectChild(node, stylens, "page-layout-properties"), stylename, masterStyles, e, masterStyleName; + if(!props) { + return + } + stylename = node.getAttributeNS(stylens, "name"); + rule += applySimpleMapping(props, pageContentPropertySimpleMapping); + imageProps = getDirectChild(props, stylens, "background-image"); + if(imageProps) { + url = imageProps.getAttributeNS(xlinkns, "href"); + if(url) { + rule += "background-image: url('odfkit:" + url + "');"; + rule += applySimpleMapping(imageProps, bgImageSimpleMapping) + } + } + if(documentType === "presentation") { + masterStyles = getDirectChild(node.parentNode.parentElement, officens, "master-styles"); + e = masterStyles && masterStyles.firstElementChild; + while(e) { + if(e.namespaceURI === stylens && (e.localName === "master-page" && e.getAttributeNS(stylens, "page-layout-name") === stylename)) { + masterStyleName = e.getAttributeNS(stylens, "name"); + contentLayoutRule = "draw|page[draw|master-page-name=" + masterStyleName + "] {" + rule + "}"; + pageSizeRule = "office|body, draw|page[draw|master-page-name=" + masterStyleName + "] {" + applySimpleMapping(props, pageSizePropertySimpleMapping) + " }"; + try { + sheet.insertRule(contentLayoutRule, sheet.cssRules.length); + sheet.insertRule(pageSizeRule, sheet.cssRules.length) + }catch(e1) { + throw e1; } } + e = e.nextElementSibling } - if(node === walker.currentNode && donethisround === todo) { - return null - } - if(donethisround === 0) { - for(i = 0;i < l;i += 1) { - if(n[i] === false) { - return[new RelaxNGParseError("Interleave does not match.", element)] - } + }else { + if(documentType === "text") { + contentLayoutRule = "office|text {" + rule + "}"; + rule = ""; + pageSizeRule = "office|body {" + "width: " + props.getAttributeNS(fons, "page-width") + ";" + "}"; + try { + sheet.insertRule(contentLayoutRule, sheet.cssRules.length); + sheet.insertRule(pageSizeRule, sheet.cssRules.length) + }catch(e2) { + throw e2; } - return null } - todo = 0; - for(i = 0;i < l;i += 1) { - if(n[i] !== true) { - todo += 1 + } + } + function addListStyleRules(sheet, name, node) { + var n = node.firstChild, e, itemrule; + while(n) { + if(n.namespaceURI === textns) { + e = (n); + if(n.localName === "list-level-style-number") { + itemrule = getNumberRule(e); + addListStyleRule(sheet, name, e, itemrule) + }else { + if(n.localName === "list-level-style-image") { + itemrule = getImageRule(); + addListStyleRule(sheet, name, e, itemrule) + }else { + if(n.localName === "list-level-style-bullet") { + itemrule = getBulletRule(e); + addListStyleRule(sheet, name, e, itemrule) + } + } } } + n = n.nextSibling } - return null } - function validateGroup(elementdef, walker, element) { - if(elementdef.e.length !== 2) { - throw"Group with wrong # of members: " + elementdef.e.length; + function addRule(sheet, family, name, node) { + if(family === "list") { + addListStyleRules(sheet, name, node.element) + }else { + if(family === "page") { + addPageStyleRules(sheet, node.element) + }else { + addStyleRule(sheet, family, name, node) + } } - return validateNonEmptyPattern(elementdef.e[0], walker, element) || validateNonEmptyPattern(elementdef.e[1], walker, element) } - function validateText(elementdef, walker, element) { - var node = walker.currentNode, type = node ? node.nodeType : 0; - while(node !== element && type !== 3) { - if(type === 1) { - return[new RelaxNGParseError("Element not allowed here.", node)] + function addRules(sheet, family, name, node) { + addRule(sheet, family, name, node); + var n; + for(n in node.derivedStyles) { + if(node.derivedStyles.hasOwnProperty(n)) { + addRules(sheet, family, n, node.derivedStyles[n]) } - node = walker.nextSibling(); - type = node ? node.nodeType : 0 } - walker.nextSibling(); - return null } - validateNonEmptyPattern = function validateNonEmptyPattern(elementdef, walker, element, data) { - var name = elementdef.name, err = null; - if(name === "text") { - err = validateText(elementdef, walker, element) - }else { - if(name === "data") { - err = null - }else { - if(name === "value") { - if(data !== elementdef.text) { - err = [new RelaxNGParseError("Wrong value, should be '" + elementdef.text + "', not '" + data + "'", element)] - } - }else { - if(name === "list") { - err = null - }else { - if(name === "attribute") { - err = validateAttribute(elementdef, walker, element) - }else { - if(name === "element") { - err = validateElement(elementdef, walker) - }else { - if(name === "oneOrMore") { - err = validateOneOrMore(elementdef, walker, element) - }else { - if(name === "choice") { - err = validateChoice(elementdef, walker, element, data) - }else { - if(name === "group") { - err = validateGroup(elementdef, walker, element) - }else { - if(name === "interleave") { - err = validateInterleave(elementdef, walker, element) - }else { - throw name + " not allowed in nonEmptyPattern."; - } - } - } - } - } - } + this.style2css = function(doctype, stylesheet, fontFaceMap, styles, autostyles) { + var doc, styletree, tree, rule, name, family, stylenodes, styleautonodes; + while(stylesheet.cssRules.length) { + stylesheet.deleteRule(stylesheet.cssRules.length - 1) + } + doc = null; + if(styles) { + doc = styles.ownerDocument; + odfRoot = styles.parentNode + } + if(autostyles) { + doc = autostyles.ownerDocument; + odfRoot = autostyles.parentNode + } + if(!doc) { + return + } + odf.Namespaces.forEachPrefix(function(prefix, ns) { + rule = "@namespace " + prefix + " url(" + ns + ");"; + try { + stylesheet.insertRule(rule, stylesheet.cssRules.length) + }catch(ignore) { + } + }); + fontFaceDeclsMap = fontFaceMap; + documentType = doctype; + defaultFontSize = runtime.getWindow().getComputedStyle(document.body, null).getPropertyValue("font-size") || "12pt"; + stylenodes = getStyleMap(styles); + styleautonodes = getStyleMap(autostyles); + styletree = {}; + for(family in familynamespaceprefixes) { + if(familynamespaceprefixes.hasOwnProperty(family)) { + tree = styletree[family] = {}; + addStyleMapToStyleTree(stylenodes[family], tree); + addStyleMapToStyleTree(styleautonodes[family], tree); + for(name in tree) { + if(tree.hasOwnProperty(name)) { + addRules(stylesheet, family, name, tree[name]) } } } } - return err - }; - this.validate = function validate(walker, callback) { - walker.currentNode = walker.root; - var errors = validatePattern(start.e[0], walker, (walker.root)); - callback(errors) - }; - this.init = function init(start1, nsmap1) { - start = start1; - nsmap = nsmap1 } }; -xmldom.XPathIterator = function XPathIterator() { -}; -xmldom.XPath = function() { - var createXPathPathIterator, parsePredicates; - function isSmallestPositive(a, b, c) { - return a !== -1 && ((a < b || b === -1) && (a < c || c === -1)) - } - function parseXPathStep(xpath, pos, end, steps) { - var location = "", predicates = [], brapos = xpath.indexOf("[", pos), slapos = xpath.indexOf("/", pos), eqpos = xpath.indexOf("=", pos); - if(isSmallestPositive(slapos, brapos, eqpos)) { - location = xpath.substring(pos, slapos); - pos = slapos + 1 - }else { - if(isSmallestPositive(brapos, slapos, eqpos)) { - location = xpath.substring(pos, brapos); - pos = parsePredicates(xpath, brapos, predicates) - }else { - if(isSmallestPositive(eqpos, slapos, brapos)) { - location = xpath.substring(pos, eqpos); - pos = eqpos - }else { - location = xpath.substring(pos, end); - pos = end - } - } +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("xmldom.XPath"); +runtime.loadClass("odf.Namespaces"); +odf.StyleInfo = function StyleInfo() { + var chartns = odf.Namespaces.chartns, dbns = odf.Namespaces.dbns, dr3dns = odf.Namespaces.dr3dns, drawns = odf.Namespaces.drawns, formns = odf.Namespaces.formns, numberns = odf.Namespaces.numberns, officens = odf.Namespaces.officens, presentationns = odf.Namespaces.presentationns, stylens = odf.Namespaces.stylens, tablens = odf.Namespaces.tablens, textns = odf.Namespaces.textns, nsprefixes = {"urn:oasis:names:tc:opendocument:xmlns:chart:1.0":"chart:", "urn:oasis:names:tc:opendocument:xmlns:database:1.0":"db:", + "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0":"dr3d:", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0":"draw:", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0":"fo:", "urn:oasis:names:tc:opendocument:xmlns:form:1.0":"form:", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0":"number:", "urn:oasis:names:tc:opendocument:xmlns:office:1.0":"office:", "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0":"presentation:", "urn:oasis:names:tc:opendocument:xmlns:style:1.0":"style:", + "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0":"svg:", "urn:oasis:names:tc:opendocument:xmlns:table:1.0":"table:", "urn:oasis:names:tc:opendocument:xmlns:text:1.0":"chart:", "http://www.w3.org/XML/1998/namespace":"xml:"}, elementstyles = {"text":[{ens:stylens, en:"tab-stop", ans:stylens, a:"leader-text-style"}, {ens:stylens, en:"drop-cap", ans:stylens, a:"style-name"}, {ens:textns, en:"notes-configuration", ans:textns, a:"citation-body-style-name"}, {ens:textns, en:"notes-configuration", + ans:textns, a:"citation-style-name"}, {ens:textns, en:"a", ans:textns, a:"style-name"}, {ens:textns, en:"alphabetical-index", ans:textns, a:"style-name"}, {ens:textns, en:"linenumbering-configuration", ans:textns, a:"style-name"}, {ens:textns, en:"list-level-style-number", ans:textns, a:"style-name"}, {ens:textns, en:"ruby-text", ans:textns, a:"style-name"}, {ens:textns, en:"span", ans:textns, a:"style-name"}, {ens:textns, en:"a", ans:textns, a:"visited-style-name"}, {ens:stylens, en:"text-properties", + ans:stylens, a:"text-line-through-text-style"}, {ens:textns, en:"alphabetical-index-source", ans:textns, a:"main-entry-style-name"}, {ens:textns, en:"index-entry-bibliography", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-chapter", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-link-end", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-link-start", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-page-number", ans:textns, a:"style-name"}, {ens:textns, + en:"index-entry-span", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-tab-stop", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-text", ans:textns, a:"style-name"}, {ens:textns, en:"index-title-template", ans:textns, a:"style-name"}, {ens:textns, en:"list-level-style-bullet", ans:textns, a:"style-name"}, {ens:textns, en:"outline-level-style", ans:textns, a:"style-name"}], "paragraph":[{ens:drawns, en:"caption", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"circle", ans:drawns, + a:"text-style-name"}, {ens:drawns, en:"connector", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"control", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"custom-shape", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"ellipse", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"frame", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"line", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"measure", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"path", ans:drawns, a:"text-style-name"}, + {ens:drawns, en:"polygon", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"polyline", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"rect", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"regular-polygon", ans:drawns, a:"text-style-name"}, {ens:officens, en:"annotation", ans:drawns, a:"text-style-name"}, {ens:formns, en:"column", ans:formns, a:"text-style-name"}, {ens:stylens, en:"style", ans:stylens, a:"next-style-name"}, {ens:tablens, en:"body", ans:tablens, a:"paragraph-style-name"}, + {ens:tablens, en:"even-columns", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"even-rows", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"first-column", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"first-row", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"last-column", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"last-row", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"odd-columns", ans:tablens, a:"paragraph-style-name"}, + {ens:tablens, en:"odd-rows", ans:tablens, a:"paragraph-style-name"}, {ens:textns, en:"notes-configuration", ans:textns, a:"default-style-name"}, {ens:textns, en:"alphabetical-index-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"bibliography-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"h", ans:textns, a:"style-name"}, {ens:textns, en:"illustration-index-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"index-source-style", ans:textns, a:"style-name"}, + {ens:textns, en:"object-index-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"p", ans:textns, a:"style-name"}, {ens:textns, en:"table-index-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"table-of-content-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"table-index-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"user-index-entry-template", ans:textns, a:"style-name"}, {ens:stylens, en:"page-layout-properties", ans:stylens, a:"register-truth-ref-style-name"}], + "chart":[{ens:chartns, en:"axis", ans:chartns, a:"style-name"}, {ens:chartns, en:"chart", ans:chartns, a:"style-name"}, {ens:chartns, en:"data-label", ans:chartns, a:"style-name"}, {ens:chartns, en:"data-point", ans:chartns, a:"style-name"}, {ens:chartns, en:"equation", ans:chartns, a:"style-name"}, {ens:chartns, en:"error-indicator", ans:chartns, a:"style-name"}, {ens:chartns, en:"floor", ans:chartns, a:"style-name"}, {ens:chartns, en:"footer", ans:chartns, a:"style-name"}, {ens:chartns, en:"grid", + ans:chartns, a:"style-name"}, {ens:chartns, en:"legend", ans:chartns, a:"style-name"}, {ens:chartns, en:"mean-value", ans:chartns, a:"style-name"}, {ens:chartns, en:"plot-area", ans:chartns, a:"style-name"}, {ens:chartns, en:"regression-curve", ans:chartns, a:"style-name"}, {ens:chartns, en:"series", ans:chartns, a:"style-name"}, {ens:chartns, en:"stock-gain-marker", ans:chartns, a:"style-name"}, {ens:chartns, en:"stock-loss-marker", ans:chartns, a:"style-name"}, {ens:chartns, en:"stock-range-line", + ans:chartns, a:"style-name"}, {ens:chartns, en:"subtitle", ans:chartns, a:"style-name"}, {ens:chartns, en:"title", ans:chartns, a:"style-name"}, {ens:chartns, en:"wall", ans:chartns, a:"style-name"}], "section":[{ens:textns, en:"alphabetical-index", ans:textns, a:"style-name"}, {ens:textns, en:"bibliography", ans:textns, a:"style-name"}, {ens:textns, en:"illustration-index", ans:textns, a:"style-name"}, {ens:textns, en:"index-title", ans:textns, a:"style-name"}, {ens:textns, en:"object-index", + ans:textns, a:"style-name"}, {ens:textns, en:"section", ans:textns, a:"style-name"}, {ens:textns, en:"table-of-content", ans:textns, a:"style-name"}, {ens:textns, en:"table-index", ans:textns, a:"style-name"}, {ens:textns, en:"user-index", ans:textns, a:"style-name"}], "ruby":[{ens:textns, en:"ruby", ans:textns, a:"style-name"}], "table":[{ens:dbns, en:"query", ans:dbns, a:"style-name"}, {ens:dbns, en:"table-representation", ans:dbns, a:"style-name"}, {ens:tablens, en:"background", ans:tablens, + a:"style-name"}, {ens:tablens, en:"table", ans:tablens, a:"style-name"}], "table-column":[{ens:dbns, en:"column", ans:dbns, a:"style-name"}, {ens:tablens, en:"table-column", ans:tablens, a:"style-name"}], "table-row":[{ens:dbns, en:"query", ans:dbns, a:"default-row-style-name"}, {ens:dbns, en:"table-representation", ans:dbns, a:"default-row-style-name"}, {ens:tablens, en:"table-row", ans:tablens, a:"style-name"}], "table-cell":[{ens:dbns, en:"column", ans:dbns, a:"default-cell-style-name"}, {ens:tablens, + en:"table-column", ans:tablens, a:"default-cell-style-name"}, {ens:tablens, en:"table-row", ans:tablens, a:"default-cell-style-name"}, {ens:tablens, en:"body", ans:tablens, a:"style-name"}, {ens:tablens, en:"covered-table-cell", ans:tablens, a:"style-name"}, {ens:tablens, en:"even-columns", ans:tablens, a:"style-name"}, {ens:tablens, en:"covered-table-cell", ans:tablens, a:"style-name"}, {ens:tablens, en:"even-columns", ans:tablens, a:"style-name"}, {ens:tablens, en:"even-rows", ans:tablens, a:"style-name"}, + {ens:tablens, en:"first-column", ans:tablens, a:"style-name"}, {ens:tablens, en:"first-row", ans:tablens, a:"style-name"}, {ens:tablens, en:"last-column", ans:tablens, a:"style-name"}, {ens:tablens, en:"last-row", ans:tablens, a:"style-name"}, {ens:tablens, en:"odd-columns", ans:tablens, a:"style-name"}, {ens:tablens, en:"odd-rows", ans:tablens, a:"style-name"}, {ens:tablens, en:"table-cell", ans:tablens, a:"style-name"}], "graphic":[{ens:dr3dns, en:"cube", ans:drawns, a:"style-name"}, {ens:dr3dns, + en:"extrude", ans:drawns, a:"style-name"}, {ens:dr3dns, en:"rotate", ans:drawns, a:"style-name"}, {ens:dr3dns, en:"scene", ans:drawns, a:"style-name"}, {ens:dr3dns, en:"sphere", ans:drawns, a:"style-name"}, {ens:drawns, en:"caption", ans:drawns, a:"style-name"}, {ens:drawns, en:"circle", ans:drawns, a:"style-name"}, {ens:drawns, en:"connector", ans:drawns, a:"style-name"}, {ens:drawns, en:"control", ans:drawns, a:"style-name"}, {ens:drawns, en:"custom-shape", ans:drawns, a:"style-name"}, {ens:drawns, + en:"ellipse", ans:drawns, a:"style-name"}, {ens:drawns, en:"frame", ans:drawns, a:"style-name"}, {ens:drawns, en:"g", ans:drawns, a:"style-name"}, {ens:drawns, en:"line", ans:drawns, a:"style-name"}, {ens:drawns, en:"measure", ans:drawns, a:"style-name"}, {ens:drawns, en:"page-thumbnail", ans:drawns, a:"style-name"}, {ens:drawns, en:"path", ans:drawns, a:"style-name"}, {ens:drawns, en:"polygon", ans:drawns, a:"style-name"}, {ens:drawns, en:"polyline", ans:drawns, a:"style-name"}, {ens:drawns, en:"rect", + ans:drawns, a:"style-name"}, {ens:drawns, en:"regular-polygon", ans:drawns, a:"style-name"}, {ens:officens, en:"annotation", ans:drawns, a:"style-name"}], "presentation":[{ens:dr3dns, en:"cube", ans:presentationns, a:"style-name"}, {ens:dr3dns, en:"extrude", ans:presentationns, a:"style-name"}, {ens:dr3dns, en:"rotate", ans:presentationns, a:"style-name"}, {ens:dr3dns, en:"scene", ans:presentationns, a:"style-name"}, {ens:dr3dns, en:"sphere", ans:presentationns, a:"style-name"}, {ens:drawns, en:"caption", + ans:presentationns, a:"style-name"}, {ens:drawns, en:"circle", ans:presentationns, a:"style-name"}, {ens:drawns, en:"connector", ans:presentationns, a:"style-name"}, {ens:drawns, en:"control", ans:presentationns, a:"style-name"}, {ens:drawns, en:"custom-shape", ans:presentationns, a:"style-name"}, {ens:drawns, en:"ellipse", ans:presentationns, a:"style-name"}, {ens:drawns, en:"frame", ans:presentationns, a:"style-name"}, {ens:drawns, en:"g", ans:presentationns, a:"style-name"}, {ens:drawns, en:"line", + ans:presentationns, a:"style-name"}, {ens:drawns, en:"measure", ans:presentationns, a:"style-name"}, {ens:drawns, en:"page-thumbnail", ans:presentationns, a:"style-name"}, {ens:drawns, en:"path", ans:presentationns, a:"style-name"}, {ens:drawns, en:"polygon", ans:presentationns, a:"style-name"}, {ens:drawns, en:"polyline", ans:presentationns, a:"style-name"}, {ens:drawns, en:"rect", ans:presentationns, a:"style-name"}, {ens:drawns, en:"regular-polygon", ans:presentationns, a:"style-name"}, {ens:officens, + en:"annotation", ans:presentationns, a:"style-name"}], "drawing-page":[{ens:drawns, en:"page", ans:drawns, a:"style-name"}, {ens:presentationns, en:"notes", ans:drawns, a:"style-name"}, {ens:stylens, en:"handout-master", ans:drawns, a:"style-name"}, {ens:stylens, en:"master-page", ans:drawns, a:"style-name"}], "list-style":[{ens:textns, en:"list", ans:textns, a:"style-name"}, {ens:textns, en:"numbered-paragraph", ans:textns, a:"style-name"}, {ens:textns, en:"list-item", ans:textns, a:"style-override"}, + {ens:stylens, en:"style", ans:stylens, a:"list-style-name"}], "data":[{ens:stylens, en:"style", ans:stylens, a:"data-style-name"}, {ens:stylens, en:"style", ans:stylens, a:"percentage-data-style-name"}, {ens:presentationns, en:"date-time-decl", ans:stylens, a:"data-style-name"}, {ens:textns, en:"creation-date", ans:stylens, a:"data-style-name"}, {ens:textns, en:"creation-time", ans:stylens, a:"data-style-name"}, {ens:textns, en:"database-display", ans:stylens, a:"data-style-name"}, {ens:textns, + en:"date", ans:stylens, a:"data-style-name"}, {ens:textns, en:"editing-duration", ans:stylens, a:"data-style-name"}, {ens:textns, en:"expression", ans:stylens, a:"data-style-name"}, {ens:textns, en:"meta-field", ans:stylens, a:"data-style-name"}, {ens:textns, en:"modification-date", ans:stylens, a:"data-style-name"}, {ens:textns, en:"modification-time", ans:stylens, a:"data-style-name"}, {ens:textns, en:"print-date", ans:stylens, a:"data-style-name"}, {ens:textns, en:"print-time", ans:stylens, + a:"data-style-name"}, {ens:textns, en:"table-formula", ans:stylens, a:"data-style-name"}, {ens:textns, en:"time", ans:stylens, a:"data-style-name"}, {ens:textns, en:"user-defined", ans:stylens, a:"data-style-name"}, {ens:textns, en:"user-field-get", ans:stylens, a:"data-style-name"}, {ens:textns, en:"user-field-input", ans:stylens, a:"data-style-name"}, {ens:textns, en:"variable-get", ans:stylens, a:"data-style-name"}, {ens:textns, en:"variable-input", ans:stylens, a:"data-style-name"}, {ens:textns, + en:"variable-set", ans:stylens, a:"data-style-name"}], "page-layout":[{ens:presentationns, en:"notes", ans:stylens, a:"page-layout-name"}, {ens:stylens, en:"handout-master", ans:stylens, a:"page-layout-name"}, {ens:stylens, en:"master-page", ans:stylens, a:"page-layout-name"}]}, elements, xpath = xmldom.XPath; + function hasDerivedStyles(odfbody, nsResolver, styleElement) { + var nodes, xp, styleName = styleElement.getAttributeNS(stylens, "name"), styleFamily = styleElement.getAttributeNS(stylens, "family"); + xp = "//style:*[@style:parent-style-name='" + styleName + "'][@style:family='" + styleFamily + "']"; + nodes = xpath.getODFElementsWithXPath(odfbody, xp, nsResolver); + if(nodes.length) { + return true } - steps.push({location:location, predicates:predicates}); - return pos + return false } - function parseXPath(xpath) { - var steps = [], p = 0, end = xpath.length, value; - while(p < end) { - p = parseXPathStep(xpath, p, end, steps); - if(p < end && xpath[p] === "=") { - value = xpath.substring(p + 1, end); - if(value.length > 2 && (value[0] === "'" || value[0] === '"')) { - value = value.slice(1, value.length - 1) - }else { - try { - value = parseInt(value, 10) - }catch(ignore) { - } - } - p = end + function prefixUsedStyleNames(element, prefix) { + var i, stylename, a, e, ns, elname, elns, localName, length = 0; + elname = elements[element.localName]; + if(elname) { + elns = elname[element.namespaceURI]; + if(elns) { + length = elns.length } } - return{steps:steps, value:value} - } - parsePredicates = function parsePredicates(xpath, start, predicates) { - var pos = start, l = xpath.length, depth = 0; - while(pos < l) { - if(xpath[pos] === "]") { - depth -= 1; - if(depth <= 0) { - predicates.push(parseXPath(xpath.substring(start, pos))) - } - }else { - if(xpath[pos] === "[") { - if(depth <= 0) { - start = pos + 1 - } - depth += 1 - } + for(i = 0;i < length;i += 1) { + a = (elns[i]); + ns = a.ns; + localName = a.localname; + stylename = element.getAttributeNS(ns, localName); + if(stylename) { + element.setAttributeNS(ns, nsprefixes[ns] + localName, prefix + stylename) } - pos += 1 } - return pos - }; - xmldom.XPathIterator.prototype.next = function() { - }; - xmldom.XPathIterator.prototype.reset = function() { - }; - function XPathNodeIterator() { - var node, done = false; - this.setNode = function setNode(n) { - node = n - }; - this.reset = function() { - done = false - }; - this.next = function next() { - var val = done ? null : node; - done = true; - return val + e = element.firstElementChild; + while(e) { + prefixUsedStyleNames(e, prefix); + e = e.nextElementSibling } } - function AttributeIterator(it, namespace, localName) { - this.reset = function reset() { - it.reset() - }; - this.next = function next() { - var node = it.next(); - while(node) { - node = node.getAttributeNodeNS(namespace, localName); - if(node) { - return node - } - node = it.next() + function prefixStyleName(styleElement, prefix) { + var stylename = styleElement.getAttributeNS(drawns, "name"), ns; + if(stylename) { + ns = drawns + }else { + stylename = styleElement.getAttributeNS(stylens, "name"); + if(stylename) { + ns = stylens } - return node + } + if(ns) { + styleElement.setAttributeNS(ns, nsprefixes[ns] + "name", prefix + stylename) } } - function AllChildElementIterator(it, recurse) { - var root = it.next(), node = null; - this.reset = function reset() { - it.reset(); - root = it.next(); - node = null - }; - this.next = function next() { - while(root) { - if(node) { - if(recurse && node.firstChild) { - node = node.firstChild - }else { - while(!node.nextSibling && node !== root) { - node = node.parentNode - } - if(node === root) { - root = it.next() - }else { - node = node.nextSibling - } - } - }else { - do { - node = root.firstChild; - if(!node) { - root = it.next() - } - }while(root && !node) - } - if(node && node.nodeType === Node.ELEMENT_NODE) { - return node + function prefixStyleNames(styleElementsRoot, prefix, styleUsingElementsRoot) { + var s; + if(styleElementsRoot) { + s = styleElementsRoot.firstChild; + while(s) { + if(s.nodeType === Node.ELEMENT_NODE) { + prefixStyleName((s), prefix) } + s = s.nextSibling + } + prefixUsedStyleNames(styleElementsRoot, prefix); + if(styleUsingElementsRoot) { + prefixUsedStyleNames(styleUsingElementsRoot, prefix) } - return null } } - function ConditionIterator(it, condition) { - this.reset = function reset() { - it.reset() - }; - this.next = function next() { - var n = it.next(); - while(n && !condition(n)) { - n = it.next() + function removeRegExpFromUsedStyleNames(element, regExp) { + var i, stylename, e, elname, elns, a, ns, localName, length = 0; + elname = elements[element.localName]; + if(elname) { + elns = elname[element.namespaceURI]; + if(elns) { + length = elns.length } - return n } - } - function createNodenameFilter(it, name, namespaceResolver) { - var s = name.split(":", 2), namespace = namespaceResolver(s[0]), localName = s[1]; - return new ConditionIterator(it, function(node) { - return node.localName === localName && node.namespaceURI === namespace - }) - } - function createPredicateFilteredIterator(it, p, namespaceResolver) { - var nit = new XPathNodeIterator, pit = createXPathPathIterator(nit, p, namespaceResolver), value = p.value; - if(value === undefined) { - return new ConditionIterator(it, function(node) { - nit.setNode(node); - pit.reset(); - return pit.next() - }) + for(i = 0;i < length;i += 1) { + a = (elns[i]); + ns = a.ns; + localName = a.localname; + stylename = element.getAttributeNS(ns, localName); + if(stylename) { + stylename = stylename.replace(regExp, ""); + element.setAttributeNS(ns, nsprefixes[ns] + localName, stylename) + } + } + e = element.firstElementChild; + while(e) { + removeRegExpFromUsedStyleNames(e, regExp); + e = e.nextElementSibling } - return new ConditionIterator(it, function(node) { - nit.setNode(node); - pit.reset(); - var n = pit.next(); - return n && n.nodeValue === value - }) } - createXPathPathIterator = function createXPathPathIterator(it, xpath, namespaceResolver) { - var i, j, step, location, p; - for(i = 0;i < xpath.steps.length;i += 1) { - step = xpath.steps[i]; - location = step.location; - if(location === "") { - it = new AllChildElementIterator(it, false) - }else { - if(location[0] === "@") { - p = location.slice(1).split(":", 2); - it = new AttributeIterator(it, namespaceResolver(p[0]), p[1]) - }else { - if(location !== ".") { - it = new AllChildElementIterator(it, false); - if(location.indexOf(":") !== -1) { - it = createNodenameFilter(it, location, namespaceResolver) - } - } - } - } - for(j = 0;j < step.predicates.length;j += 1) { - p = step.predicates[j]; - it = createPredicateFilteredIterator(it, p, namespaceResolver) + function removeRegExpFromStyleName(styleElement, regExp) { + var stylename = styleElement.getAttributeNS(drawns, "name"), ns; + if(stylename) { + ns = drawns + }else { + stylename = styleElement.getAttributeNS(stylens, "name"); + if(stylename) { + ns = stylens } } - return it - }; - function fallback(node, xpath, namespaceResolver) { - var it = new XPathNodeIterator, i, nodelist, parsedXPath; - it.setNode(node); - parsedXPath = parseXPath(xpath); - it = createXPathPathIterator(it, parsedXPath, namespaceResolver); - nodelist = []; - i = it.next(); - while(i) { - nodelist.push(i); - i = it.next() + if(ns) { + stylename = stylename.replace(regExp, ""); + styleElement.setAttributeNS(ns, nsprefixes[ns] + "name", stylename) } - return nodelist } - function getODFElementsWithXPath(node, xpath, namespaceResolver) { - var doc = node.ownerDocument, nodes, elements = [], n = null; - if(!doc || !doc.evaluate) { - elements = fallback(node, xpath, namespaceResolver) - }else { - nodes = doc.evaluate(xpath, node, namespaceResolver, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null); - n = nodes.iterateNext(); - while(n !== null) { - if(n.nodeType === Node.ELEMENT_NODE) { - elements.push(n) + function removePrefixFromStyleNames(styleElementsRoot, prefix, styleUsingElementsRoot) { + var s, regExp = new RegExp("^" + prefix); + if(styleElementsRoot) { + s = styleElementsRoot.firstChild; + while(s) { + if(s.nodeType === Node.ELEMENT_NODE) { + removeRegExpFromStyleName((s), regExp) } - n = nodes.iterateNext() + s = s.nextSibling + } + removeRegExpFromUsedStyleNames(styleElementsRoot, regExp); + if(styleUsingElementsRoot) { + removeRegExpFromUsedStyleNames(styleUsingElementsRoot, regExp) } } - return elements - } - xmldom.XPath = function XPath() { - this.getODFElementsWithXPath = getODFElementsWithXPath - }; - return xmldom.XPath -}(); -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -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(); - runtime.assert(Boolean(window), "Expected to be run in an environment which has a global window, like a browser."); - function wrapAnnotation(annotation) { - var annotationWrapper = doc.createElement("div"), annotationNote = doc.createElement("div"), connectorHorizontal = doc.createElement("div"), connectorAngular = doc.createElement("div"), removeButton = doc.createElement("div"), annotationNode = annotation.node; - annotationWrapper.className = "annotationWrapper"; - annotationNode.parentNode.insertBefore(annotationWrapper, annotationNode); - annotationNote.className = "annotationNote"; - annotationNote.appendChild(annotationNode); - removeButton.className = "annotationRemoveButton"; - annotationNote.appendChild(removeButton); - connectorHorizontal.className = "annotationConnector horizontal"; - connectorAngular.className = "annotationConnector angular"; - annotationWrapper.appendChild(annotationNote); - annotationWrapper.appendChild(connectorHorizontal); - annotationWrapper.appendChild(connectorAngular) } - function unwrapAnnotation(annotation) { - var annotationNode = annotation.node, annotationWrapper = annotationNode.parentNode.parentNode; - if(annotationWrapper.localName === "div") { - annotationWrapper.parentNode.insertBefore(annotationNode, annotationWrapper); - annotationWrapper.parentNode.removeChild(annotationWrapper) + function determineStylesForNode(element, usedStyles) { + var i, stylename, elname, elns, a, ns, localName, keyname, length = 0, map; + elname = elements[element.localName]; + if(elname) { + elns = elname[element.namespaceURI]; + if(elns) { + length = elns.length + } } - } - function highlightAnnotation(annotation) { - var annotationNode = annotation.node, annotationEnd = annotation.end, range = doc.createRange(), textNodes; - if(annotationEnd) { - range.setStart(annotationNode, annotationNode.childNodes.length); - range.setEnd(annotationEnd, 0); - textNodes = odfUtils.getTextNodes(range, false); - textNodes.forEach(function(n) { - var container = doc.createElement("span"); - container.className = "annotationHighlight"; - container.setAttribute("annotation", annotationNode.getAttributeNS(odf.Namespaces.officens, "name")); - n.parentNode.insertBefore(container, n); - container.appendChild(n) - }) + for(i = 0;i < length;i += 1) { + a = (elns[i]); + ns = a.ns; + localName = a.localname; + stylename = element.getAttributeNS(ns, localName); + if(stylename) { + usedStyles = usedStyles || {}; + keyname = a.keyname; + if(usedStyles.hasOwnProperty(keyname)) { + usedStyles[keyname][stylename] = 1 + }else { + map = {}; + map[stylename] = 1; + usedStyles[keyname] = map + } + } } - range.detach() + return usedStyles } - function unhighlightAnnotation(annotation) { - var annotationName = annotation.node.getAttributeNS(odf.Namespaces.officens, "name"), highlightSpans = doc.querySelectorAll('span.annotationHighlight[annotation="' + annotationName + '"]'), i, container; - for(i = 0;i < highlightSpans.length;i += 1) { - container = highlightSpans[i]; - while(container.firstChild) { - container.parentNode.insertBefore(container.firstChild, container) + function determineUsedStyles(styleUsingElementsRoot, usedStyles) { + var i, e; + determineStylesForNode(styleUsingElementsRoot, usedStyles); + i = styleUsingElementsRoot.firstChild; + while(i) { + if(i.nodeType === Node.ELEMENT_NODE) { + e = (i); + determineUsedStyles(e, usedStyles) } - container.parentNode.removeChild(container) + i = i.nextSibling } } - function lineDistance(point1, point2) { - var xs = 0, ys = 0; - xs = point2.x - point1.x; - xs = xs * xs; - ys = point2.y - point1.y; - ys = ys * ys; - return Math.sqrt(xs + ys) + function StyleDefinition(key, name, family) { + this.key = key; + this.name = name; + this.family = family; + this.requires = {} } - 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, zoomLevel = odfCanvas.getZoomLevel(); - annotationNote.style.left = (annotationsPane.getBoundingClientRect().left - annotationWrapper.getBoundingClientRect().left) / zoomLevel + "px"; - annotationNote.style.width = annotationsPane.getBoundingClientRect().width / zoomLevel + "px"; - connectorHorizontal.style.width = parseFloat(annotationNote.style.left) - CONNECTOR_MARGIN + "px"; - if(previousAnnotation) { - previousRect = previousAnnotation.node.parentNode.getBoundingClientRect(); - if((annotationWrapper.getBoundingClientRect().top - previousRect.bottom) / zoomLevel <= NOTE_MARGIN) { - annotationNote.style.top = Math.abs(annotationWrapper.getBoundingClientRect().top - previousRect.bottom) / zoomLevel + NOTE_MARGIN + "px" - }else { - annotationNote.style.top = "0px" + function getStyleDefinition(stylename, stylefamily, knownStyles) { + var styleKey = stylename + '"' + stylefamily, styleDefinition = knownStyles[styleKey]; + if(!styleDefinition) { + styleDefinition = knownStyles[styleKey] = new StyleDefinition(styleKey, stylename, stylefamily) + } + return styleDefinition + } + function determineDependentStyles(element, styleScope, knownStyles) { + var i, stylename, elname, elns, a, ns, localName, e, referencedStyleFamily, referencedStyleDef, length = 0, newScopeName = element.getAttributeNS(stylens, "name"), newScopeFamily = element.getAttributeNS(stylens, "family"); + if(newScopeName && newScopeFamily) { + styleScope = getStyleDefinition(newScopeName, newScopeFamily, knownStyles) + } + if(styleScope) { + elname = elements[element.localName]; + if(elname) { + elns = elname[element.namespaceURI]; + if(elns) { + length = elns.length + } + } + for(i = 0;i < length;i += 1) { + a = (elns[i]); + ns = a.ns; + localName = a.localname; + stylename = element.getAttributeNS(ns, localName); + if(stylename) { + referencedStyleFamily = a.keyname; + referencedStyleDef = getStyleDefinition(stylename, referencedStyleFamily, knownStyles); + styleScope.requires[referencedStyleDef.key] = referencedStyleDef + } } } - connectorAngular.style.left = connectorHorizontal.getBoundingClientRect().width / zoomLevel + "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) / (zoomLevel * parseFloat(connectorAngular.style.width))); - connectorAngular.style.transform = "rotate(" + connectorAngle + "rad)"; - connectorAngular.style.MozTransform = "rotate(" + connectorAngle + "rad)"; - connectorAngular.style.WebkitTransform = "rotate(" + connectorAngle + "rad)"; - connectorAngular.style.msTransform = "rotate(" + connectorAngle + "rad)" + e = element.firstElementChild; + while(e) { + determineDependentStyles(e, styleScope, knownStyles); + e = e.nextElementSibling + } + return knownStyles } - function showAnnotationsPane(show) { - var sizer = odfCanvas.getSizer(); - if(show) { - annotationsPane.style.display = "inline-block"; - sizer.style.paddingRight = window.getComputedStyle(annotationsPane).width - }else { - annotationsPane.style.display = "none"; - sizer.style.paddingRight = 0 + function inverse() { + var i, l, keyname, list, item, e = {}, map, array, en, ens; + for(keyname in elementstyles) { + if(elementstyles.hasOwnProperty(keyname)) { + list = elementstyles[keyname]; + l = list.length; + for(i = 0;i < l;i += 1) { + item = list[i]; + en = item.en; + ens = item.ens; + if(e.hasOwnProperty(en)) { + map = e[en] + }else { + e[en] = map = {} + } + if(map.hasOwnProperty(ens)) { + array = map[ens] + }else { + map[ens] = array = [] + } + array.push({ns:item.ans, localname:item.a, keyname:keyname}) + } + } } - odfCanvas.refreshSize() + return e } - function sortAnnotations() { - annotations.sort(function(a, b) { - if(a.node.compareDocumentPosition(b.node) === Node.DOCUMENT_POSITION_FOLLOWING) { - return-1 + function mergeRequiredStyles(styleDependency, usedStyles) { + var family = usedStyles[styleDependency.family]; + if(!family) { + family = usedStyles[styleDependency.family] = {} + } + family[styleDependency.name] = 1; + Object.keys((styleDependency.requires)).forEach(function(requiredStyleKey) { + mergeRequiredStyles((styleDependency.requires[requiredStyleKey]), usedStyles) + }) + } + function mergeUsedAutomaticStyles(automaticStylesRoot, usedStyles) { + var automaticStyles = determineDependentStyles(automaticStylesRoot, null, {}); + Object.keys(automaticStyles).forEach(function(styleKey) { + var automaticStyleDefinition = automaticStyles[styleKey], usedFamily = usedStyles[automaticStyleDefinition.family]; + if(usedFamily && usedFamily.hasOwnProperty(automaticStyleDefinition.name)) { + mergeRequiredStyles(automaticStyleDefinition, usedStyles) } - return 1 }) } - function rerenderAnnotations() { - var i; - for(i = 0;i < annotations.length;i += 1) { - renderAnnotation(annotations[i]) + function collectUsedFontFaces(usedFontFaceDeclMap, styleElement) { + var localNames = ["font-name", "font-name-asian", "font-name-complex"], e, currentElement; + function collectByAttribute(localName) { + var fontFaceName = currentElement.getAttributeNS(stylens, localName); + if(fontFaceName) { + usedFontFaceDeclMap[fontFaceName] = true + } } - } - this.rerenderAnnotations = rerenderAnnotations; - function addAnnotation(annotation) { - showAnnotationsPane(true); - annotations.push({node:annotation.node, end:annotation.end}); - sortAnnotations(); - wrapAnnotation(annotation); - if(annotation.end) { - highlightAnnotation(annotation) + e = styleElement && styleElement.firstElementChild; + while(e) { + currentElement = e; + localNames.forEach(collectByAttribute); + collectUsedFontFaces(usedFontFaceDeclMap, currentElement); + e = e.nextElementSibling } - rerenderAnnotations() } - this.addAnnotation = addAnnotation; - function forgetAnnotation(annotation) { - var index = annotations.indexOf(annotation); - unwrapAnnotation(annotation); - unhighlightAnnotation(annotation); - if(index !== -1) { - annotations.splice(index, 1) + this.collectUsedFontFaces = collectUsedFontFaces; + function changeFontFaceNames(styleElement, fontFaceNameChangeMap) { + var localNames = ["font-name", "font-name-asian", "font-name-complex"], e, currentElement; + function changeFontFaceNameByAttribute(localName) { + var fontFaceName = currentElement.getAttributeNS(stylens, localName); + if(fontFaceName && fontFaceNameChangeMap.hasOwnProperty(fontFaceName)) { + currentElement.setAttributeNS(stylens, "style:" + localName, fontFaceNameChangeMap[fontFaceName]) + } } - if(annotations.length === 0) { - showAnnotationsPane(false) + e = styleElement && styleElement.firstElementChild; + while(e) { + currentElement = e; + localNames.forEach(changeFontFaceNameByAttribute); + changeFontFaceNames(currentElement, fontFaceNameChangeMap); + e = e.nextElementSibling } } - function forgetAnnotations() { - while(annotations.length) { - forgetAnnotation(annotations[0]) + this.changeFontFaceNames = changeFontFaceNames; + this.UsedStyleList = function(styleUsingElementsRoot, automaticStylesRoot) { + var usedStyles = {}; + this.uses = function(element) { + var localName = element.localName, name = element.getAttributeNS(drawns, "name") || element.getAttributeNS(stylens, "name"), keyName, map; + if(localName === "style") { + keyName = element.getAttributeNS(stylens, "family") + }else { + if(element.namespaceURI === numberns) { + keyName = "data" + }else { + keyName = localName + } + } + map = usedStyles[keyName]; + return map ? map[name] > 0 : false + }; + determineUsedStyles(styleUsingElementsRoot, usedStyles); + if(automaticStylesRoot) { + mergeUsedAutomaticStyles(automaticStylesRoot, usedStyles) } - } - this.forgetAnnotations = forgetAnnotations + }; + this.hasDerivedStyles = hasDerivedStyles; + this.prefixStyleNames = prefixStyleNames; + this.removePrefixFromStyleNames = removePrefixFromStyleNames; + this.determineStylesForNode = determineStylesForNode; + elements = inverse() }; /* @@ -6268,19 +6334,40 @@ gui.AnnotationViewManager = function AnnotationViewManager(odfCanvas, odfFragmen @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -odf.OdfNodeFilter = function OdfNodeFilter() { - this.acceptNode = function(node) { - var result; - if(node.namespaceURI === "http://www.w3.org/1999/xhtml") { - result = NodeFilter.FILTER_SKIP - }else { - if(node.namespaceURI && node.namespaceURI.match(/^urn:webodf:/)) { - result = NodeFilter.FILTER_REJECT +runtime.loadClass("odf.OdfUtils"); +odf.TextSerializer = function TextSerializer() { + var self = this, odfUtils = new odf.OdfUtils; + function serializeNode(node) { + var s = "", accept = self.filter ? self.filter.acceptNode(node) : NodeFilter.FILTER_ACCEPT, nodeType = node.nodeType, child; + if(accept === NodeFilter.FILTER_ACCEPT || accept === NodeFilter.FILTER_SKIP) { + child = node.firstChild; + while(child) { + s += serializeNode(child); + child = child.nextSibling + } + } + if(accept === NodeFilter.FILTER_ACCEPT) { + if(nodeType === Node.ELEMENT_NODE && odfUtils.isParagraph(node)) { + s += "\n" }else { - result = NodeFilter.FILTER_ACCEPT + if(nodeType === Node.TEXT_NODE && node.textContent) { + s += node.textContent + } } } - return result + return s + } + this.filter = null; + this.writeToString = function(node) { + var plainText; + if(!node) { + return"" + } + plainText = serializeNode(node); + if(plainText[plainText.length - 1] === "\n") { + plainText = plainText.substr(0, plainText.length - 1) + } + return plainText } }; /* @@ -6320,394 +6407,294 @@ odf.OdfNodeFilter = function OdfNodeFilter() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -odf.Namespaces = function() { - var dbns = "urn:oasis:names:tc:opendocument:xmlns:database:1.0", dcns = "http://purl.org/dc/elements/1.1/", metans = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0", dr3dns = "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0", drawns = "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0", chartns = "urn:oasis:names:tc:opendocument:xmlns:chart:1.0", fons = "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0", formns = "urn:oasis:names:tc:opendocument:xmlns:form:1.0", numberns = "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0", - officens = "urn:oasis:names:tc:opendocument:xmlns:office:1.0", presentationns = "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0", stylens = "urn:oasis:names:tc:opendocument:xmlns:style:1.0", svgns = "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0", tablens = "urn:oasis:names:tc:opendocument:xmlns:table:1.0", textns = "urn:oasis:names:tc:opendocument:xmlns:text:1.0", xlinkns = "http://www.w3.org/1999/xlink", xmlns = "http://www.w3.org/XML/1998/namespace", namespaceMap = {"db":dbns, - "dc":dcns, "meta":metans, "dr3d":dr3dns, "draw":drawns, "chart":chartns, "fo":fons, "form":formns, "numberns":numberns, "office":officens, "presentation":presentationns, "style":stylens, "svg":svgns, "table":tablens, "text":textns, "xlink":xlinkns, "xml":xmlns}, namespaces; - function forEachPrefix(cb) { - var prefix; - for(prefix in namespaceMap) { - if(namespaceMap.hasOwnProperty(prefix)) { - cb(prefix, namespaceMap[prefix]) - } - } - } - function resolvePrefix(prefix) { - return namespaceMap[prefix] || null - } - function lookupPrefix(namespaceURI) { - var foundPrefix, prefix; - for(prefix in namespaceMap) { - if(namespaceMap.hasOwnProperty(prefix) && namespaceMap[prefix] === namespaceURI) { - foundPrefix = prefix; - break +runtime.loadClass("core.PositionFilter"); +runtime.loadClass("odf.OdfUtils"); +ops.TextPositionFilter = function TextPositionFilter(getRootNode) { + var odfUtils = new odf.OdfUtils, ELEMENT_NODE = Node.ELEMENT_NODE, TEXT_NODE = Node.TEXT_NODE, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, FILTER_REJECT = core.PositionFilter.FilterResult.FILTER_REJECT; + function checkLeftRight(container, leftNode, rightNode) { + var r, firstPos, rightOfChar; + if(leftNode) { + r = odfUtils.lookLeftForCharacter(leftNode); + if(r === 1) { + return FILTER_ACCEPT + } + if(r === 2 && (odfUtils.scanRightForAnyCharacter(rightNode) || odfUtils.scanRightForAnyCharacter(odfUtils.nextNode(container)))) { + return FILTER_ACCEPT } } - return foundPrefix - } - resolvePrefix.lookupNamespaceURI = resolvePrefix; - namespaces = function Namespaces() { - }; - namespaces.forEachPrefix = forEachPrefix; - namespaces.resolvePrefix = resolvePrefix; - namespaces.lookupPrefix = lookupPrefix; - namespaces.namespaceMap = namespaceMap; - namespaces.dbns = dbns; - namespaces.dcns = dcns; - namespaces.metans = metans; - namespaces.dr3dns = dr3dns; - namespaces.drawns = drawns; - namespaces.chartns = chartns; - namespaces.fons = fons; - namespaces.formns = formns; - namespaces.numberns = numberns; - namespaces.officens = officens; - namespaces.presentationns = presentationns; - namespaces.stylens = stylens; - namespaces.svgns = svgns; - namespaces.tablens = tablens; - namespaces.textns = textns; - namespaces.xlinkns = xlinkns; - namespaces.xmlns = xmlns; - return namespaces -}(); -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("xmldom.XPath"); -runtime.loadClass("odf.Namespaces"); -odf.StyleInfo = function StyleInfo() { - var chartns = odf.Namespaces.chartns, dbns = odf.Namespaces.dbns, dr3dns = odf.Namespaces.dr3dns, drawns = odf.Namespaces.drawns, formns = odf.Namespaces.formns, numberns = odf.Namespaces.numberns, officens = odf.Namespaces.officens, presentationns = odf.Namespaces.presentationns, stylens = odf.Namespaces.stylens, tablens = odf.Namespaces.tablens, textns = odf.Namespaces.textns, nsprefixes = {"urn:oasis:names:tc:opendocument:xmlns:chart:1.0":"chart:", "urn:oasis:names:tc:opendocument:xmlns:database:1.0":"db:", - "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0":"dr3d:", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0":"draw:", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0":"fo:", "urn:oasis:names:tc:opendocument:xmlns:form:1.0":"form:", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0":"number:", "urn:oasis:names:tc:opendocument:xmlns:office:1.0":"office:", "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0":"presentation:", "urn:oasis:names:tc:opendocument:xmlns:style:1.0":"style:", - "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0":"svg:", "urn:oasis:names:tc:opendocument:xmlns:table:1.0":"table:", "urn:oasis:names:tc:opendocument:xmlns:text:1.0":"chart:", "http://www.w3.org/XML/1998/namespace":"xml:"}, elementstyles = {"text":[{ens:stylens, en:"tab-stop", ans:stylens, a:"leader-text-style"}, {ens:stylens, en:"drop-cap", ans:stylens, a:"style-name"}, {ens:textns, en:"notes-configuration", ans:textns, a:"citation-body-style-name"}, {ens:textns, en:"notes-configuration", - ans:textns, a:"citation-style-name"}, {ens:textns, en:"a", ans:textns, a:"style-name"}, {ens:textns, en:"alphabetical-index", ans:textns, a:"style-name"}, {ens:textns, en:"linenumbering-configuration", ans:textns, a:"style-name"}, {ens:textns, en:"list-level-style-number", ans:textns, a:"style-name"}, {ens:textns, en:"ruby-text", ans:textns, a:"style-name"}, {ens:textns, en:"span", ans:textns, a:"style-name"}, {ens:textns, en:"a", ans:textns, a:"visited-style-name"}, {ens:stylens, en:"text-properties", - ans:stylens, a:"text-line-through-text-style"}, {ens:textns, en:"alphabetical-index-source", ans:textns, a:"main-entry-style-name"}, {ens:textns, en:"index-entry-bibliography", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-chapter", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-link-end", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-link-start", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-page-number", ans:textns, a:"style-name"}, {ens:textns, - en:"index-entry-span", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-tab-stop", ans:textns, a:"style-name"}, {ens:textns, en:"index-entry-text", ans:textns, a:"style-name"}, {ens:textns, en:"index-title-template", ans:textns, a:"style-name"}, {ens:textns, en:"list-level-style-bullet", ans:textns, a:"style-name"}, {ens:textns, en:"outline-level-style", ans:textns, a:"style-name"}], "paragraph":[{ens:drawns, en:"caption", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"circle", ans:drawns, - a:"text-style-name"}, {ens:drawns, en:"connector", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"control", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"custom-shape", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"ellipse", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"frame", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"line", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"measure", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"path", ans:drawns, a:"text-style-name"}, - {ens:drawns, en:"polygon", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"polyline", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"rect", ans:drawns, a:"text-style-name"}, {ens:drawns, en:"regular-polygon", ans:drawns, a:"text-style-name"}, {ens:officens, en:"annotation", ans:drawns, a:"text-style-name"}, {ens:formns, en:"column", ans:formns, a:"text-style-name"}, {ens:stylens, en:"style", ans:stylens, a:"next-style-name"}, {ens:tablens, en:"body", ans:tablens, a:"paragraph-style-name"}, - {ens:tablens, en:"even-columns", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"even-rows", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"first-column", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"first-row", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"last-column", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"last-row", ans:tablens, a:"paragraph-style-name"}, {ens:tablens, en:"odd-columns", ans:tablens, a:"paragraph-style-name"}, - {ens:tablens, en:"odd-rows", ans:tablens, a:"paragraph-style-name"}, {ens:textns, en:"notes-configuration", ans:textns, a:"default-style-name"}, {ens:textns, en:"alphabetical-index-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"bibliography-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"h", ans:textns, a:"style-name"}, {ens:textns, en:"illustration-index-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"index-source-style", ans:textns, a:"style-name"}, - {ens:textns, en:"object-index-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"p", ans:textns, a:"style-name"}, {ens:textns, en:"table-index-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"table-of-content-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"table-index-entry-template", ans:textns, a:"style-name"}, {ens:textns, en:"user-index-entry-template", ans:textns, a:"style-name"}, {ens:stylens, en:"page-layout-properties", ans:stylens, a:"register-truth-ref-style-name"}], - "chart":[{ens:chartns, en:"axis", ans:chartns, a:"style-name"}, {ens:chartns, en:"chart", ans:chartns, a:"style-name"}, {ens:chartns, en:"data-label", ans:chartns, a:"style-name"}, {ens:chartns, en:"data-point", ans:chartns, a:"style-name"}, {ens:chartns, en:"equation", ans:chartns, a:"style-name"}, {ens:chartns, en:"error-indicator", ans:chartns, a:"style-name"}, {ens:chartns, en:"floor", ans:chartns, a:"style-name"}, {ens:chartns, en:"footer", ans:chartns, a:"style-name"}, {ens:chartns, en:"grid", - ans:chartns, a:"style-name"}, {ens:chartns, en:"legend", ans:chartns, a:"style-name"}, {ens:chartns, en:"mean-value", ans:chartns, a:"style-name"}, {ens:chartns, en:"plot-area", ans:chartns, a:"style-name"}, {ens:chartns, en:"regression-curve", ans:chartns, a:"style-name"}, {ens:chartns, en:"series", ans:chartns, a:"style-name"}, {ens:chartns, en:"stock-gain-marker", ans:chartns, a:"style-name"}, {ens:chartns, en:"stock-loss-marker", ans:chartns, a:"style-name"}, {ens:chartns, en:"stock-range-line", - ans:chartns, a:"style-name"}, {ens:chartns, en:"subtitle", ans:chartns, a:"style-name"}, {ens:chartns, en:"title", ans:chartns, a:"style-name"}, {ens:chartns, en:"wall", ans:chartns, a:"style-name"}], "section":[{ens:textns, en:"alphabetical-index", ans:textns, a:"style-name"}, {ens:textns, en:"bibliography", ans:textns, a:"style-name"}, {ens:textns, en:"illustration-index", ans:textns, a:"style-name"}, {ens:textns, en:"index-title", ans:textns, a:"style-name"}, {ens:textns, en:"object-index", - ans:textns, a:"style-name"}, {ens:textns, en:"section", ans:textns, a:"style-name"}, {ens:textns, en:"table-of-content", ans:textns, a:"style-name"}, {ens:textns, en:"table-index", ans:textns, a:"style-name"}, {ens:textns, en:"user-index", ans:textns, a:"style-name"}], "ruby":[{ens:textns, en:"ruby", ans:textns, a:"style-name"}], "table":[{ens:dbns, en:"query", ans:dbns, a:"style-name"}, {ens:dbns, en:"table-representation", ans:dbns, a:"style-name"}, {ens:tablens, en:"background", ans:tablens, - a:"style-name"}, {ens:tablens, en:"table", ans:tablens, a:"style-name"}], "table-column":[{ens:dbns, en:"column", ans:dbns, a:"style-name"}, {ens:tablens, en:"table-column", ans:tablens, a:"style-name"}], "table-row":[{ens:dbns, en:"query", ans:dbns, a:"default-row-style-name"}, {ens:dbns, en:"table-representation", ans:dbns, a:"default-row-style-name"}, {ens:tablens, en:"table-row", ans:tablens, a:"style-name"}], "table-cell":[{ens:dbns, en:"column", ans:dbns, a:"default-cell-style-name"}, {ens:tablens, - en:"table-column", ans:tablens, a:"default-cell-style-name"}, {ens:tablens, en:"table-row", ans:tablens, a:"default-cell-style-name"}, {ens:tablens, en:"body", ans:tablens, a:"style-name"}, {ens:tablens, en:"covered-table-cell", ans:tablens, a:"style-name"}, {ens:tablens, en:"even-columns", ans:tablens, a:"style-name"}, {ens:tablens, en:"covered-table-cell", ans:tablens, a:"style-name"}, {ens:tablens, en:"even-columns", ans:tablens, a:"style-name"}, {ens:tablens, en:"even-rows", ans:tablens, a:"style-name"}, - {ens:tablens, en:"first-column", ans:tablens, a:"style-name"}, {ens:tablens, en:"first-row", ans:tablens, a:"style-name"}, {ens:tablens, en:"last-column", ans:tablens, a:"style-name"}, {ens:tablens, en:"last-row", ans:tablens, a:"style-name"}, {ens:tablens, en:"odd-columns", ans:tablens, a:"style-name"}, {ens:tablens, en:"odd-rows", ans:tablens, a:"style-name"}, {ens:tablens, en:"table-cell", ans:tablens, a:"style-name"}], "graphic":[{ens:dr3dns, en:"cube", ans:drawns, a:"style-name"}, {ens:dr3dns, - en:"extrude", ans:drawns, a:"style-name"}, {ens:dr3dns, en:"rotate", ans:drawns, a:"style-name"}, {ens:dr3dns, en:"scene", ans:drawns, a:"style-name"}, {ens:dr3dns, en:"sphere", ans:drawns, a:"style-name"}, {ens:drawns, en:"caption", ans:drawns, a:"style-name"}, {ens:drawns, en:"circle", ans:drawns, a:"style-name"}, {ens:drawns, en:"connector", ans:drawns, a:"style-name"}, {ens:drawns, en:"control", ans:drawns, a:"style-name"}, {ens:drawns, en:"custom-shape", ans:drawns, a:"style-name"}, {ens:drawns, - en:"ellipse", ans:drawns, a:"style-name"}, {ens:drawns, en:"frame", ans:drawns, a:"style-name"}, {ens:drawns, en:"g", ans:drawns, a:"style-name"}, {ens:drawns, en:"line", ans:drawns, a:"style-name"}, {ens:drawns, en:"measure", ans:drawns, a:"style-name"}, {ens:drawns, en:"page-thumbnail", ans:drawns, a:"style-name"}, {ens:drawns, en:"path", ans:drawns, a:"style-name"}, {ens:drawns, en:"polygon", ans:drawns, a:"style-name"}, {ens:drawns, en:"polyline", ans:drawns, a:"style-name"}, {ens:drawns, en:"rect", - ans:drawns, a:"style-name"}, {ens:drawns, en:"regular-polygon", ans:drawns, a:"style-name"}, {ens:officens, en:"annotation", ans:drawns, a:"style-name"}], "presentation":[{ens:dr3dns, en:"cube", ans:presentationns, a:"style-name"}, {ens:dr3dns, en:"extrude", ans:presentationns, a:"style-name"}, {ens:dr3dns, en:"rotate", ans:presentationns, a:"style-name"}, {ens:dr3dns, en:"scene", ans:presentationns, a:"style-name"}, {ens:dr3dns, en:"sphere", ans:presentationns, a:"style-name"}, {ens:drawns, en:"caption", - ans:presentationns, a:"style-name"}, {ens:drawns, en:"circle", ans:presentationns, a:"style-name"}, {ens:drawns, en:"connector", ans:presentationns, a:"style-name"}, {ens:drawns, en:"control", ans:presentationns, a:"style-name"}, {ens:drawns, en:"custom-shape", ans:presentationns, a:"style-name"}, {ens:drawns, en:"ellipse", ans:presentationns, a:"style-name"}, {ens:drawns, en:"frame", ans:presentationns, a:"style-name"}, {ens:drawns, en:"g", ans:presentationns, a:"style-name"}, {ens:drawns, en:"line", - ans:presentationns, a:"style-name"}, {ens:drawns, en:"measure", ans:presentationns, a:"style-name"}, {ens:drawns, en:"page-thumbnail", ans:presentationns, a:"style-name"}, {ens:drawns, en:"path", ans:presentationns, a:"style-name"}, {ens:drawns, en:"polygon", ans:presentationns, a:"style-name"}, {ens:drawns, en:"polyline", ans:presentationns, a:"style-name"}, {ens:drawns, en:"rect", ans:presentationns, a:"style-name"}, {ens:drawns, en:"regular-polygon", ans:presentationns, a:"style-name"}, {ens:officens, - en:"annotation", ans:presentationns, a:"style-name"}], "drawing-page":[{ens:drawns, en:"page", ans:drawns, a:"style-name"}, {ens:presentationns, en:"notes", ans:drawns, a:"style-name"}, {ens:stylens, en:"handout-master", ans:drawns, a:"style-name"}, {ens:stylens, en:"master-page", ans:drawns, a:"style-name"}], "list-style":[{ens:textns, en:"list", ans:textns, a:"style-name"}, {ens:textns, en:"numbered-paragraph", ans:textns, a:"style-name"}, {ens:textns, en:"list-item", ans:textns, a:"style-override"}, - {ens:stylens, en:"style", ans:stylens, a:"list-style-name"}], "data":[{ens:stylens, en:"style", ans:stylens, a:"data-style-name"}, {ens:stylens, en:"style", ans:stylens, a:"percentage-data-style-name"}, {ens:presentationns, en:"date-time-decl", ans:stylens, a:"data-style-name"}, {ens:textns, en:"creation-date", ans:stylens, a:"data-style-name"}, {ens:textns, en:"creation-time", ans:stylens, a:"data-style-name"}, {ens:textns, en:"database-display", ans:stylens, a:"data-style-name"}, {ens:textns, - en:"date", ans:stylens, a:"data-style-name"}, {ens:textns, en:"editing-duration", ans:stylens, a:"data-style-name"}, {ens:textns, en:"expression", ans:stylens, a:"data-style-name"}, {ens:textns, en:"meta-field", ans:stylens, a:"data-style-name"}, {ens:textns, en:"modification-date", ans:stylens, a:"data-style-name"}, {ens:textns, en:"modification-time", ans:stylens, a:"data-style-name"}, {ens:textns, en:"print-date", ans:stylens, a:"data-style-name"}, {ens:textns, en:"print-time", ans:stylens, - a:"data-style-name"}, {ens:textns, en:"table-formula", ans:stylens, a:"data-style-name"}, {ens:textns, en:"time", ans:stylens, a:"data-style-name"}, {ens:textns, en:"user-defined", ans:stylens, a:"data-style-name"}, {ens:textns, en:"user-field-get", ans:stylens, a:"data-style-name"}, {ens:textns, en:"user-field-input", ans:stylens, a:"data-style-name"}, {ens:textns, en:"variable-get", ans:stylens, a:"data-style-name"}, {ens:textns, en:"variable-input", ans:stylens, a:"data-style-name"}, {ens:textns, - en:"variable-set", ans:stylens, a:"data-style-name"}], "page-layout":[{ens:presentationns, en:"notes", ans:stylens, a:"page-layout-name"}, {ens:stylens, en:"handout-master", ans:stylens, a:"page-layout-name"}, {ens:stylens, en:"master-page", ans:stylens, a:"page-layout-name"}]}, elements, xpath = new xmldom.XPath; - function hasDerivedStyles(odfbody, nsResolver, styleElement) { - var nodes, xp, resolver = nsResolver("style"), styleName = styleElement.getAttributeNS(resolver, "name"), styleFamily = styleElement.getAttributeNS(resolver, "family"); - xp = "//style:*[@style:parent-style-name='" + styleName + "'][@style:family='" + styleFamily + "']"; - nodes = xpath.getODFElementsWithXPath(odfbody, xp, nsResolver); - if(nodes.length) { - return true - } - return false - } - function prefixUsedStyleNames(styleUsingElementsRoot, prefix) { - var elname = elements[styleUsingElementsRoot.localName], elns = elname && elname[styleUsingElementsRoot.namespaceURI], length = elns ? elns.length : 0, i, stylename, e; - for(i = 0;i < length;i += 1) { - stylename = styleUsingElementsRoot.getAttributeNS(elns[i].ns, elns[i].localname); - if(stylename) { - styleUsingElementsRoot.setAttributeNS(elns[i].ns, nsprefixes[elns[i].ns] + elns[i].localname, prefix + stylename) + firstPos = leftNode === null && odfUtils.isParagraph(container); + rightOfChar = odfUtils.lookRightForCharacter(rightNode); + if(firstPos) { + if(rightOfChar) { + return FILTER_ACCEPT } + return odfUtils.scanRightForAnyCharacter(rightNode) ? FILTER_REJECT : FILTER_ACCEPT } - i = styleUsingElementsRoot.firstChild; - while(i) { - if(i.nodeType === Node.ELEMENT_NODE) { - e = (i); - prefixUsedStyleNames(e, prefix) - } - i = i.nextSibling + if(!rightOfChar) { + return FILTER_REJECT } + leftNode = leftNode || odfUtils.previousNode(container); + return odfUtils.scanLeftForAnyCharacter(leftNode) ? FILTER_REJECT : FILTER_ACCEPT } - function prefixStyleName(styleElement, prefix) { - var stylename = styleElement.getAttributeNS(drawns, "name"), ns; - if(stylename) { - ns = drawns + this.acceptPosition = function(iterator) { + var container = iterator.container(), nodeType = container.nodeType, offset, text, leftChar, rightChar, leftNode, rightNode, r; + if(nodeType !== ELEMENT_NODE && nodeType !== TEXT_NODE) { + return FILTER_REJECT + } + if(nodeType === TEXT_NODE) { + if(!odfUtils.isGroupingElement(container.parentNode) || odfUtils.isWithinTrackedChanges(container.parentNode, getRootNode())) { + return FILTER_REJECT + } + offset = iterator.unfilteredDomOffset(); + text = container.data; + runtime.assert(offset !== text.length, "Unexpected offset."); + if(offset > 0) { + leftChar = (text[offset - 1]); + if(!odfUtils.isODFWhitespace(leftChar)) { + return FILTER_ACCEPT + } + if(offset > 1) { + leftChar = (text[offset - 2]); + if(!odfUtils.isODFWhitespace(leftChar)) { + r = FILTER_ACCEPT + }else { + if(!odfUtils.isODFWhitespace(text.substr(0, offset))) { + return FILTER_REJECT + } + } + }else { + leftNode = odfUtils.previousNode(container); + if(odfUtils.scanLeftForNonSpace(leftNode)) { + r = FILTER_ACCEPT + } + } + if(r === FILTER_ACCEPT) { + return odfUtils.isTrailingWhitespace((container), offset) ? FILTER_REJECT : FILTER_ACCEPT + } + rightChar = (text[offset]); + if(odfUtils.isODFWhitespace(rightChar)) { + return FILTER_REJECT + } + return odfUtils.scanLeftForAnyCharacter(odfUtils.previousNode(container)) ? FILTER_REJECT : FILTER_ACCEPT + } + leftNode = iterator.leftNode(); + rightNode = container; + container = (container.parentNode); + r = checkLeftRight(container, leftNode, rightNode) }else { - stylename = styleElement.getAttributeNS(stylens, "name"); - if(stylename) { - ns = stylens + if(!odfUtils.isGroupingElement(container) || odfUtils.isWithinTrackedChanges(container, getRootNode())) { + r = FILTER_REJECT + }else { + leftNode = iterator.leftNode(); + rightNode = iterator.rightNode(); + r = checkLeftRight(container, leftNode, rightNode) } } - if(ns) { - styleElement.setAttributeNS(ns, nsprefixes[ns] + "name", prefix + stylename) - } + return r } - function prefixStyleNames(styleElementsRoot, prefix, styleUsingElementsRoot) { - var s; - if(styleElementsRoot) { - s = styleElementsRoot.firstChild; - while(s) { - if(s.nodeType === Node.ELEMENT_NODE) { - prefixStyleName((s), prefix) +}; +if(typeof Object.create !== "function") { + Object["create"] = function(o) { + var F = function() { + }; + F.prototype = o; + return new F + } +} +xmldom.LSSerializer = function LSSerializer() { + var self = this; + function Namespaces(nsmap) { + function invertMap(map) { + var m = {}, i; + for(i in map) { + if(map.hasOwnProperty(i)) { + m[map[i]] = i } - s = s.nextSibling } - prefixUsedStyleNames(styleElementsRoot, prefix); - if(styleUsingElementsRoot) { - prefixUsedStyleNames(styleUsingElementsRoot, prefix) + return m + } + var current = nsmap || {}, currentrev = invertMap(nsmap), levels = [current], levelsrev = [currentrev], level = 0; + this.push = function() { + level += 1; + current = levels[level] = Object.create(current); + currentrev = levelsrev[level] = Object.create(currentrev) + }; + this.pop = function() { + levels.pop(); + levelsrev.pop(); + level -= 1; + current = levels[level]; + currentrev = levelsrev[level] + }; + this.getLocalNamespaceDefinitions = function() { + return currentrev + }; + this.getQName = function(node) { + var ns = node.namespaceURI, i = 0, p; + if(!ns) { + return node.localName + } + p = currentrev[ns]; + if(p) { + return p + ":" + node.localName } + do { + if(p || !node.prefix) { + p = "ns" + i; + i += 1 + }else { + p = node.prefix + } + if(current[p] === ns) { + break + } + if(!current[p]) { + current[p] = ns; + currentrev[ns] = p; + break + } + p = null + }while(p === null); + return p + ":" + node.localName } } - function removeRegExpFromUsedStyleNames(styleUsingElementsRoot, regExp) { - var elname = elements[styleUsingElementsRoot.localName], elns = elname && elname[styleUsingElementsRoot.namespaceURI], length = elns ? elns.length : 0, i, stylename, e; + function escapeContent(value) { + return value.replace(/&/g, "&").replace(//g, ">").replace(/'/g, "'").replace(/"/g, """) + } + function serializeAttribute(qname, attr) { + var escapedValue = typeof attr.value === "string" ? escapeContent(attr.value) : attr.value, s = qname + '="' + escapedValue + '"'; + return s + } + function startElement(ns, qname, element) { + var s = "", atts = (element.attributes), length, i, attr, attstr = "", accept, prefix, nsmap; + s += "<" + qname; + length = atts.length; for(i = 0;i < length;i += 1) { - stylename = styleUsingElementsRoot.getAttributeNS(elns[i].ns, elns[i].localname); - if(stylename) { - stylename = stylename.replace(regExp, ""); - styleUsingElementsRoot.setAttributeNS(elns[i].ns, nsprefixes[elns[i].ns] + elns[i].localname, stylename) + attr = (atts.item(i)); + if(attr.namespaceURI !== "http://www.w3.org/2000/xmlns/") { + accept = self.filter ? self.filter.acceptNode(attr) : NodeFilter.FILTER_ACCEPT; + if(accept === NodeFilter.FILTER_ACCEPT) { + attstr += " " + serializeAttribute(ns.getQName(attr), attr) + } } } - i = styleUsingElementsRoot.firstChild; - while(i) { - if(i.nodeType === Node.ELEMENT_NODE) { - e = (i); - removeRegExpFromUsedStyleNames(e, regExp) + nsmap = ns.getLocalNamespaceDefinitions(); + for(i in nsmap) { + if(nsmap.hasOwnProperty(i)) { + prefix = nsmap[i]; + if(!prefix) { + s += ' xmlns="' + i + '"' + }else { + if(prefix !== "xmlns") { + s += " xmlns:" + nsmap[i] + '="' + i + '"' + } + } } - i = i.nextSibling } + s += attstr + ">"; + return s } - function removeRegExpFromStyleName(styleElement, regExp) { - var stylename = styleElement.getAttributeNS(drawns, "name"), ns; - if(stylename) { - ns = drawns - }else { - stylename = styleElement.getAttributeNS(stylens, "name"); - if(stylename) { - ns = stylens - } - } - if(ns) { - stylename = stylename.replace(regExp, ""); - styleElement.setAttributeNS(ns, nsprefixes[ns] + "name", stylename) + function serializeNode(ns, node) { + var s = "", accept = self.filter ? self.filter.acceptNode(node) : NodeFilter.FILTER_ACCEPT, child, qname; + if(accept === NodeFilter.FILTER_ACCEPT && node.nodeType === Node.ELEMENT_NODE) { + ns.push(); + qname = ns.getQName(node); + s += startElement(ns, qname, node) } - } - function removePrefixFromStyleNames(styleElementsRoot, prefix, styleUsingElementsRoot) { - var s, regExp = new RegExp("^" + prefix); - if(styleElementsRoot) { - s = styleElementsRoot.firstChild; - while(s) { - if(s.nodeType === Node.ELEMENT_NODE) { - removeRegExpFromStyleName((s), regExp) - } - s = s.nextSibling + if(accept === NodeFilter.FILTER_ACCEPT || accept === NodeFilter.FILTER_SKIP) { + child = node.firstChild; + while(child) { + s += serializeNode(ns, child); + child = child.nextSibling } - removeRegExpFromUsedStyleNames(styleElementsRoot, regExp); - if(styleUsingElementsRoot) { - removeRegExpFromUsedStyleNames(styleUsingElementsRoot, regExp) + if(node.nodeValue) { + s += escapeContent(node.nodeValue) } } - } - function determineStylesForNode(node, usedStyles) { - var elname = elements[node.localName], elns = elname && elname[node.namespaceURI], length = elns ? elns.length : 0, stylename, keyname, map, i; - for(i = 0;i < length;i += 1) { - stylename = node.getAttributeNS(elns[i].ns, elns[i].localname); - if(stylename) { - usedStyles = usedStyles || {}; - keyname = elns[i].keyname; - map = usedStyles[keyname] = usedStyles[keyname] || {}; - map[stylename] = 1 - } + if(qname) { + s += ""; + ns.pop() } - return usedStyles + return s } - function determineUsedStyles(styleUsingElementsRoot, usedStyles) { - var i, e; - determineStylesForNode(styleUsingElementsRoot, usedStyles); - i = styleUsingElementsRoot.firstChild; - while(i) { - if(i.nodeType === Node.ELEMENT_NODE) { - e = (i); - determineUsedStyles(e, usedStyles) - } - i = i.nextSibling + this.filter = null; + this.writeToString = function(node, nsmap) { + if(!node) { + return"" } + var ns = new Namespaces(nsmap); + return serializeNode(ns, node) } - function StyleDefinition(key, name, family) { - this.key = key; - this.name = name; - this.family = family; - this.requires = {} - } - function getStyleDefinition(stylename, stylefamily, knownStyles) { - var styleKey = stylename + '"' + stylefamily, styleDefinition = knownStyles[styleKey]; - if(!styleDefinition) { - styleDefinition = knownStyles[styleKey] = new StyleDefinition(styleKey, stylename, stylefamily) - } - return styleDefinition - } - function determineDependentStyles(styleUsingElementsRoot, styleScope, knownStyles) { - var elname = elements[styleUsingElementsRoot.localName], elns = elname && elname[styleUsingElementsRoot.namespaceURI], length = elns ? elns.length : 0, newScopeName = styleUsingElementsRoot.getAttributeNS(stylens, "name"), newScopeFamily = styleUsingElementsRoot.getAttributeNS(stylens, "family"), referencedStyleName, referencedStyleFamily, referencedStyleDef, i, e; - if(newScopeName && newScopeFamily) { - styleScope = getStyleDefinition(newScopeName, newScopeFamily, knownStyles) - } - if(styleScope) { - for(i = 0;i < length;i += 1) { - referencedStyleName = styleUsingElementsRoot.getAttributeNS(elns[i].ns, elns[i].localname); - if(referencedStyleName) { - referencedStyleFamily = elns[i].keyname; - referencedStyleDef = getStyleDefinition(referencedStyleName, referencedStyleFamily, knownStyles); - styleScope.requires[referencedStyleDef.key] = referencedStyleDef - } - } - } - i = styleUsingElementsRoot.firstChild; - while(i) { - if(i.nodeType === Node.ELEMENT_NODE) { - e = (i); - determineDependentStyles(e, styleScope, knownStyles) - } - i = i.nextSibling - } - return knownStyles - } - function inverse(elementstyles) { - var keyname, i, l, list, item, e = {}, map, array; - for(keyname in elementstyles) { - if(elementstyles.hasOwnProperty(keyname)) { - list = elementstyles[keyname]; - l = list.length; - for(i = 0;i < l;i += 1) { - item = list[i]; - map = e[item.en] = e[item.en] || {}; - array = map[item.ens] = map[item.ens] || []; - array.push({ns:item.ans, localname:item.a, keyname:keyname}) - } - } - } - return e - } - function mergeRequiredStyles(styleDependency, usedStyles) { - var family = usedStyles[styleDependency.family]; - if(!family) { - family = usedStyles[styleDependency.family] = {} - } - family[styleDependency.name] = 1; - Object.keys((styleDependency.requires)).forEach(function(requiredStyleKey) { - mergeRequiredStyles((styleDependency.requires[requiredStyleKey]), usedStyles) - }) - } - function mergeUsedAutomaticStyles(automaticStylesRoot, usedStyles) { - var automaticStyles = determineDependentStyles(automaticStylesRoot, null, {}); - Object.keys(automaticStyles).forEach(function(styleKey) { - var automaticStyleDefinition = automaticStyles[styleKey], usedFamily = usedStyles[automaticStyleDefinition.family]; - if(usedFamily && usedFamily.hasOwnProperty(automaticStyleDefinition.name)) { - mergeRequiredStyles(automaticStyleDefinition, usedStyles) - } - }) - } - function collectUsedFontFaces(usedFontFaceDeclMap, styleElementsRoot) { - var localNames = ["font-name", "font-name-asian", "font-name-complex"], currentNode; - function collectByAttribute(localName) { - var fontFaceName = currentNode.getAttributeNS(stylens, localName); - if(fontFaceName) { - usedFontFaceDeclMap[fontFaceName] = true - } - } - if(styleElementsRoot) { - currentNode = styleElementsRoot.firstChild; - while(currentNode) { - if(currentNode.nodeType === Node.ELEMENT_NODE) { - localNames.forEach(collectByAttribute); - collectUsedFontFaces(usedFontFaceDeclMap, (currentNode)) - } - currentNode = currentNode.nextSibling - } - } - } - this.collectUsedFontFaces = collectUsedFontFaces; - function changeFontFaceNames(styleElementsRoot, fontFaceNameChangeMap) { - var localNames = ["font-name", "font-name-asian", "font-name-complex"], currentNode; - function changeFontFaceNameByAttribute(localName) { - var fontFaceName = currentNode.getAttributeNS(stylens, localName); - if(fontFaceName && fontFaceNameChangeMap.hasOwnProperty(fontFaceName)) { - currentNode.setAttributeNS(stylens, "style:" + localName, fontFaceNameChangeMap[fontFaceName]) - } - } - if(styleElementsRoot) { - currentNode = styleElementsRoot.firstChild; - while(currentNode) { - if(currentNode.nodeType === Node.ELEMENT_NODE) { - localNames.forEach(changeFontFaceNameByAttribute); - changeFontFaceNames((currentNode), fontFaceNameChangeMap) - } - currentNode = currentNode.nextSibling - } +}; +/* + + Copyright (C) 2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("odf.Namespaces"); +runtime.loadClass("xmldom.LSSerializer"); +runtime.loadClass("odf.OdfNodeFilter"); +runtime.loadClass("odf.TextSerializer"); +gui.Clipboard = function Clipboard() { + var xmlSerializer, textSerializer, filter; + this.setDataFromRange = function(e, range) { + var result = true, setDataResult, clipboard = e.clipboardData, window = runtime.getWindow(), document = range.startContainer.ownerDocument, fragmentContainer; + if(!clipboard && window) { + clipboard = window.clipboardData } - } - this.changeFontFaceNames = changeFontFaceNames; - this.UsedStyleList = function(styleUsingElementsRoot, automaticStylesRoot) { - var usedStyles = {}; - this.uses = function(element) { - var localName = element.localName, name = element.getAttributeNS(drawns, "name") || element.getAttributeNS(stylens, "name"), keyName, map; - if(localName === "style") { - keyName = element.getAttributeNS(stylens, "family") - }else { - if(element.namespaceURI === numberns) { - keyName = "data" - }else { - keyName = localName - } - } - map = usedStyles[keyName]; - return map ? map[name] > 0 : false - }; - determineUsedStyles(styleUsingElementsRoot, usedStyles); - if(automaticStylesRoot) { - mergeUsedAutomaticStyles(automaticStylesRoot, usedStyles) + if(clipboard) { + fragmentContainer = document.createElement("span"); + fragmentContainer.appendChild(range.cloneContents()); + setDataResult = clipboard.setData("text/plain", textSerializer.writeToString(fragmentContainer)); + result = result && setDataResult; + setDataResult = clipboard.setData("text/html", xmlSerializer.writeToString(fragmentContainer, odf.Namespaces.namespaceMap)); + result = result && setDataResult; + e.preventDefault() + }else { + result = false } + return result }; - this.hasDerivedStyles = hasDerivedStyles; - this.prefixStyleNames = prefixStyleNames; - this.removePrefixFromStyleNames = removePrefixFromStyleNames; - this.determineStylesForNode = determineStylesForNode; - elements = inverse(elementstyles) + function init() { + xmlSerializer = new xmldom.LSSerializer; + textSerializer = new odf.TextSerializer; + filter = new odf.OdfNodeFilter; + xmlSerializer.filter = filter; + textSerializer.filter = filter + } + init() }; /* @@ -6746,432 +6733,623 @@ odf.StyleInfo = function StyleInfo() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils"); +runtime.loadClass("core.Base64"); +runtime.loadClass("core.Zip"); +runtime.loadClass("xmldom.LSSerializer"); +runtime.loadClass("odf.StyleInfo"); runtime.loadClass("odf.Namespaces"); -odf.OdfUtils = function OdfUtils() { - var textns = odf.Namespaces.textns, drawns = odf.Namespaces.drawns, whitespaceOnly = /^\s*$/, domUtils = new core.DomUtils; - function isImage(e) { - var name = e && e.localName; - return name === "image" && e.namespaceURI === drawns - } - this.isImage = isImage; - function isCharacterFrame(e) { - var name = e && e.localName; - return name === "frame" && (e.namespaceURI === drawns && e.getAttributeNS(textns, "anchor-type") === "as-char") - } - this.isCharacterFrame = isCharacterFrame; - this.isTextSpan = function(e) { - var name = e && e.localName; - return name === "span" && e.namespaceURI === textns - }; - function isParagraph(e) { - var name = e && e.localName; - return(name === "p" || name === "h") && e.namespaceURI === textns - } - this.isParagraph = isParagraph; - function getParagraphElement(node) { - while(node && !isParagraph(node)) { - node = node.parentNode +runtime.loadClass("odf.OdfNodeFilter"); +runtime.loadClass("odf.MetadataManager"); +(function() { + var styleInfo = new odf.StyleInfo, metadataManager, officens = "urn:oasis:names:tc:opendocument:xmlns:office:1.0", manifestns = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0", webodfns = "urn:webodf:names:scope", stylens = odf.Namespaces.stylens, nodeorder = ["meta", "settings", "scripts", "font-face-decls", "styles", "automatic-styles", "master-styles", "body"], automaticStylePrefix = (new Date).getTime() + "_webodf_", base64 = new core.Base64, documentStylesScope = "document-styles", documentContentScope = + "document-content"; + function getDirectChild(node, ns, name) { + node = node ? node.firstChild : null; + while(node) { + if(node.localName === name && node.namespaceURI === ns) { + return(node) + } + node = node.nextSibling } - return node + return null } - this.getParagraphElement = getParagraphElement; - this.isWithinTrackedChanges = function(node, container) { - while(node && node !== container) { - if(node.namespaceURI === textns && node.localName === "tracked-changes") { - return true + function getNodePosition(child) { + var i, l = nodeorder.length; + for(i = 0;i < l;i += 1) { + if(child.namespaceURI === officens && child.localName === nodeorder[i]) { + return i } - node = node.parentNode } - return false - }; - this.isListItem = function(e) { - var name = e && e.localName; - return name === "list-item" && e.namespaceURI === textns - }; - this.isLineBreak = function(e) { - var name = e && e.localName; - return name === "line-break" && e.namespaceURI === textns - }; - function isODFWhitespace(text) { - return/^[ \t\r\n]+$/.test(text) + return-1 } - this.isODFWhitespace = isODFWhitespace; - function isGroupingElement(e) { - var localName = e && e.localName; - if(/^(span|p|h|a|meta)$/.test(localName) && e.namespaceURI === textns || localName === "span" && e.className === "annotationHighlight") { - return true + function OdfStylesFilter(styleUsingElementsRoot, automaticStyles) { + var usedStyleList = new styleInfo.UsedStyleList(styleUsingElementsRoot, automaticStyles), odfNodeFilter = new odf.OdfNodeFilter; + this.acceptNode = function(node) { + var result = odfNodeFilter.acceptNode(node); + if(result === NodeFilter.FILTER_ACCEPT && (node.parentNode === automaticStyles && node.nodeType === Node.ELEMENT_NODE)) { + if(usedStyleList.uses((node))) { + result = NodeFilter.FILTER_ACCEPT + }else { + result = NodeFilter.FILTER_REJECT + } + } + return result } - return false } - this.isGroupingElement = isGroupingElement; - function isCharacterElement(e) { - var n = e && e.localName, ns, r = false; - if(n) { - ns = e.namespaceURI; - if(ns === textns) { - r = n === "s" || (n === "tab" || n === "line-break") - }else { - r = isCharacterFrame(e) + function OdfContentFilter(styleUsingElementsRoot, automaticStyles) { + var odfStylesFilter = new OdfStylesFilter(styleUsingElementsRoot, automaticStyles); + this.acceptNode = function(node) { + var result = odfStylesFilter.acceptNode(node); + if(result === NodeFilter.FILTER_ACCEPT && (node.parentNode && (node.parentNode.namespaceURI === odf.Namespaces.textns && (node.parentNode.localName === "s" || node.parentNode.localName === "tab")))) { + result = NodeFilter.FILTER_REJECT } + return result } - return r } - this.isCharacterElement = isCharacterElement; - function isSpaceElement(e) { - var n = e && e.localName, ns, r = false; - if(n) { - ns = e.namespaceURI; - if(ns === textns) { - r = n === "s" - } + function setChild(node, child) { + if(!child) { + return } - return r - } - this.isSpaceElement = isSpaceElement; - function firstChild(node) { - while(node.firstChild !== null && isGroupingElement(node)) { - node = node.firstChild + var childpos = getNodePosition(child), pos, c = node.firstChild; + if(childpos === -1) { + return } - return node - } - this.firstChild = firstChild; - function lastChild(node) { - while(node.lastChild !== null && isGroupingElement(node)) { - node = node.lastChild + while(c) { + pos = getNodePosition(c); + if(pos !== -1 && pos > childpos) { + break + } + c = c.nextSibling } - return node + node.insertBefore(child, c) } - this.lastChild = lastChild; - function previousNode(node) { - while(!isParagraph(node) && node.previousSibling === null) { - node = (node.parentNode) + odf.ODFElement = function ODFElement() { + }; + odf.ODFDocumentElement = function ODFDocumentElement() { + }; + odf.ODFDocumentElement.prototype = new odf.ODFElement; + odf.ODFDocumentElement.prototype.constructor = odf.ODFDocumentElement; + odf.ODFDocumentElement.prototype.automaticStyles; + odf.ODFDocumentElement.prototype.body; + odf.ODFDocumentElement.prototype.fontFaceDecls = null; + odf.ODFDocumentElement.prototype.manifest = null; + odf.ODFDocumentElement.prototype.masterStyles; + odf.ODFDocumentElement.prototype.meta; + odf.ODFDocumentElement.prototype.settings = null; + odf.ODFDocumentElement.prototype.styles; + odf.ODFDocumentElement.namespaceURI = officens; + odf.ODFDocumentElement.localName = "document"; + odf.OdfPart = function OdfPart(name, mimetype, container, zip) { + var self = this; + this.size = 0; + this.type = null; + this.name = name; + this.container = container; + this.url = null; + this.mimetype = mimetype; + this.document = null; + this.onstatereadychange = null; + this.onchange; + this.EMPTY = 0; + this.LOADING = 1; + this.DONE = 2; + this.state = this.EMPTY; + this.data = ""; + this.load = function() { + if(zip === null) { + return + } + this.mimetype = mimetype; + zip.loadAsDataURL(name, mimetype, function(err, url) { + if(err) { + runtime.log(err) + } + self.url = url; + if(self.onchange) { + self.onchange(self) + } + if(self.onstatereadychange) { + self.onstatereadychange(self) + } + }) } - return isParagraph(node) ? null : lastChild((node.previousSibling)) - } - this.previousNode = previousNode; - function nextNode(node) { - while(!isParagraph(node) && node.nextSibling === null) { - node = (node.parentNode) + }; + odf.OdfPart.prototype.load = function() { + }; + odf.OdfPart.prototype.getUrl = function() { + if(this.data) { + return"data:;base64," + base64.toBase64(this.data) } - return isParagraph(node) ? null : firstChild((node.nextSibling)) - } - this.nextNode = nextNode; - function scanLeftForNonSpace(node) { - var r = false; - while(node) { - if(node.nodeType === Node.TEXT_NODE) { - if(node.length === 0) { - node = previousNode(node) - }else { - return!isODFWhitespace(node.data.substr(node.length - 1, 1)) - } - }else { - if(isCharacterElement(node)) { - r = isSpaceElement(node) === false; - node = null + return null + }; + odf.OdfContainer = function OdfContainer(url, onstatereadychange) { + var self = this, zip, partMimetypes = {}, contentElement; + this.onstatereadychange = onstatereadychange; + this.onchange = null; + this.state = null; + this.rootElement; + function removeProcessingInstructions(element) { + var n = element.firstChild, next, e; + while(n) { + next = n.nextSibling; + if(n.nodeType === Node.ELEMENT_NODE) { + e = (n); + removeProcessingInstructions(e) }else { - node = previousNode(node) + if(n.nodeType === Node.PROCESSING_INSTRUCTION_NODE) { + element.removeChild(n) + } } + n = next } } - return r - } - this.scanLeftForNonSpace = scanLeftForNonSpace; - function lookLeftForCharacter(node) { - var text, r = 0; - if(node.nodeType === Node.TEXT_NODE && node.length > 0) { - text = node.data; - if(!isODFWhitespace(text.substr(text.length - 1, 1))) { - r = 1 - }else { - if(text.length === 1) { - r = scanLeftForNonSpace(previousNode(node)) ? 2 : 0 - }else { - r = isODFWhitespace(text.substr(text.length - 2, 1)) ? 0 : 2 + function setAutomaticStylesScope(stylesRootElement, scope) { + var n = stylesRootElement && stylesRootElement.firstChild; + while(n) { + if(n.nodeType === Node.ELEMENT_NODE) { + (n).setAttributeNS(webodfns, "scope", scope) } - } - }else { - if(isCharacterElement(node)) { - r = 1 + n = n.nextSibling } } - return r - } - this.lookLeftForCharacter = lookLeftForCharacter; - function lookRightForCharacter(node) { - var r = false; - if(node && (node.nodeType === Node.TEXT_NODE && node.length > 0)) { - r = !isODFWhitespace(node.data.substr(0, 1)) - }else { - if(isCharacterElement(node)) { - r = true + function unusedKey(key, map1, map2) { + var i = 0, postFixedKey; + key = key.replace(/\d+$/, ""); + postFixedKey = key; + while(map1.hasOwnProperty(postFixedKey) || map2.hasOwnProperty(postFixedKey)) { + i += 1; + postFixedKey = key + i } + return postFixedKey } - return r - } - this.lookRightForCharacter = lookRightForCharacter; - function scanLeftForAnyCharacter(node) { - var r = false; - node = node && lastChild(node); - while(node) { - if(node.nodeType === Node.TEXT_NODE && (node.length > 0 && !isODFWhitespace(node.data))) { - r = true; - break - } - if(isCharacterElement(node)) { - r = true; - break + function mapByFontFaceName(fontFaceDecls) { + var fn, result = {}, fontname; + fn = fontFaceDecls.firstChild; + while(fn) { + if(fn.nodeType === Node.ELEMENT_NODE && (fn.namespaceURI === stylens && fn.localName === "font-face")) { + fontname = (fn).getAttributeNS(stylens, "name"); + result[fontname] = fn + } + fn = fn.nextSibling } - node = previousNode(node) + return result } - return r - } - this.scanLeftForAnyCharacter = scanLeftForAnyCharacter; - function scanRightForAnyCharacter(node) { - var r = false; - node = node && firstChild(node); - while(node) { - if(node.nodeType === Node.TEXT_NODE && (node.length > 0 && !isODFWhitespace(node.data))) { - r = true; - break + function mergeFontFaceDecls(targetFontFaceDeclsRootElement, sourceFontFaceDeclsRootElement) { + var e, s, fontFaceName, newFontFaceName, targetFontFaceDeclsMap, sourceFontFaceDeclsMap, fontFaceNameChangeMap = {}; + targetFontFaceDeclsMap = mapByFontFaceName(targetFontFaceDeclsRootElement); + sourceFontFaceDeclsMap = mapByFontFaceName(sourceFontFaceDeclsRootElement); + e = sourceFontFaceDeclsRootElement.firstElementChild; + while(e) { + s = e.nextElementSibling; + if(e.namespaceURI === stylens && e.localName === "font-face") { + fontFaceName = e.getAttributeNS(stylens, "name"); + if(targetFontFaceDeclsMap.hasOwnProperty(fontFaceName)) { + if(!e.isEqualNode(targetFontFaceDeclsMap[fontFaceName])) { + newFontFaceName = unusedKey(fontFaceName, targetFontFaceDeclsMap, sourceFontFaceDeclsMap); + e.setAttributeNS(stylens, "style:name", newFontFaceName); + targetFontFaceDeclsRootElement.appendChild(e); + targetFontFaceDeclsMap[newFontFaceName] = e; + delete sourceFontFaceDeclsMap[fontFaceName]; + fontFaceNameChangeMap[fontFaceName] = newFontFaceName + } + }else { + targetFontFaceDeclsRootElement.appendChild(e); + targetFontFaceDeclsMap[fontFaceName] = e; + delete sourceFontFaceDeclsMap[fontFaceName] + } + } + e = s } - if(isCharacterElement(node)) { - r = true; - break + return fontFaceNameChangeMap + } + function cloneStylesInScope(stylesRootElement, scope) { + var copy = null, e, s, scopeAttrValue; + if(stylesRootElement) { + copy = stylesRootElement.cloneNode(true); + e = copy.firstElementChild; + while(e) { + s = e.nextElementSibling; + scopeAttrValue = e.getAttributeNS(webodfns, "scope"); + if(scopeAttrValue && scopeAttrValue !== scope) { + copy.removeChild(e) + } + e = s + } } - node = nextNode(node) + return copy } - return r - } - this.scanRightForAnyCharacter = scanRightForAnyCharacter; - function isTrailingWhitespace(textnode, offset) { - if(!isODFWhitespace(textnode.data.substr(offset))) { - return false + function cloneFontFaceDeclsUsedInStyles(fontFaceDeclsRootElement, stylesRootElementList) { + var e, nextSibling, fontFaceName, copy = null, usedFontFaceDeclMap = {}; + if(fontFaceDeclsRootElement) { + stylesRootElementList.forEach(function(stylesRootElement) { + styleInfo.collectUsedFontFaces(usedFontFaceDeclMap, stylesRootElement) + }); + copy = fontFaceDeclsRootElement.cloneNode(true); + e = copy.firstElementChild; + while(e) { + nextSibling = e.nextElementSibling; + fontFaceName = e.getAttributeNS(stylens, "name"); + if(!usedFontFaceDeclMap[fontFaceName]) { + copy.removeChild(e) + } + e = nextSibling + } + } + return copy } - return!scanRightForAnyCharacter(nextNode(textnode)) - } - this.isTrailingWhitespace = isTrailingWhitespace; - function isSignificantWhitespace(textNode, offset) { - var text = textNode.data, result; - if(!isODFWhitespace(text[offset])) { - return false + function initializeMetadataManager(metaRootElement) { + metadataManager = new odf.MetadataManager(metaRootElement) } - if(isCharacterElement(textNode.parentNode)) { - return false + function importRootNode(xmldoc) { + var doc = self.rootElement.ownerDocument, node; + if(xmldoc) { + removeProcessingInstructions(xmldoc.documentElement); + try { + node = doc.importNode(xmldoc.documentElement, true) + }catch(ignore) { + } + } + return node } - if(offset > 0) { - if(!isODFWhitespace(text[offset - 1])) { - result = true + function setState(state) { + self.state = state; + if(self.onchange) { + self.onchange(self) } - }else { - if(scanLeftForNonSpace(previousNode(textNode))) { - result = true + if(self.onstatereadychange) { + self.onstatereadychange(self) } } - if(result === true) { - return isTrailingWhitespace(textNode, offset) ? false : true + function setRootElement(root) { + contentElement = null; + self.rootElement = (root); + root.fontFaceDecls = getDirectChild(root, officens, "font-face-decls"); + root.styles = getDirectChild(root, officens, "styles"); + root.automaticStyles = getDirectChild(root, officens, "automatic-styles"); + root.masterStyles = getDirectChild(root, officens, "master-styles"); + root.body = getDirectChild(root, officens, "body"); + root.meta = getDirectChild(root, officens, "meta") } - return false - } - this.isSignificantWhitespace = isSignificantWhitespace; - this.isDowngradableSpaceElement = function(node) { - if(node.namespaceURI === textns && node.localName === "s") { - return scanLeftForNonSpace(previousNode(node)) && scanRightForAnyCharacter(nextNode(node)) - } - return false - }; - function getFirstNonWhitespaceChild(node) { - var child = node && node.firstChild; - while(child && (child.nodeType === Node.TEXT_NODE && whitespaceOnly.test(child.nodeValue))) { - child = child.nextSibling - } - return child - } - this.getFirstNonWhitespaceChild = getFirstNonWhitespaceChild; - function parseLength(length) { - var re = /(-?[0-9]*[0-9][0-9]*(\.[0-9]*)?|0+\.[0-9]*[1-9][0-9]*|\.[0-9]*[1-9][0-9]*)((cm)|(mm)|(in)|(pt)|(pc)|(px)|(%))/, m = re.exec(length); - if(!m) { - return null - } - return{value:parseFloat(m[1]), unit:m[3]} - } - this.parseLength = parseLength; - function parsePositiveLength(length) { - var result = parseLength(length); - if(result && (result.value <= 0 || result.unit === "%")) { - return null - } - return result - } - function parseNonNegativeLength(length) { - var result = parseLength(length); - if(result && (result.value < 0 || result.unit === "%")) { - return null - } - return result - } - this.parseNonNegativeLength = parseNonNegativeLength; - function parsePercentage(length) { - var result = parseLength(length); - if(result && result.unit !== "%") { - return null - } - return result - } - function parseFoFontSize(fontSize) { - return parsePositiveLength(fontSize) || parsePercentage(fontSize) - } - this.parseFoFontSize = parseFoFontSize; - function parseFoLineHeight(lineHeight) { - return parseNonNegativeLength(lineHeight) || parsePercentage(lineHeight) - } - this.parseFoLineHeight = parseFoLineHeight; - function getImpactedParagraphs(range) { - var outerContainer = (range.commonAncestorContainer), impactedParagraphs = []; - if(outerContainer.nodeType === Node.ELEMENT_NODE) { - impactedParagraphs = domUtils.getElementsByTagNameNS(outerContainer, textns, "p").concat(domUtils.getElementsByTagNameNS(outerContainer, textns, "h")) - } - while(outerContainer && !isParagraph(outerContainer)) { - outerContainer = outerContainer.parentNode - } - if(outerContainer) { - impactedParagraphs.push(outerContainer) + function handleFlatXml(xmldoc) { + var root = importRootNode(xmldoc); + if(!root || (root.localName !== "document" || root.namespaceURI !== officens)) { + setState(OdfContainer.INVALID); + return + } + setRootElement((root)); + setState(OdfContainer.DONE) } - return impactedParagraphs.filter(function(n) { - return domUtils.rangeIntersectsNode(range, n) - }) - } - this.getImpactedParagraphs = getImpactedParagraphs; - function isAcceptedNode(node) { - switch(node.namespaceURI) { - case odf.Namespaces.drawns: - ; - case odf.Namespaces.svgns: - ; - case odf.Namespaces.dr3dns: - return false; - case odf.Namespaces.textns: - switch(node.localName) { - case "note-body": - ; - case "ruby-text": - return false - } - break; - case odf.Namespaces.officens: - switch(node.localName) { - case "annotation": - ; - case "binary-data": - ; - case "event-listeners": - return false - } - break; - default: - switch(node.localName) { - case "editinfo": - return false - } - break + function handleStylesXml(xmldoc) { + var node = importRootNode(xmldoc), root = self.rootElement, n; + if(!node || (node.localName !== "document-styles" || node.namespaceURI !== officens)) { + setState(OdfContainer.INVALID); + return + } + root.fontFaceDecls = getDirectChild(node, officens, "font-face-decls"); + setChild(root, root.fontFaceDecls); + n = getDirectChild(node, officens, "styles"); + root.styles = n || xmldoc.createElementNS(officens, "styles"); + setChild(root, root.styles); + n = getDirectChild(node, officens, "automatic-styles"); + root.automaticStyles = n || xmldoc.createElementNS(officens, "automatic-styles"); + setAutomaticStylesScope(root.automaticStyles, documentStylesScope); + setChild(root, root.automaticStyles); + node = getDirectChild(node, officens, "master-styles"); + root.masterStyles = node || xmldoc.createElementNS(officens, "master-styles"); + setChild(root, root.masterStyles); + styleInfo.prefixStyleNames(root.automaticStyles, automaticStylePrefix, root.masterStyles) } - return true - } - function isSignificantTextContent(textNode) { - return Boolean(getParagraphElement(textNode) && (!isODFWhitespace(textNode.textContent) || isSignificantWhitespace(textNode, 0))) - } - function includeNode(range, nodeRange, includePartial) { - return includePartial && domUtils.rangesIntersect(range, nodeRange) || domUtils.containsRange(range, nodeRange) - } - function getTextNodes(range, includePartial) { - var document = range.startContainer.ownerDocument, nodeRange = document.createRange(), textNodes; - function nodeFilter(node) { - nodeRange.selectNodeContents(node); - if(node.nodeType === Node.TEXT_NODE) { - if(includeNode(range, nodeRange, includePartial)) { - return isSignificantTextContent(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT - } + function handleContentXml(xmldoc) { + var node = importRootNode(xmldoc), root, automaticStyles, fontFaceDecls, fontFaceNameChangeMap, c; + if(!node || (node.localName !== "document-content" || node.namespaceURI !== officens)) { + setState(OdfContainer.INVALID); + return + } + root = self.rootElement; + fontFaceDecls = getDirectChild(node, officens, "font-face-decls"); + if(root.fontFaceDecls && fontFaceDecls) { + fontFaceNameChangeMap = mergeFontFaceDecls(root.fontFaceDecls, fontFaceDecls) }else { - if(domUtils.rangesIntersect(range, nodeRange)) { - if(isAcceptedNode(node)) { - return NodeFilter.FILTER_SKIP - } + if(fontFaceDecls) { + root.fontFaceDecls = fontFaceDecls; + setChild(root, fontFaceDecls) } } - return NodeFilter.FILTER_REJECT - } - textNodes = domUtils.getNodesInRange(range, nodeFilter); - nodeRange.detach(); - return textNodes - } - this.getTextNodes = getTextNodes; - this.getTextElements = function(range, includePartial, includeInsignificantWhitespace) { - var document = range.startContainer.ownerDocument, nodeRange = document.createRange(), elements; - function nodeFilter(node) { - nodeRange.selectNodeContents(node); - if(isCharacterElement(node.parentNode)) { - return NodeFilter.FILTER_REJECT + automaticStyles = getDirectChild(node, officens, "automatic-styles"); + setAutomaticStylesScope(automaticStyles, documentContentScope); + if(fontFaceNameChangeMap) { + styleInfo.changeFontFaceNames(automaticStyles, fontFaceNameChangeMap) } - if(node.nodeType === Node.TEXT_NODE) { - if(includeNode(range, nodeRange, includePartial)) { - if(includeInsignificantWhitespace || isSignificantTextContent(node)) { - return NodeFilter.FILTER_ACCEPT - } + if(root.automaticStyles && automaticStyles) { + c = automaticStyles.firstChild; + while(c) { + root.automaticStyles.appendChild(c); + c = automaticStyles.firstChild } }else { - if(isCharacterElement(node)) { - if(includeNode(range, nodeRange, includePartial)) { - return NodeFilter.FILTER_ACCEPT - } - }else { - if(isAcceptedNode(node) || isGroupingElement(node)) { - return NodeFilter.FILTER_SKIP - } + if(automaticStyles) { + root.automaticStyles = automaticStyles; + setChild(root, automaticStyles) } } - return NodeFilter.FILTER_REJECT + node = getDirectChild(node, officens, "body"); + if(node === null) { + throw" tag is mising."; + } + root.body = node; + setChild(root, root.body) } - elements = domUtils.getNodesInRange(range, nodeFilter); - nodeRange.detach(); - return elements - }; - this.getParagraphElements = function(range) { - var document = range.startContainer.ownerDocument, nodeRange = document.createRange(), elements; - function nodeFilter(node) { - nodeRange.selectNodeContents(node); - if(isParagraph(node)) { - if(domUtils.rangesIntersect(range, nodeRange)) { - return NodeFilter.FILTER_ACCEPT + function handleMetaXml(xmldoc) { + var node = importRootNode(xmldoc), root; + if(!node || (node.localName !== "document-meta" || node.namespaceURI !== officens)) { + return + } + root = self.rootElement; + node = getDirectChild(node, officens, "meta"); + root.meta = node || xmldoc.createElementNS(officens, "meta"); + setChild(root, root.meta); + initializeMetadataManager(root.meta) + } + function handleSettingsXml(xmldoc) { + var node = importRootNode(xmldoc), root; + if(!node || (node.localName !== "document-settings" || node.namespaceURI !== officens)) { + return + } + root = self.rootElement; + root.settings = getDirectChild(node, officens, "settings"); + setChild(root, root.settings) + } + function handleManifestXml(xmldoc) { + var node = importRootNode(xmldoc), root, e; + if(!node || (node.localName !== "manifest" || node.namespaceURI !== manifestns)) { + return + } + root = self.rootElement; + root.manifest = (node); + e = root.manifest.firstElementChild; + while(e) { + if(e.localName === "file-entry" && e.namespaceURI === manifestns) { + partMimetypes[e.getAttributeNS(manifestns, "full-path")] = e.getAttributeNS(manifestns, "media-type") } + e = e.nextElementSibling + } + } + function loadNextComponent(remainingComponents) { + var component = remainingComponents.shift(); + if(component) { + zip.loadAsDOM(component.path, function(err, xmldoc) { + component.handler(xmldoc); + if(err || self.state === OdfContainer.INVALID) { + return + } + loadNextComponent(remainingComponents) + }) }else { - if(isAcceptedNode(node) || isGroupingElement(node)) { - return NodeFilter.FILTER_SKIP - } + setState(OdfContainer.DONE) } - return NodeFilter.FILTER_REJECT } - elements = domUtils.getNodesInRange(range, nodeFilter); - nodeRange.detach(); - return elements - }; - this.getImageElements = function(range) { - var document = range.startContainer.ownerDocument, nodeRange = document.createRange(), elements; - function nodeFilter(node) { - nodeRange.selectNodeContents(node); - if(isImage(node) && domUtils.containsRange(range, nodeRange)) { - return NodeFilter.FILTER_ACCEPT + function loadComponents() { + var componentOrder = [{path:"styles.xml", handler:handleStylesXml}, {path:"content.xml", handler:handleContentXml}, {path:"meta.xml", handler:handleMetaXml}, {path:"settings.xml", handler:handleSettingsXml}, {path:"META-INF/manifest.xml", handler:handleManifestXml}]; + loadNextComponent(componentOrder) + } + function createDocumentElement(name) { + var s = ""; + function defineNamespace(prefix, ns) { + s += " xmlns:" + prefix + '="' + ns + '"' } - return NodeFilter.FILTER_SKIP + odf.Namespaces.forEachPrefix(defineNamespace); + return'' } - elements = domUtils.getNodesInRange(range, nodeFilter); - nodeRange.detach(); - return elements - } -}; + function serializeMetaXml() { + var serializer = new xmldom.LSSerializer, s = createDocumentElement("document-meta"); + serializer.filter = new odf.OdfNodeFilter; + s += serializer.writeToString(self.rootElement.meta, odf.Namespaces.namespaceMap); + s += ""; + return s + } + function createManifestEntry(fullPath, mediaType) { + var element = document.createElementNS(manifestns, "manifest:file-entry"); + element.setAttributeNS(manifestns, "manifest:full-path", fullPath); + element.setAttributeNS(manifestns, "manifest:media-type", mediaType); + return element + } + function serializeManifestXml() { + var header = '\n', xml = '', manifest = (runtime.parseXML(xml)), manifestRoot = getDirectChild(manifest, manifestns, "manifest"), serializer = new xmldom.LSSerializer, fullPath; + for(fullPath in partMimetypes) { + if(partMimetypes.hasOwnProperty(fullPath)) { + manifestRoot.appendChild(createManifestEntry(fullPath, partMimetypes[fullPath])) + } + } + serializer.filter = new odf.OdfNodeFilter; + return header + serializer.writeToString(manifest, odf.Namespaces.namespaceMap) + } + function serializeSettingsXml() { + var serializer = new xmldom.LSSerializer, s = createDocumentElement("document-settings"); + serializer.filter = new odf.OdfNodeFilter; + s += serializer.writeToString(self.rootElement.settings, odf.Namespaces.namespaceMap); + s += ""; + return s + } + function serializeStylesXml() { + var fontFaceDecls, automaticStyles, masterStyles, nsmap = odf.Namespaces.namespaceMap, serializer = new xmldom.LSSerializer, s = createDocumentElement("document-styles"); + automaticStyles = cloneStylesInScope(self.rootElement.automaticStyles, documentStylesScope); + masterStyles = (self.rootElement.masterStyles.cloneNode(true)); + fontFaceDecls = cloneFontFaceDeclsUsedInStyles(self.rootElement.fontFaceDecls, [masterStyles, self.rootElement.styles, automaticStyles]); + styleInfo.removePrefixFromStyleNames(automaticStyles, automaticStylePrefix, masterStyles); + serializer.filter = new OdfStylesFilter(masterStyles, automaticStyles); + s += serializer.writeToString(fontFaceDecls, nsmap); + s += serializer.writeToString(self.rootElement.styles, nsmap); + s += serializer.writeToString(automaticStyles, nsmap); + s += serializer.writeToString(masterStyles, nsmap); + s += ""; + return s + } + function serializeContentXml() { + var fontFaceDecls, automaticStyles, nsmap = odf.Namespaces.namespaceMap, serializer = new xmldom.LSSerializer, s = createDocumentElement("document-content"); + automaticStyles = cloneStylesInScope(self.rootElement.automaticStyles, documentContentScope); + fontFaceDecls = cloneFontFaceDeclsUsedInStyles(self.rootElement.fontFaceDecls, [automaticStyles]); + serializer.filter = new OdfContentFilter(self.rootElement.body, automaticStyles); + s += serializer.writeToString(fontFaceDecls, nsmap); + s += serializer.writeToString(automaticStyles, nsmap); + s += serializer.writeToString(self.rootElement.body, nsmap); + s += ""; + return s + } + function createElement(type) { + var original = document.createElementNS(type.namespaceURI, type.localName), method, iface = new type.Type; + for(method in iface) { + if(iface.hasOwnProperty(method)) { + original[method] = iface[method] + } + } + return original + } + function loadFromXML(url, callback) { + runtime.loadXML(url, function(err, dom) { + if(err) { + callback(err) + }else { + handleFlatXml(dom) + } + }) + } + this.setRootElement = setRootElement; + this.getContentElement = function() { + var body; + if(!contentElement) { + body = self.rootElement.body; + contentElement = getDirectChild(body, officens, "text") || (getDirectChild(body, officens, "presentation") || getDirectChild(body, officens, "spreadsheet")) + } + if(!contentElement) { + throw"Could not find content element in ."; + } + return contentElement + }; + this.getDocumentType = function() { + var content = self.getContentElement(); + return content && content.localName + }; + this.getMetadataManager = function() { + return metadataManager + }; + this.getPart = function(partname) { + return new odf.OdfPart(partname, partMimetypes[partname], self, zip) + }; + this.getPartData = function(url, callback) { + zip.load(url, callback) + }; + function updateMetadataForSaving() { + var generatorString, window = runtime.getWindow(); + generatorString = "WebODF/" + (String(typeof webodf_version) !== "undefined" ? webodf_version : "FromSource"); + if(window) { + generatorString = generatorString + " " + window.navigator.userAgent + } + metadataManager.setMetadata({"meta:generator":generatorString}, null) + } + function createEmptyTextDocument() { + var emptyzip = new core.Zip("", null), data = runtime.byteArrayFromString("application/vnd.oasis.opendocument.text", "utf8"), root = self.rootElement, text = document.createElementNS(officens, "text"); + emptyzip.save("mimetype", data, false, new Date); + function addToplevelElement(memberName, realLocalName) { + var element; + if(!realLocalName) { + realLocalName = memberName + } + element = document.createElementNS(officens, realLocalName); + root[memberName] = element; + root.appendChild(element) + } + addToplevelElement("meta"); + addToplevelElement("settings"); + addToplevelElement("scripts"); + addToplevelElement("fontFaceDecls", "font-face-decls"); + addToplevelElement("styles"); + addToplevelElement("automaticStyles", "automatic-styles"); + addToplevelElement("masterStyles", "master-styles"); + addToplevelElement("body"); + root.body.appendChild(text); + initializeMetadataManager(root.meta); + setState(OdfContainer.DONE); + return emptyzip + } + function fillZip() { + var data, date = new Date; + updateMetadataForSaving(); + data = runtime.byteArrayFromString(serializeSettingsXml(), "utf8"); + zip.save("settings.xml", data, true, date); + data = runtime.byteArrayFromString(serializeMetaXml(), "utf8"); + zip.save("meta.xml", data, true, date); + data = runtime.byteArrayFromString(serializeStylesXml(), "utf8"); + zip.save("styles.xml", data, true, date); + data = runtime.byteArrayFromString(serializeContentXml(), "utf8"); + zip.save("content.xml", data, true, date); + data = runtime.byteArrayFromString(serializeManifestXml(), "utf8"); + zip.save("META-INF/manifest.xml", data, true, date) + } + function createByteArray(successCallback, errorCallback) { + fillZip(); + zip.createByteArray(successCallback, errorCallback) + } + this.createByteArray = createByteArray; + function saveAs(newurl, callback) { + fillZip(); + zip.writeAs(newurl, function(err) { + callback(err) + }) + } + this.saveAs = saveAs; + this.save = function(callback) { + saveAs(url, callback) + }; + this.getUrl = function() { + return url + }; + this.setBlob = function(filename, mimetype, content) { + var data = base64.convertBase64ToByteArray(content), date = new Date; + zip.save(filename, data, false, date); + if(partMimetypes.hasOwnProperty(filename)) { + runtime.log(filename + " has been overwritten.") + } + partMimetypes[filename] = mimetype + }; + this.removeBlob = function(filename) { + var foundAndRemoved = zip.remove(filename); + runtime.assert(foundAndRemoved, "file is not found: " + filename); + delete partMimetypes[filename] + }; + this.state = OdfContainer.LOADING; + this.rootElement = (createElement({Type:odf.ODFDocumentElement, namespaceURI:odf.ODFDocumentElement.namespaceURI, localName:odf.ODFDocumentElement.localName})); + if(url) { + zip = new core.Zip(url, function(err, zipobject) { + zip = zipobject; + if(err) { + loadFromXML(url, function(xmlerr) { + if(err) { + zip.error = err + "\n" + xmlerr; + setState(OdfContainer.INVALID) + } + }) + }else { + loadComponents() + } + }) + }else { + zip = createEmptyTextDocument() + } + }; + odf.OdfContainer.EMPTY = 0; + odf.OdfContainer.LOADING = 1; + odf.OdfContainer.DONE = 2; + odf.OdfContainer.INVALID = 3; + odf.OdfContainer.SAVING = 4; + odf.OdfContainer.MODIFIED = 5; + odf.OdfContainer.getContainer = function(url) { + return new odf.OdfContainer(url, null) + }; + return odf.OdfContainer +})(); /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -7206,45 +7384,189 @@ odf.OdfUtils = function OdfUtils() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.OdfUtils"); -odf.TextSerializer = function TextSerializer() { - var self = this, odfUtils = new odf.OdfUtils; - function serializeNode(node) { - var s = "", accept = self.filter ? self.filter.acceptNode(node) : NodeFilter.FILTER_ACCEPT, nodeType = node.nodeType, child; - if(accept === NodeFilter.FILTER_ACCEPT || accept === NodeFilter.FILTER_SKIP) { - child = node.firstChild; - while(child) { - s += serializeNode(child); - child = child.nextSibling - } +runtime.loadClass("core.Base64"); +runtime.loadClass("xmldom.XPath"); +runtime.loadClass("odf.OdfContainer"); +(function() { + var xpath = xmldom.XPath, base64 = new core.Base64; + function getEmbeddedFontDeclarations(fontFaceDecls) { + var decls = {}, fonts, i, font, name, uris, href, family; + if(!fontFaceDecls) { + return decls } - if(accept === NodeFilter.FILTER_ACCEPT) { - if(nodeType === Node.ELEMENT_NODE && odfUtils.isParagraph(node)) { - s += "\n" - }else { - if(nodeType === Node.TEXT_NODE && node.textContent) { - s += node.textContent - } + fonts = xpath.getODFElementsWithXPath(fontFaceDecls, "style:font-face[svg:font-face-src]", odf.Namespaces.lookupNamespaceURI); + for(i = 0;i < fonts.length;i += 1) { + font = fonts[i]; + name = font.getAttributeNS(odf.Namespaces.stylens, "name"); + family = font.getAttributeNS(odf.Namespaces.svgns, "font-family"); + uris = xpath.getODFElementsWithXPath(font, "svg:font-face-src/svg:font-face-uri", odf.Namespaces.lookupNamespaceURI); + if(uris.length > 0) { + href = uris[0].getAttributeNS(odf.Namespaces.xlinkns, "href"); + decls[name] = {href:href, family:family} } } - return s + return decls } - this.filter = null; - this.writeToString = function(node) { - var plainText; - if(!node) { - return"" - } - plainText = serializeNode(node); - if(plainText[plainText.length - 1] === "\n") { - plainText = plainText.substr(0, plainText.length - 1) + function addFontToCSS(name, font, fontdata, stylesheet) { + var cssFamily = font.family || name, rule = "@font-face { font-family: '" + cssFamily + "'; src: " + "url(data:application/x-font-ttf;charset=binary;base64," + base64.convertUTF8ArrayToBase64(fontdata) + ') format("truetype"); }'; + try { + stylesheet.insertRule(rule, stylesheet.cssRules.length) + }catch(e) { + runtime.log("Problem inserting rule in CSS: " + runtime.toJson(e) + "\nRule: " + rule) } - return plainText } -}; -/* - - Copyright (C) 2012-2013 KO GmbH + function loadFontIntoCSS(embeddedFontDeclarations, odfContainer, pos, stylesheet, callback) { + var name, i = 0, n; + for(n in embeddedFontDeclarations) { + if(embeddedFontDeclarations.hasOwnProperty(n)) { + if(i === pos) { + name = n; + break + } + i += 1 + } + } + if(!name) { + if(callback) { + callback() + } + return + } + odfContainer.getPartData(embeddedFontDeclarations[name].href, function(err, fontdata) { + if(err) { + runtime.log(err) + }else { + if(!fontdata) { + runtime.log("missing font data for " + embeddedFontDeclarations[name].href) + }else { + addFontToCSS(name, embeddedFontDeclarations[name], fontdata, stylesheet) + } + } + loadFontIntoCSS(embeddedFontDeclarations, odfContainer, pos + 1, stylesheet, callback) + }) + } + function loadFontsIntoCSS(embeddedFontDeclarations, odfContainer, stylesheet) { + loadFontIntoCSS(embeddedFontDeclarations, odfContainer, 0, stylesheet) + } + odf.FontLoader = function FontLoader() { + this.loadFonts = function(odfContainer, stylesheet) { + var embeddedFontDeclarations, fontFaceDecls = odfContainer.rootElement.fontFaceDecls; + while(stylesheet.cssRules.length) { + stylesheet.deleteRule(stylesheet.cssRules.length - 1) + } + if(fontFaceDecls) { + embeddedFontDeclarations = getEmbeddedFontDeclarations(fontFaceDecls); + loadFontsIntoCSS(embeddedFontDeclarations, odfContainer, stylesheet) + } + } + }; + return odf.FontLoader +})(); +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("core.DomUtils"); +runtime.loadClass("core.Utils"); +odf.ObjectNameGenerator = function ObjectNameGenerator(odfContainer, memberId) { + var stylens = odf.Namespaces.stylens, drawns = odf.Namespaces.drawns, xlinkns = odf.Namespaces.xlinkns, domUtils = new core.DomUtils, utils = new core.Utils, memberIdHash = utils.hashString(memberId), styleNameGenerator = null, frameNameGenerator = null, imageNameGenerator = null, existingFrameNames = {}, existingImageNames = {}; + function NameGenerator(prefix, findExistingNames) { + var reportedNames = {}; + this.generateName = function() { + var existingNames = findExistingNames(), startIndex = 0, name; + do { + name = prefix + startIndex; + startIndex += 1 + }while(reportedNames[name] || existingNames[name]); + reportedNames[name] = true; + return name + } + } + function getAllStyleNames() { + var styleElements = [odfContainer.rootElement.automaticStyles, odfContainer.rootElement.styles], styleNames = {}; + function getStyleNames(styleListElement) { + var e = styleListElement.firstElementChild; + while(e) { + if(e.namespaceURI === stylens && e.localName === "style") { + styleNames[e.getAttributeNS(stylens, "name")] = true + } + e = e.nextElementSibling + } + } + styleElements.forEach(getStyleNames); + return styleNames + } + this.generateStyleName = function() { + if(styleNameGenerator === null) { + styleNameGenerator = new NameGenerator("auto" + memberIdHash + "_", function() { + return getAllStyleNames() + }) + } + return styleNameGenerator.generateName() + }; + this.generateFrameName = function() { + if(frameNameGenerator === null) { + var nodes = domUtils.getElementsByTagNameNS(odfContainer.rootElement.body, drawns, "frame"); + nodes.forEach(function(frame) { + existingFrameNames[frame.getAttributeNS(drawns, "name")] = true + }); + frameNameGenerator = new NameGenerator("fr" + memberIdHash + "_", function() { + return existingFrameNames + }) + } + return frameNameGenerator.generateName() + }; + this.generateImageName = function() { + if(imageNameGenerator === null) { + var nodes = domUtils.getElementsByTagNameNS(odfContainer.rootElement.body, drawns, "image"); + nodes.forEach(function(image) { + var path = image.getAttributeNS(xlinkns, "href"); + path = path.substring("Pictures/".length, path.lastIndexOf(".")); + existingImageNames[path] = true + }); + imageNameGenerator = new NameGenerator("img" + memberIdHash + "_", function() { + return existingImageNames + }) + } + return imageNameGenerator.generateName() + } +}; +/* + + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -7282,6 +7604,14 @@ odf.TextSerializer = function TextSerializer() { runtime.loadClass("core.DomUtils"); runtime.loadClass("core.LoopWatchDog"); runtime.loadClass("odf.Namespaces"); +odf.TextStyleApplicatorFormatting = function() { +}; +odf.TextStyleApplicatorFormatting.prototype.getAppliedStylesForElement = function(textnode) { +}; +odf.TextStyleApplicatorFormatting.prototype.createDerivedStyleObject = function(parentStyleName, family, overrides) { +}; +odf.TextStyleApplicatorFormatting.prototype.updateStyle = function(styleNode, properties) { +}; odf.TextStyleApplicator = function TextStyleApplicator(objectNameGenerator, formatting, automaticStyles) { var domUtils = new core.DomUtils, textns = odf.Namespaces.textns, stylens = odf.Namespaces.stylens, textProperties = "style:text-properties", webodfns = "urn:webodf:names:scope"; function StyleLookup(info) { @@ -7349,9 +7679,9 @@ odf.TextStyleApplicator = function TextStyleApplicator(objectNameGenerator, form styledNodes.push(node); node = node.nextSibling } - styledNodes.forEach(function(node) { - if(node.parentNode !== styledContainer) { - styledContainer.appendChild(node) + styledNodes.forEach(function(n) { + if(n.parentNode !== styledContainer) { + styledContainer.appendChild(n) } }); if(node && moveTrailing) { @@ -7368,17 +7698,18 @@ odf.TextStyleApplicator = function TextStyleApplicator(objectNameGenerator, form } this.applyStyle = function(textNodes, limits, info) { var textPropsOnly = {}, isStyled, container, styleCache, styleLookup; - runtime.assert(info && info[textProperties], "applyStyle without any text properties"); + runtime.assert(info && info.hasOwnProperty(textProperties), "applyStyle without any text properties"); textPropsOnly[textProperties] = info[textProperties]; styleCache = new StyleManager(textPropsOnly); styleLookup = new StyleLookup(textPropsOnly); - textNodes.forEach(function(n) { + function apply(n) { isStyled = styleLookup.isStyleApplied(n); if(isStyled === false) { - container = moveToNewSpan((n), limits); + container = moveToNewSpan(n, limits); styleCache.applyStyleToContainer(container) } - }) + } + textNodes.forEach(apply) } }; /* @@ -7418,1271 +7749,1261 @@ odf.TextStyleApplicator = function TextStyleApplicator(objectNameGenerator, form @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ +runtime.loadClass("core.Utils"); +runtime.loadClass("odf.ObjectNameGenerator"); runtime.loadClass("odf.Namespaces"); +runtime.loadClass("odf.OdfContainer"); +runtime.loadClass("odf.StyleInfo"); runtime.loadClass("odf.OdfUtils"); -runtime.loadClass("xmldom.XPath"); -runtime.loadClass("core.CSSUnits"); -odf.Style2CSS = function Style2CSS() { - var drawns = odf.Namespaces.drawns, fons = odf.Namespaces.fons, stylens = odf.Namespaces.stylens, svgns = odf.Namespaces.svgns, tablens = odf.Namespaces.tablens, textns = odf.Namespaces.textns, xlinkns = odf.Namespaces.xlinkns, presentationns = odf.Namespaces.presentationns, familynamespaceprefixes = {"graphic":"draw", "drawing-page":"draw", "paragraph":"text", "presentation":"presentation", "ruby":"text", "section":"text", "table":"table", "table-cell":"table", "table-column":"table", "table-row":"table", - "text":"text", "list":"text", "page":"office"}, familytagnames = {"graphic":["circle", "connected", "control", "custom-shape", "ellipse", "frame", "g", "line", "measure", "page", "page-thumbnail", "path", "polygon", "polyline", "rect", "regular-polygon"], "paragraph":["alphabetical-index-entry-template", "h", "illustration-index-entry-template", "index-source-style", "object-index-entry-template", "p", "table-index-entry-template", "table-of-content-entry-template", "user-index-entry-template"], - "presentation":["caption", "circle", "connector", "control", "custom-shape", "ellipse", "frame", "g", "line", "measure", "page-thumbnail", "path", "polygon", "polyline", "rect", "regular-polygon"], "drawing-page":["caption", "circle", "connector", "control", "page", "custom-shape", "ellipse", "frame", "g", "line", "measure", "page-thumbnail", "path", "polygon", "polyline", "rect", "regular-polygon"], "ruby":["ruby", "ruby-text"], "section":["alphabetical-index", "bibliography", "illustration-index", - "index-title", "object-index", "section", "table-of-content", "table-index", "user-index"], "table":["background", "table"], "table-cell":["body", "covered-table-cell", "even-columns", "even-rows", "first-column", "first-row", "last-column", "last-row", "odd-columns", "odd-rows", "table-cell"], "table-column":["table-column"], "table-row":["table-row"], "text":["a", "index-entry-chapter", "index-entry-link-end", "index-entry-link-start", "index-entry-page-number", "index-entry-span", "index-entry-tab-stop", - "index-entry-text", "index-title-template", "linenumbering-configuration", "list-level-style-number", "list-level-style-bullet", "outline-level-style", "span"], "list":["list-item"]}, textPropertySimpleMapping = [[fons, "color", "color"], [fons, "background-color", "background-color"], [fons, "font-weight", "font-weight"], [fons, "font-style", "font-style"]], bgImageSimpleMapping = [[stylens, "repeat", "background-repeat"]], paragraphPropertySimpleMapping = [[fons, "background-color", "background-color"], - [fons, "text-align", "text-align"], [fons, "text-indent", "text-indent"], [fons, "padding", "padding"], [fons, "padding-left", "padding-left"], [fons, "padding-right", "padding-right"], [fons, "padding-top", "padding-top"], [fons, "padding-bottom", "padding-bottom"], [fons, "border-left", "border-left"], [fons, "border-right", "border-right"], [fons, "border-top", "border-top"], [fons, "border-bottom", "border-bottom"], [fons, "margin", "margin"], [fons, "margin-left", "margin-left"], [fons, "margin-right", - "margin-right"], [fons, "margin-top", "margin-top"], [fons, "margin-bottom", "margin-bottom"], [fons, "border", "border"]], graphicPropertySimpleMapping = [[fons, "background-color", "background-color"], [fons, "min-height", "min-height"], [drawns, "stroke", "border"], [svgns, "stroke-color", "border-color"], [svgns, "stroke-width", "border-width"], [fons, "border", "border"], [fons, "border-left", "border-left"], [fons, "border-right", "border-right"], [fons, "border-top", "border-top"], [fons, - "border-bottom", "border-bottom"]], tablecellPropertySimpleMapping = [[fons, "background-color", "background-color"], [fons, "border-left", "border-left"], [fons, "border-right", "border-right"], [fons, "border-top", "border-top"], [fons, "border-bottom", "border-bottom"], [fons, "border", "border"]], tablecolumnPropertySimpleMapping = [[stylens, "column-width", "width"]], tablerowPropertySimpleMapping = [[stylens, "row-height", "height"], [fons, "keep-together", null]], tablePropertySimpleMapping = - [[stylens, "width", "width"], [fons, "margin-left", "margin-left"], [fons, "margin-right", "margin-right"], [fons, "margin-top", "margin-top"], [fons, "margin-bottom", "margin-bottom"]], pageContentPropertySimpleMapping = [[fons, "background-color", "background-color"], [fons, "padding", "padding"], [fons, "padding-left", "padding-left"], [fons, "padding-right", "padding-right"], [fons, "padding-top", "padding-top"], [fons, "padding-bottom", "padding-bottom"], [fons, "border", "border"], [fons, - "border-left", "border-left"], [fons, "border-right", "border-right"], [fons, "border-top", "border-top"], [fons, "border-bottom", "border-bottom"], [fons, "margin", "margin"], [fons, "margin-left", "margin-left"], [fons, "margin-right", "margin-right"], [fons, "margin-top", "margin-top"], [fons, "margin-bottom", "margin-bottom"]], pageSizePropertySimpleMapping = [[fons, "page-width", "width"], [fons, "page-height", "height"]], borderPropertyMap = {"border":true, "border-left":true, "border-right":true, - "border-top":true, "border-bottom":true, "stroke-width":true}, fontFaceDeclsMap = {}, utils = new odf.OdfUtils, documentType, odfRoot, defaultFontSize, xpath = new xmldom.XPath, cssUnits = new core.CSSUnits; - function getStyleMap(stylesnode) { - var stylemap = {}, node, name, family, style; - if(!stylesnode) { - return stylemap +runtime.loadClass("odf.TextStyleApplicator"); +odf.Formatting = function Formatting() { + var self = this, odfContainer, styleInfo = new odf.StyleInfo, svgns = odf.Namespaces.svgns, stylens = odf.Namespaces.stylens, textns = odf.Namespaces.textns, numberns = odf.Namespaces.numberns, fons = odf.Namespaces.fons, odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils, utils = new core.Utils, builtInDefaultStyleAttributesByFamily = {"paragraph":{"style:paragraph-properties":{"fo:text-align":"left"}}}, defaultPageFormatSettings = {width:21.001, height:29.7, margin:2, padding:0}; + function getSystemDefaultStyleAttributes(styleFamily) { + var result, builtInDefaultStyleAttributes = builtInDefaultStyleAttributesByFamily[styleFamily]; + if(builtInDefaultStyleAttributes) { + result = utils.mergeObjects({}, builtInDefaultStyleAttributes) + }else { + result = {} } - node = stylesnode.firstChild; - while(node) { - if(node.namespaceURI === stylens && (node.localName === "style" || node.localName === "default-style")) { - family = node.getAttributeNS(stylens, "family") - }else { - if(node.namespaceURI === textns && node.localName === "list-style") { - family = "list" - }else { - if(node.namespaceURI === stylens && (node.localName === "page-layout" || node.localName === "default-page-layout")) { - family = "page" - }else { - family = undefined - } - } + return result + } + this.getSystemDefaultStyleAttributes = getSystemDefaultStyleAttributes; + this.setOdfContainer = function(odfcontainer) { + odfContainer = odfcontainer + }; + function getDirectChild(node, ns, name) { + var e = node && node.firstElementChild; + while(e) { + if(e.namespaceURI === ns && e.localName === name) { + break } - if(family) { - name = node.getAttributeNS && node.getAttributeNS(stylens, "name"); - if(!name) { - name = "" + e = e.nextElementSibling + } + return e + } + function getFontMap() { + var fontFaceDecls = odfContainer.rootElement.fontFaceDecls, fontFaceDeclsMap = {}, node, name, family; + node = fontFaceDecls && fontFaceDecls.firstElementChild; + while(node) { + name = node.getAttributeNS(stylens, "name"); + if(name) { + family = node.getAttributeNS(svgns, "font-family"); + if(family || node.getElementsByTagNameNS(svgns, "font-face-uri").length > 0) { + fontFaceDeclsMap[name] = family } - style = stylemap[family] = stylemap[family] || {}; - style[name] = node } - node = node.nextSibling + node = node.nextElementSibling } - return stylemap + return fontFaceDeclsMap } - function findStyle(stylestree, name) { - if(!name || !stylestree) { - return null + this.getFontMap = getFontMap; + this.getAvailableParagraphStyles = function() { + var node = odfContainer.rootElement.styles, p_family, p_name, p_displayName, paragraphStyles = []; + node = node && node.firstElementChild; + while(node) { + if(node.localName === "style" && node.namespaceURI === stylens) { + p_family = node.getAttributeNS(stylens, "family"); + if(p_family === "paragraph") { + p_name = node.getAttributeNS(stylens, "name"); + p_displayName = node.getAttributeNS(stylens, "display-name") || p_name; + if(p_name && p_displayName) { + paragraphStyles.push({name:p_name, displayName:p_displayName}) + } + } + } + node = node.nextElementSibling } - if(stylestree[name]) { - return stylestree[name] + return paragraphStyles + }; + this.isStyleUsed = function(styleElement) { + var hasDerivedStyles, isUsed, root = odfContainer.rootElement; + hasDerivedStyles = styleInfo.hasDerivedStyles(root, odf.Namespaces.lookupNamespaceURI, styleElement); + isUsed = (new styleInfo.UsedStyleList(root.styles)).uses(styleElement) || ((new styleInfo.UsedStyleList(root.automaticStyles)).uses(styleElement) || (new styleInfo.UsedStyleList(root.body)).uses(styleElement)); + return hasDerivedStyles || isUsed + }; + function getDefaultStyleElement(family) { + var node = odfContainer.rootElement.styles.firstElementChild; + while(node) { + if(node.namespaceURI === stylens && (node.localName === "default-style" && node.getAttributeNS(stylens, "family") === family)) { + return node + } + node = node.nextElementSibling } - var n, style; - for(n in stylestree) { - if(stylestree.hasOwnProperty(n)) { - style = findStyle(stylestree[n].derivedStyles, name); - if(style) { - return style + return null + } + this.getDefaultStyleElement = getDefaultStyleElement; + function getStyleElement(styleName, family, styleElements) { + var node, nodeStyleName, styleListElement, i; + styleElements = styleElements || [odfContainer.rootElement.automaticStyles, odfContainer.rootElement.styles]; + for(i = 0;i < styleElements.length;i += 1) { + styleListElement = (styleElements[i]); + node = styleListElement.firstElementChild; + while(node) { + nodeStyleName = node.getAttributeNS(stylens, "name"); + if(node.namespaceURI === stylens && (node.localName === "style" && (node.getAttributeNS(stylens, "family") === family && nodeStyleName === styleName))) { + return node + } + if(family === "list-style" && (node.namespaceURI === textns && (node.localName === "list-style" && nodeStyleName === styleName))) { + return node } + if(family === "data" && (node.namespaceURI === numberns && nodeStyleName === styleName)) { + return node + } + node = node.nextElementSibling } } return null } - function addStyleToStyleTree(stylename, stylesmap, stylestree) { - var style = stylesmap[stylename], parentname, parentstyle; - if(!style) { - return - } - parentname = style.getAttributeNS(stylens, "parent-style-name"); - parentstyle = null; - if(parentname) { - parentstyle = findStyle(stylestree, parentname); - if(!parentstyle && stylesmap[parentname]) { - addStyleToStyleTree(parentname, stylesmap, stylestree); - parentstyle = stylesmap[parentname]; - stylesmap[parentname] = null + this.getStyleElement = getStyleElement; + function getStyleAttributes(styleNode) { + var i, a, map, ai, propertiesMap = {}, propertiesNode = styleNode.firstElementChild; + while(propertiesNode) { + if(propertiesNode.namespaceURI === stylens) { + map = propertiesMap[propertiesNode.nodeName] = {}; + a = propertiesNode.attributes; + for(i = 0;i < a.length;i += 1) { + ai = (a.item(i)); + map[ai.name] = ai.value + } } + propertiesNode = propertiesNode.nextElementSibling } - if(parentstyle) { - if(!parentstyle.derivedStyles) { - parentstyle.derivedStyles = {} - } - parentstyle.derivedStyles[stylename] = style - }else { - stylestree[stylename] = style + a = styleNode.attributes; + for(i = 0;i < a.length;i += 1) { + ai = (a.item(i)); + propertiesMap[ai.name] = ai.value } + return propertiesMap } - function addStyleMapToStyleTree(stylesmap, stylestree) { - var name; - for(name in stylesmap) { - if(stylesmap.hasOwnProperty(name)) { - addStyleToStyleTree(name, stylesmap, stylestree); - stylesmap[name] = null + this.getStyleAttributes = getStyleAttributes; + function getInheritedStyleAttributes(styleNode, includeSystemDefault) { + var styleListElement = odfContainer.rootElement.styles, parentStyleName, propertiesMap, inheritedPropertiesMap = {}, styleFamily = styleNode.getAttributeNS(stylens, "family"), node = styleNode; + while(node) { + propertiesMap = getStyleAttributes(node); + inheritedPropertiesMap = utils.mergeObjects(propertiesMap, inheritedPropertiesMap); + parentStyleName = node.getAttributeNS(stylens, "parent-style-name"); + if(parentStyleName) { + node = getStyleElement(parentStyleName, styleFamily, [styleListElement]) + }else { + node = null } } - } - function createSelector(family, name) { - var prefix = familynamespaceprefixes[family], namepart, selector; - if(prefix === null) { - return null - } - if(name) { - namepart = "[" + prefix + '|style-name="' + name + '"]' - }else { - namepart = "" + node = getDefaultStyleElement(styleFamily); + if(node) { + propertiesMap = getStyleAttributes(node); + inheritedPropertiesMap = utils.mergeObjects(propertiesMap, inheritedPropertiesMap) } - if(prefix === "presentation") { - prefix = "draw"; - if(name) { - namepart = '[presentation|style-name="' + name + '"]' - }else { - namepart = "" + if(includeSystemDefault) { + propertiesMap = getSystemDefaultStyleAttributes(styleFamily); + if(propertiesMap) { + inheritedPropertiesMap = utils.mergeObjects(propertiesMap, inheritedPropertiesMap) } } - selector = prefix + "|" + familytagnames[family].join(namepart + "," + prefix + "|") + namepart; - return selector + return inheritedPropertiesMap } - function getSelectors(family, name, node) { - var selectors = [], n, ss, s; - selectors.push(createSelector(family, name)); - for(n in node.derivedStyles) { - if(node.derivedStyles.hasOwnProperty(n)) { - ss = getSelectors(family, n, node.derivedStyles[n]); - for(s in ss) { - if(ss.hasOwnProperty(s)) { - selectors.push(ss[s]) - } - } - } + this.getInheritedStyleAttributes = getInheritedStyleAttributes; + this.getFirstCommonParentStyleNameOrSelf = function(styleName) { + var automaticStyleElementList = odfContainer.rootElement.automaticStyles, styleElementList = odfContainer.rootElement.styles, styleElement; + styleElement = getStyleElement(styleName, "paragraph", [automaticStyleElementList]); + while(styleElement) { + styleName = styleElement.getAttributeNS(stylens, "parent-style-name"); + styleElement = getStyleElement(styleName, "paragraph", [automaticStyleElementList]) } - return selectors - } - function getDirectChild(node, ns, name) { - if(!node) { + styleElement = getStyleElement(styleName, "paragraph", [styleElementList]); + if(!styleElement) { return null } - var c = node.firstChild, e; - while(c) { - if(c.namespaceURI === ns && c.localName === name) { - e = (c); - return e + return styleName + }; + this.hasParagraphStyle = function(styleName) { + return Boolean(getStyleElement(styleName, "paragraph")) + }; + function buildStyleChain(node, collectedChains) { + var parent = (node.nodeType === Node.TEXT_NODE ? node.parentNode : node), nodeStyles, appliedStyles = [], chainKey = "", foundContainer = false; + while(parent) { + if(!foundContainer && odfUtils.isGroupingElement(parent)) { + foundContainer = true } - c = c.nextSibling + nodeStyles = styleInfo.determineStylesForNode(parent); + if(nodeStyles) { + appliedStyles.push(nodeStyles) + } + parent = parent.parentElement } - return null - } - function fixBorderWidth(value) { - var index = value.indexOf(" "), width, theRestOfBorderAttributes; - if(index !== -1) { - width = value.substring(0, index); - theRestOfBorderAttributes = value.substring(index) - }else { - width = value; - theRestOfBorderAttributes = "" + function chainStyles(usedStyleMap) { + Object.keys(usedStyleMap).forEach(function(styleFamily) { + Object.keys(usedStyleMap[styleFamily]).forEach(function(styleName) { + chainKey += "|" + styleFamily + ":" + styleName + "|" + }) + }) } - width = utils.parseLength(width); - if(width && (width.unit === "pt" && width.value < 0.75)) { - value = "0.75pt" + theRestOfBorderAttributes + if(foundContainer) { + appliedStyles.forEach(chainStyles); + if(collectedChains) { + collectedChains[chainKey] = appliedStyles + } } - return value + return foundContainer ? appliedStyles : undefined } - function applySimpleMapping(props, mapping) { - var rule = "", r, value; - for(r in mapping) { - if(mapping.hasOwnProperty(r)) { - r = mapping[r]; - value = props.getAttributeNS(r[0], r[1]); - if(value) { - value = value.trim(); - if(borderPropertyMap.hasOwnProperty(r[1])) { - value = fixBorderWidth(value) - } - if(r[2]) { - rule += r[2] + ":" + value + ";" - } + function calculateAppliedStyle(styleChain) { + var mergedChildStyle = {orderedStyles:[]}; + styleChain.forEach(function(elementStyleSet) { + Object.keys((elementStyleSet)).forEach(function(styleFamily) { + var styleName = Object.keys(elementStyleSet[styleFamily])[0], styleElement, parentStyle, displayName; + styleElement = getStyleElement(styleName, styleFamily); + if(styleElement) { + parentStyle = getInheritedStyleAttributes((styleElement)); + mergedChildStyle = utils.mergeObjects(parentStyle, mergedChildStyle); + displayName = styleElement.getAttributeNS(stylens, "display-name") + }else { + runtime.log("No style element found for '" + styleName + "' of family '" + styleFamily + "'") } - } - } - return rule + mergedChildStyle.orderedStyles.push({name:styleName, family:styleFamily, displayName:displayName}) + }) + }); + return mergedChildStyle } - function getFontSize(styleNode) { - var props = getDirectChild((styleNode), stylens, "text-properties"); - if(props) { - return utils.parseFoFontSize(props.getAttributeNS(fons, "font-size")) + this.getAppliedStyles = function(textNodes) { + var styleChains = {}, styles = []; + textNodes.forEach(function(n) { + buildStyleChain(n, styleChains) + }); + Object.keys(styleChains).forEach(function(key) { + styles.push(calculateAppliedStyle(styleChains[key])) + }); + return styles + }; + this.getAppliedStylesForElement = function(node) { + var styleChain; + styleChain = buildStyleChain(node); + return styleChain ? calculateAppliedStyle(styleChain) : undefined + }; + this.applyStyle = function(memberId, textNodes, limits, info) { + var textStyles = new odf.TextStyleApplicator(new odf.ObjectNameGenerator((odfContainer), memberId), self, odfContainer.rootElement.automaticStyles); + textStyles.applyStyle(textNodes, limits, info) + }; + this.updateStyle = function(styleNode, properties) { + var fontName, fontFaceNode; + domUtils.mapObjOntoNode(styleNode, properties, odf.Namespaces.lookupNamespaceURI); + fontName = properties["style:text-properties"] && properties["style:text-properties"]["style:font-name"]; + if(fontName && !getFontMap().hasOwnProperty(fontName)) { + fontFaceNode = styleNode.ownerDocument.createElementNS(stylens, "style:font-face"); + fontFaceNode.setAttributeNS(stylens, "style:name", fontName); + fontFaceNode.setAttributeNS(svgns, "svg:font-family", fontName); + odfContainer.rootElement.fontFaceDecls.appendChild(fontFaceNode) } - return null + }; + function isAutomaticStyleElement(styleNode) { + return styleNode.parentNode === odfContainer.rootElement.automaticStyles } - function getParentStyleNode(styleNode) { - var parentStyleName = "", parentStyleFamily = "", parentStyleNode = null, xp; - if(styleNode.localName === "default-style") { - return null - } - parentStyleName = styleNode.getAttributeNS(stylens, "parent-style-name"); - parentStyleFamily = styleNode.getAttributeNS(stylens, "family"); - if(parentStyleName) { - xp = "//style:*[@style:name='" + parentStyleName + "'][@style:family='" + parentStyleFamily + "']" + this.createDerivedStyleObject = function(parentStyleName, family, overrides) { + var originalStyleElement = (getStyleElement(parentStyleName, family)), newStyleObject; + runtime.assert(Boolean(originalStyleElement), "No style element found for '" + parentStyleName + "' of family '" + family + "'"); + if(isAutomaticStyleElement(originalStyleElement)) { + newStyleObject = getStyleAttributes(originalStyleElement) }else { - xp = "//style:default-style[@style:family='" + parentStyleFamily + "']" - } - parentStyleNode = xpath.getODFElementsWithXPath((odfRoot), xp, odf.Namespaces.resolvePrefix)[0]; - return parentStyleNode - } - function getTextProperties(props) { - var rule = "", fontName, fontSize, value, textDecoration = "", fontSizeRule = "", sizeMultiplier = 1, parentStyle; - rule += applySimpleMapping(props, textPropertySimpleMapping); - value = props.getAttributeNS(stylens, "text-underline-style"); - if(value === "solid") { - textDecoration += " underline" - } - value = props.getAttributeNS(stylens, "text-line-through-style"); - if(value === "solid") { - textDecoration += " line-through" - } - if(textDecoration.length) { - textDecoration = "text-decoration:" + textDecoration + ";"; - rule += textDecoration + newStyleObject = {"style:parent-style-name":parentStyleName} } - fontName = props.getAttributeNS(stylens, "font-name") || props.getAttributeNS(fons, "font-family"); - if(fontName) { - value = fontFaceDeclsMap[fontName]; - rule += "font-family: " + (value || fontName) + ";" + newStyleObject["style:family"] = family; + utils.mergeObjects(newStyleObject, overrides); + return newStyleObject + }; + this.getDefaultTabStopDistance = function() { + var defaultParagraph = getDefaultStyleElement("paragraph"), paragraphProperties = defaultParagraph && defaultParagraph.firstElementChild, tabStopDistance; + while(paragraphProperties) { + if(paragraphProperties.namespaceURI === stylens && paragraphProperties.localName === "paragraph-properties") { + tabStopDistance = paragraphProperties.getAttributeNS(stylens, "tab-stop-distance") + } + paragraphProperties = paragraphProperties.nextElementSibling } - parentStyle = props.parentNode; - fontSize = getFontSize((parentStyle)); - if(!fontSize) { - return rule + if(!tabStopDistance) { + tabStopDistance = "1.25cm" } - while(parentStyle) { - fontSize = getFontSize((parentStyle)); - if(fontSize) { - if(fontSize.unit !== "%") { - fontSizeRule = "font-size: " + fontSize.value * sizeMultiplier + fontSize.unit + ";"; + return odfUtils.parseNonNegativeLength(tabStopDistance) + }; + function getPageLayoutStyleElement(styleName, styleFamily) { + var masterPageName, layoutName, pageLayoutElements, node, i, styleElement = getStyleElement(styleName, styleFamily); + runtime.assert(styleFamily === "paragraph" || styleFamily === "table", "styleFamily has to be either paragraph or table"); + if(styleElement) { + masterPageName = styleElement.getAttributeNS(stylens, "master-page-name") || "Standard"; + node = odfContainer.rootElement.masterStyles.lastElementChild; + while(node) { + if(node.getAttributeNS(stylens, "name") === masterPageName) { break } - sizeMultiplier *= fontSize.value / 100 + node = node.previousElementSibling + } + layoutName = node.getAttributeNS(stylens, "page-layout-name"); + pageLayoutElements = domUtils.getElementsByTagNameNS(odfContainer.rootElement.automaticStyles, stylens, "page-layout"); + for(i = 0;i < pageLayoutElements.length;i += 1) { + node = pageLayoutElements[i]; + if(node.getAttributeNS(stylens, "name") === layoutName) { + return(node) + } } - parentStyle = getParentStyleNode(parentStyle) - } - if(!fontSizeRule) { - fontSizeRule = "font-size: " + parseFloat(defaultFontSize) * sizeMultiplier + cssUnits.getUnits(defaultFontSize) + ";" } - rule += fontSizeRule; - return rule + return null } - function getParagraphProperties(props) { - var rule = "", imageProps, url, element, lineHeight; - rule += applySimpleMapping(props, paragraphPropertySimpleMapping); - imageProps = props.getElementsByTagNameNS(stylens, "background-image"); - if(imageProps.length > 0) { - url = imageProps.item(0).getAttributeNS(xlinkns, "href"); - if(url) { - rule += "background-image: url('odfkit:" + url + "');"; - element = (imageProps.item(0)); - rule += applySimpleMapping(element, bgImageSimpleMapping) + function lengthInCm(length, defaultValue) { + var result = odfUtils.parseLength(length), value = defaultValue; + if(result) { + switch(result.unit) { + case "cm": + value = result.value; + break; + case "mm": + value = result.value * 0.1; + break; + case "in": + value = result.value * 2.54; + break; + case "pt": + value = result.value * 0.035277778; + break; + case "pc": + ; + case "px": + ; + case "em": + break; + default: + runtime.log("Unit identifier: " + result.unit + " is not supported."); + break } } - lineHeight = props.getAttributeNS(fons, "line-height"); - if(lineHeight && lineHeight !== "normal") { - lineHeight = utils.parseFoLineHeight(lineHeight); - if(lineHeight.unit !== "%") { - rule += "line-height: " + lineHeight.value + lineHeight.unit + ";" + return value + } + this.getContentSize = function(styleName, styleFamily) { + var pageLayoutElement, props, printOrientation, defaultOrientedPageWidth, defaultOrientedPageHeight, pageWidth, pageHeight, margin, marginLeft, marginRight, marginTop, marginBottom, padding, paddingLeft, paddingRight, paddingTop, paddingBottom; + pageLayoutElement = getPageLayoutStyleElement(styleName, styleFamily); + if(!pageLayoutElement) { + pageLayoutElement = getDirectChild(odfContainer.rootElement.styles, stylens, "default-page-layout") + } + props = getDirectChild(pageLayoutElement, stylens, "page-layout-properties"); + if(props) { + printOrientation = props.getAttributeNS(stylens, "print-orientation") || "portrait"; + if(printOrientation === "portrait") { + defaultOrientedPageWidth = defaultPageFormatSettings.width; + defaultOrientedPageHeight = defaultPageFormatSettings.height }else { - rule += "line-height: " + lineHeight.value / 100 + ";" + defaultOrientedPageWidth = defaultPageFormatSettings.height; + defaultOrientedPageHeight = defaultPageFormatSettings.width } - } - return rule - } - function hexToRgb(hex) { - var result, shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shorthandRegex, function(m, r, g, b) { - return r + r + g + g + b + b - }); - result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? {r:parseInt(result[1], 16), g:parseInt(result[2], 16), b:parseInt(result[3], 16)} : null - } - function isNumber(n) { - return!isNaN(parseFloat(n)) - } - function getGraphicProperties(props) { - var rule = "", alpha, bgcolor, fill; - rule += applySimpleMapping(props, graphicPropertySimpleMapping); - alpha = props.getAttributeNS(drawns, "opacity"); - fill = props.getAttributeNS(drawns, "fill"); - bgcolor = props.getAttributeNS(drawns, "fill-color"); - if(fill === "solid" || fill === "hatch") { - if(bgcolor && bgcolor !== "none") { - alpha = isNumber(alpha) ? parseFloat(alpha) / 100 : 1; - bgcolor = hexToRgb(bgcolor); - if(bgcolor) { - rule += "background-color: rgba(" + bgcolor.r + "," + bgcolor.g + "," + bgcolor.b + "," + alpha + ");" - } + pageWidth = lengthInCm(props.getAttributeNS(fons, "page-width"), defaultOrientedPageWidth); + pageHeight = lengthInCm(props.getAttributeNS(fons, "page-height"), defaultOrientedPageHeight); + margin = lengthInCm(props.getAttributeNS(fons, "margin"), null); + if(margin === null) { + marginLeft = lengthInCm(props.getAttributeNS(fons, "margin-left"), defaultPageFormatSettings.margin); + marginRight = lengthInCm(props.getAttributeNS(fons, "margin-right"), defaultPageFormatSettings.margin); + marginTop = lengthInCm(props.getAttributeNS(fons, "margin-top"), defaultPageFormatSettings.margin); + marginBottom = lengthInCm(props.getAttributeNS(fons, "margin-bottom"), defaultPageFormatSettings.margin) }else { - rule += "background: none;" + marginLeft = marginRight = marginTop = marginBottom = margin } - }else { - if(fill === "none") { - rule += "background: none;" + padding = lengthInCm(props.getAttributeNS(fons, "padding"), null); + if(padding === null) { + paddingLeft = lengthInCm(props.getAttributeNS(fons, "padding-left"), defaultPageFormatSettings.padding); + paddingRight = lengthInCm(props.getAttributeNS(fons, "padding-right"), defaultPageFormatSettings.padding); + paddingTop = lengthInCm(props.getAttributeNS(fons, "padding-top"), defaultPageFormatSettings.padding); + paddingBottom = lengthInCm(props.getAttributeNS(fons, "padding-bottom"), defaultPageFormatSettings.padding) + }else { + paddingLeft = paddingRight = paddingTop = paddingBottom = padding } } - return rule + return{width:pageWidth - marginLeft - marginRight - paddingLeft - paddingRight, height:pageHeight - marginTop - marginBottom - paddingTop - paddingBottom} } - function getDrawingPageProperties(props) { - var rule = ""; - rule += applySimpleMapping(props, graphicPropertySimpleMapping); - if(props.getAttributeNS(presentationns, "background-visible") === "true") { - rule += "background: none;" +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("core.DomUtils"); +runtime.loadClass("odf.OdfContainer"); +runtime.loadClass("odf.Formatting"); +runtime.loadClass("xmldom.XPath"); +runtime.loadClass("odf.FontLoader"); +runtime.loadClass("odf.Style2CSS"); +runtime.loadClass("odf.OdfUtils"); +runtime.loadClass("gui.AnnotationViewManager"); +(function() { + function LoadingQueue() { + var queue = [], taskRunning = false; + function run(task) { + taskRunning = true; + runtime.setTimeout(function() { + try { + task() + }catch(e) { + runtime.log(String(e)) + } + taskRunning = false; + if(queue.length > 0) { + run(queue.pop()) + } + }, 10) } - return rule - } - function getTableCellProperties(props) { - var rule = ""; - rule += applySimpleMapping(props, tablecellPropertySimpleMapping); - return rule - } - function getTableRowProperties(props) { - var rule = ""; - rule += applySimpleMapping(props, tablerowPropertySimpleMapping); - return rule - } - function getTableColumnProperties(props) { - var rule = ""; - rule += applySimpleMapping(props, tablecolumnPropertySimpleMapping); - return rule - } - function getTableProperties(props) { - var rule = "", borderModel; - rule += applySimpleMapping(props, tablePropertySimpleMapping); - borderModel = props.getAttributeNS(tablens, "border-model"); - if(borderModel === "collapsing") { - rule += "border-collapse:collapse;" - }else { - if(borderModel === "separating") { - rule += "border-collapse:separate;" + this.clearQueue = function() { + queue.length = 0 + }; + this.addToQueue = function(loadingTask) { + if(queue.length === 0 && !taskRunning) { + return run(loadingTask) } + queue.push(loadingTask) } - return rule } - function addStyleRule(sheet, family, name, node) { - var selectors = getSelectors(family, name, node), selector = selectors.join(","), rule = "", properties = getDirectChild(node, stylens, "text-properties"); - if(properties) { - rule += getTextProperties(properties) - } - properties = getDirectChild(node, stylens, "paragraph-properties"); - if(properties) { - rule += getParagraphProperties(properties) - } - properties = getDirectChild(node, stylens, "graphic-properties"); - if(properties) { - rule += getGraphicProperties(properties) - } - properties = getDirectChild(node, stylens, "drawing-page-properties"); - if(properties) { - rule += getDrawingPageProperties(properties) - } - properties = getDirectChild(node, stylens, "table-cell-properties"); - if(properties) { - rule += getTableCellProperties(properties) - } - properties = getDirectChild(node, stylens, "table-row-properties"); - if(properties) { - rule += getTableRowProperties(properties) - } - properties = getDirectChild(node, stylens, "table-column-properties"); - if(properties) { - rule += getTableColumnProperties(properties) - } - properties = getDirectChild(node, stylens, "table-properties"); - if(properties) { - rule += getTableProperties(properties) - } - if(rule.length === 0) { - return + function PageSwitcher(css) { + var sheet = (css.sheet), position = 1; + function updateCSS() { + while(sheet.cssRules.length > 0) { + sheet.deleteRule(0) + } + sheet.insertRule("#shadowContent draw|page {display:none;}", 0); + sheet.insertRule("office|presentation draw|page {display:none;}", 1); + sheet.insertRule("#shadowContent draw|page:nth-of-type(" + position + ") {display:block;}", 2); + sheet.insertRule("office|presentation draw|page:nth-of-type(" + position + ") {display:block;}", 3) } - rule = selector + "{" + rule + "}"; - try { - sheet.insertRule(rule, sheet.cssRules.length) - }catch(e) { - throw e; + this.showFirstPage = function() { + position = 1; + updateCSS() + }; + this.showNextPage = function() { + position += 1; + updateCSS() + }; + this.showPreviousPage = function() { + if(position > 1) { + position -= 1; + updateCSS() + } + }; + this.showPage = function(n) { + if(n > 0) { + position = n; + updateCSS() + } + }; + this.css = css; + this.destroy = function(callback) { + css.parentNode.removeChild(css); + callback() } } - function getNumberRule(node) { - var style = node.getAttributeNS(stylens, "num-format"), suffix = node.getAttributeNS(stylens, "num-suffix"), prefix = node.getAttributeNS(stylens, "num-prefix"), stylemap = {1:"decimal", "a":"lower-latin", "A":"upper-latin", "i":"lower-roman", "I":"upper-roman"}, content = prefix || ""; - if(stylemap.hasOwnProperty(style)) { - content += " counter(list, " + stylemap[style] + ")" + function listenEvent(eventTarget, eventType, eventHandler) { + if(eventTarget.addEventListener) { + eventTarget.addEventListener(eventType, eventHandler, false) }else { - if(style) { - content += "'" + style + "';" + if(eventTarget.attachEvent) { + eventType = "on" + eventType; + eventTarget.attachEvent(eventType, eventHandler) }else { - content += " ''" + eventTarget["on" + eventType] = eventHandler } } - if(suffix) { - content += " '" + suffix + "'" - } - return"content: " + content + ";" } - function getImageRule() { - var rule = "content: none;"; - return rule + var drawns = odf.Namespaces.drawns, fons = odf.Namespaces.fons, officens = odf.Namespaces.officens, stylens = odf.Namespaces.stylens, svgns = odf.Namespaces.svgns, tablens = odf.Namespaces.tablens, textns = odf.Namespaces.textns, xlinkns = odf.Namespaces.xlinkns, xmlns = odf.Namespaces.xmlns, presentationns = odf.Namespaces.presentationns, webodfhelperns = "urn:webodf:names:helper", window = runtime.getWindow(), xpath = xmldom.XPath, odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils; + function clear(element) { + while(element.firstChild) { + element.removeChild(element.firstChild) + } } - function getBulletRule(node) { - var bulletChar = node.getAttributeNS(textns, "bullet-char"); - return"content: '" + bulletChar + "';" + function handleStyles(odfcontainer, formatting, stylesxmlcss) { + var style2css = new odf.Style2CSS; + style2css.style2css(odfcontainer.getDocumentType(), (stylesxmlcss.sheet), formatting.getFontMap(), odfcontainer.rootElement.styles, odfcontainer.rootElement.automaticStyles) } - function addListStyleRule(sheet, name, node, itemrule) { - var selector = 'text|list[text|style-name="' + name + '"]', level = node.getAttributeNS(textns, "level"), itemSelector, listItemRule, listLevelProps = utils.getFirstNonWhitespaceChild(node), listLevelLabelAlign = utils.getFirstNonWhitespaceChild(listLevelProps), labelAlignAttr, bulletIndent, listIndent, bulletWidth, rule; - if(listLevelLabelAlign) { - labelAlignAttr = listLevelLabelAlign.attributes; - bulletIndent = labelAlignAttr["fo:text-indent"] ? labelAlignAttr["fo:text-indent"].value : undefined; - listIndent = labelAlignAttr["fo:margin-left"] ? labelAlignAttr["fo:margin-left"].value : undefined - } - if(!bulletIndent) { - bulletIndent = "-0.6cm" - } - if(bulletIndent.charAt(0) === "-") { - bulletWidth = bulletIndent.substring(1) - }else { - bulletWidth = "-" + bulletIndent - } - level = level && parseInt(level, 10); - while(level > 1) { - selector += " > text|list-item > text|list"; - level -= 1 - } - itemSelector = selector; - itemSelector += " > text|list-item > *:not(text|list):first-child"; - if(listIndent !== undefined) { - listItemRule = itemSelector + "{margin-left:" + listIndent + ";}"; - sheet.insertRule(listItemRule, sheet.cssRules.length) + function handleFonts(odfContainer, fontcss) { + var fontLoader = new odf.FontLoader; + fontLoader.loadFonts(odfContainer, (fontcss.sheet)) + } + function getMasterPageElement(odfContainer, masterPageName) { + if(!masterPageName) { + return null } - selector += " > text|list-item > *:not(text|list):first-child:before"; - rule = itemrule; - rule = selector + "{" + rule + ";"; - rule += "counter-increment:list;"; - rule += "margin-left:" + bulletIndent + ";"; - rule += "width:" + bulletWidth + ";"; - rule += "display:inline-block}"; - try { - sheet.insertRule(rule, sheet.cssRules.length) - }catch(e) { - throw e; + var masterStyles = odfContainer.rootElement.masterStyles, masterPageElement = masterStyles.firstElementChild; + while(masterPageElement) { + if(masterPageElement.getAttributeNS(stylens, "name") === masterPageName && (masterPageElement.localName === "master-page" && masterPageElement.namespaceURI === stylens)) { + break + } } + return masterPageElement } - function addPageStyleRules(sheet, node) { - var rule = "", imageProps, url, element, contentLayoutRule = "", pageSizeRule = "", props = node.getElementsByTagNameNS(stylens, "page-layout-properties")[0], masterStyles = props.parentNode.parentNode.parentNode.masterStyles, masterPages, masterStyleName = "", i; - rule += applySimpleMapping(props, pageContentPropertySimpleMapping); - imageProps = props.getElementsByTagNameNS(stylens, "background-image"); - if(imageProps.length > 0) { - url = imageProps.item(0).getAttributeNS(xlinkns, "href"); - if(url) { - rule += "background-image: url('odfkit:" + url + "');"; - element = (imageProps.item(0)); - rule += applySimpleMapping(element, bgImageSimpleMapping) + function dropTemplateDrawFrames(clonedNode) { + var i, element, presentationClass, clonedDrawFrameElements = clonedNode.getElementsByTagNameNS(drawns, "frame"); + for(i = 0;i < clonedDrawFrameElements.length;i += 1) { + element = (clonedDrawFrameElements[i]); + presentationClass = element.getAttributeNS(presentationns, "class"); + if(presentationClass && !/^(date-time|footer|header|page-number')$/.test(presentationClass)) { + element.parentNode.removeChild(element) } } - if(documentType === "presentation") { - if(masterStyles) { - masterPages = masterStyles.getElementsByTagNameNS(stylens, "master-page"); - for(i = 0;i < masterPages.length;i += 1) { - if(masterPages[i].getAttributeNS(stylens, "page-layout-name") === props.parentNode.getAttributeNS(stylens, "name")) { - masterStyleName = masterPages[i].getAttributeNS(stylens, "name"); - contentLayoutRule = "draw|page[draw|master-page-name=" + masterStyleName + "] {" + rule + "}"; - pageSizeRule = "office|body, draw|page[draw|master-page-name=" + masterStyleName + "] {" + applySimpleMapping(props, pageSizePropertySimpleMapping) + " }"; - try { - sheet.insertRule(contentLayoutRule, sheet.cssRules.length); - sheet.insertRule(pageSizeRule, sheet.cssRules.length) - }catch(e1) { - throw e1; - } - } - } - } - }else { - if(documentType === "text") { - contentLayoutRule = "office|text {" + rule + "}"; - rule = ""; - pageSizeRule = "office|body {" + "width: " + props.getAttributeNS(fons, "page-width") + ";" + "}"; - try { - sheet.insertRule(contentLayoutRule, sheet.cssRules.length); - sheet.insertRule(pageSizeRule, sheet.cssRules.length) - }catch(e2) { - throw e2; + } + function getHeaderFooter(odfContainer, frame, headerFooterId) { + var headerFooter = null, i, declElements = odfContainer.rootElement.body.getElementsByTagNameNS(presentationns, headerFooterId + "-decl"), headerFooterName = frame.getAttributeNS(presentationns, "use-" + headerFooterId + "-name"), element; + if(headerFooterName && declElements.length > 0) { + for(i = 0;i < declElements.length;i += 1) { + element = (declElements[i]); + if(element.getAttributeNS(presentationns, "name") === headerFooterName) { + headerFooter = element.textContent; + break } } } + return headerFooter } - function addListStyleRules(sheet, name, node) { - var n = node.firstChild, e, itemrule; - while(n) { - if(n.namespaceURI === textns) { - e = (n); - if(n.localName === "list-level-style-number") { - itemrule = getNumberRule(e); - addListStyleRule(sheet, name, e, itemrule) - }else { - if(n.localName === "list-level-style-image") { - itemrule = getImageRule(); - addListStyleRule(sheet, name, e, itemrule) - }else { - if(n.localName === "list-level-style-bullet") { - itemrule = getBulletRule(e); - addListStyleRule(sheet, name, e, itemrule) - } - } - } + function setContainerValue(rootElement, ns, localName, value) { + var i, containerList, document = rootElement.ownerDocument, e; + containerList = rootElement.getElementsByTagNameNS(ns, localName); + for(i = 0;i < containerList.length;i += 1) { + clear(containerList[i]); + if(value) { + e = (containerList[i]); + e.appendChild(document.createTextNode(value)) } - n = n.nextSibling } } - function addRule(sheet, family, name, node) { - if(family === "list") { - addListStyleRules(sheet, name, node) + function setDrawElementPosition(styleid, frame, stylesheet) { + frame.setAttributeNS(webodfhelperns, "styleid", styleid); + var rule, anchor = frame.getAttributeNS(textns, "anchor-type"), x = frame.getAttributeNS(svgns, "x"), y = frame.getAttributeNS(svgns, "y"), width = frame.getAttributeNS(svgns, "width"), height = frame.getAttributeNS(svgns, "height"), minheight = frame.getAttributeNS(fons, "min-height"), minwidth = frame.getAttributeNS(fons, "min-width"); + if(anchor === "as-char") { + rule = "display: inline-block;" }else { - if(family === "page") { - addPageStyleRules(sheet, node) + if(anchor || (x || y)) { + rule = "position: absolute;" }else { - addStyleRule(sheet, family, name, node) + if(width || (height || (minheight || minwidth))) { + rule = "display: block;" + } } } + if(x) { + rule += "left: " + x + ";" + } + if(y) { + rule += "top: " + y + ";" + } + if(width) { + rule += "width: " + width + ";" + } + if(height) { + rule += "height: " + height + ";" + } + if(minheight) { + rule += "min-height: " + minheight + ";" + } + if(minwidth) { + rule += "min-width: " + minwidth + ";" + } + if(rule) { + rule = "draw|" + frame.localName + '[webodfhelper|styleid="' + styleid + '"] {' + rule + "}"; + stylesheet.insertRule(rule, stylesheet.cssRules.length) + } } - function addRules(sheet, family, name, node) { - addRule(sheet, family, name, node); - var n; - for(n in node.derivedStyles) { - if(node.derivedStyles.hasOwnProperty(n)) { - addRules(sheet, family, n, node.derivedStyles[n]) + function getUrlFromBinaryDataElement(image) { + var node = image.firstChild; + while(node) { + if(node.namespaceURI === officens && node.localName === "binary-data") { + return"data:image/png;base64," + node.textContent.replace(/[\r\n\s]/g, "") } + node = node.nextSibling } + return"" } - this.style2css = function(doctype, stylesheet, fontFaceMap, styles, autostyles) { - var doc, styletree, tree, name, rule, family, stylenodes, styleautonodes; - while(stylesheet.cssRules.length) { - stylesheet.deleteRule(stylesheet.cssRules.length - 1) - } - doc = null; - if(styles) { - doc = styles.ownerDocument; - odfRoot = styles.parentNode - } - if(autostyles) { - doc = autostyles.ownerDocument; - odfRoot = autostyles.parentNode + function setImage(id, container, image, stylesheet) { + image.setAttributeNS(webodfhelperns, "styleid", id); + var url = image.getAttributeNS(xlinkns, "href"), part; + function callback(url) { + var rule; + if(url) { + rule = "background-image: url(" + url + ");"; + rule = 'draw|image[webodfhelper|styleid="' + id + '"] {' + rule + "}"; + stylesheet.insertRule(rule, stylesheet.cssRules.length) + } } - if(!doc) { - return + function onchange(p) { + callback(p.url) } - odf.Namespaces.forEachPrefix(function(prefix, ns) { - rule = "@namespace " + prefix + " url(" + ns + ");"; + if(url) { try { - stylesheet.insertRule(rule, stylesheet.cssRules.length) - }catch(ignore) { + part = container.getPart(url); + part.onchange = onchange; + part.load() + }catch(e) { + runtime.log("slight problem: " + String(e)) } - }); - fontFaceDeclsMap = fontFaceMap; - documentType = doctype; - defaultFontSize = runtime.getWindow().getComputedStyle(document.body, null).getPropertyValue("font-size") || "12pt"; - stylenodes = getStyleMap(styles); - styleautonodes = getStyleMap(autostyles); - styletree = {}; - for(family in familynamespaceprefixes) { - if(familynamespaceprefixes.hasOwnProperty(family)) { - tree = styletree[family] = {}; - addStyleMapToStyleTree(stylenodes[family], tree); - addStyleMapToStyleTree(styleautonodes[family], tree); - for(name in tree) { - if(tree.hasOwnProperty(name)) { - addRules(stylesheet, family, name, tree[name]) - } - } + }else { + url = getUrlFromBinaryDataElement(image); + callback(url) + } + } + function formatParagraphAnchors(odfbody) { + var n, i, nodes = xpath.getODFElementsWithXPath(odfbody, ".//*[*[@text:anchor-type='paragraph']]", odf.Namespaces.lookupNamespaceURI); + for(i = 0;i < nodes.length;i += 1) { + n = nodes[i]; + if(n.setAttributeNS) { + n.setAttributeNS(webodfhelperns, "containsparagraphanchor", true) } } } -}; -/* - - Copyright (C) 2013 KO GmbH - - @licstart - This file is part of WebODF. - - WebODF 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. - - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend - - @source: http://www.webodf.org/ - @source: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("odf.Namespaces"); -runtime.loadClass("core.DomUtils"); -odf.MetadataManager = function MetadataManager(metaElement) { - var domUtils = new core.DomUtils, metadata = {}; - function setMetadata(setProperties, removedProperties) { - if(setProperties) { - Object.keys(setProperties).forEach(function(key) { - metadata[key] = setProperties[key] - }); - domUtils.mapKeyValObjOntoNode(metaElement, setProperties, odf.Namespaces.resolvePrefix) + function modifyTables(odffragment, documentns) { + var i, tableCells, node; + function modifyTableCell(node) { + if(node.hasAttributeNS(tablens, "number-columns-spanned")) { + node.setAttributeNS(documentns, "colspan", node.getAttributeNS(tablens, "number-columns-spanned")) + } + if(node.hasAttributeNS(tablens, "number-rows-spanned")) { + node.setAttributeNS(documentns, "rowspan", node.getAttributeNS(tablens, "number-rows-spanned")) + } } - if(removedProperties) { - removedProperties.forEach(function(name) { - delete metadata[name] - }); - domUtils.removeKeyElementsFromNode(metaElement, removedProperties, odf.Namespaces.resolvePrefix) + tableCells = odffragment.getElementsByTagNameNS(tablens, "table-cell"); + for(i = 0;i < tableCells.length;i += 1) { + node = (tableCells.item(i)); + modifyTableCell(node) } } - this.setMetadata = setMetadata; - this.incrementEditingCycles = function() { - var cycles = parseInt(metadata["meta:editing-cycles"] || 0, 10) + 1; - setMetadata({"meta:editing-cycles":cycles}, null) - }; - function init() { - metadata = domUtils.getKeyValRepresentationOfNode(metaElement, odf.Namespaces.lookupPrefix) - } - init() -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("core.Base64"); -runtime.loadClass("core.Zip"); -runtime.loadClass("xmldom.LSSerializer"); -runtime.loadClass("odf.StyleInfo"); -runtime.loadClass("odf.Namespaces"); -runtime.loadClass("odf.OdfNodeFilter"); -runtime.loadClass("odf.MetadataManager"); -odf.OdfContainer = function() { - var styleInfo = new odf.StyleInfo, metadataManager, officens = "urn:oasis:names:tc:opendocument:xmlns:office:1.0", manifestns = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0", webodfns = "urn:webodf:names:scope", stylens = odf.Namespaces.stylens, nodeorder = ["meta", "settings", "scripts", "font-face-decls", "styles", "automatic-styles", "master-styles", "body"], automaticStylePrefix = (new Date).getTime() + "_webodf_", base64 = new core.Base64, documentStylesScope = "document-styles", documentContentScope = - "document-content"; - function getDirectChild(node, ns, name) { - node = node ? node.firstChild : null; - while(node) { - if(node.localName === name && node.namespaceURI === ns) { - return(node) - } - node = node.nextSibling - } - return null - } - function getNodePosition(child) { - var i, l = nodeorder.length; - for(i = 0;i < l;i += 1) { - if(child.namespaceURI === officens && child.localName === nodeorder[i]) { - return i + function modifyLinks(odffragment) { + var i, links, node; + function modifyLink(node) { + var url, clickHandler; + if(!node.hasAttributeNS(xlinkns, "href")) { + return } - } - return-1 - } - function OdfStylesFilter(styleUsingElementsRoot, automaticStyles) { - var usedStyleList = new styleInfo.UsedStyleList(styleUsingElementsRoot, automaticStyles), odfNodeFilter = new odf.OdfNodeFilter; - this.acceptNode = function(node) { - var result = odfNodeFilter.acceptNode(node); - if(result === NodeFilter.FILTER_ACCEPT && (node.parentNode === automaticStyles && node.nodeType === Node.ELEMENT_NODE)) { - if(usedStyleList.uses((node))) { - result = NodeFilter.FILTER_ACCEPT - }else { - result = NodeFilter.FILTER_REJECT + url = node.getAttributeNS(xlinkns, "href"); + if(url[0] === "#") { + url = url.substring(1); + clickHandler = function() { + var bookmarks = xpath.getODFElementsWithXPath(odffragment, "//text:bookmark-start[@text:name='" + url + "']", odf.Namespaces.lookupNamespaceURI); + if(bookmarks.length === 0) { + bookmarks = xpath.getODFElementsWithXPath(odffragment, "//text:bookmark[@text:name='" + url + "']", odf.Namespaces.lookupNamespaceURI) + } + if(bookmarks.length > 0) { + bookmarks[0].scrollIntoView(true) + } + return false + } + }else { + clickHandler = function() { + window.open(url) } } - return result + node.onclick = clickHandler + } + links = odffragment.getElementsByTagNameNS(textns, "a"); + for(i = 0;i < links.length;i += 1) { + node = (links.item(i)); + modifyLink(node) } } - function OdfContentFilter(styleUsingElementsRoot, automaticStyles) { - var odfStylesFilter = new OdfStylesFilter(styleUsingElementsRoot, automaticStyles); - this.acceptNode = function(node) { - var result = odfStylesFilter.acceptNode(node); - if(result === NodeFilter.FILTER_ACCEPT && (node.parentNode && (node.parentNode.namespaceURI === odf.Namespaces.textns && (node.parentNode.localName === "s" || node.parentNode.localName === "tab")))) { - result = NodeFilter.FILTER_REJECT + function modifyLineBreakElements(odffragment) { + var document = odffragment.ownerDocument, lineBreakElements = domUtils.getElementsByTagNameNS(odffragment, textns, "line-break"); + lineBreakElements.forEach(function(lineBreak) { + if(!lineBreak.hasChildNodes()) { + lineBreak.appendChild(document.createElement("br")) } - return result - } + }) } - function setChild(node, child) { - if(!child) { - return - } - var childpos = getNodePosition(child), pos, c = node.firstChild; - if(childpos === -1) { - return - } - while(c) { - pos = getNodePosition(c); - if(pos !== -1 && pos > childpos) { - break + function expandSpaceElements(odffragment) { + var spaces, doc = odffragment.ownerDocument; + function expandSpaceElement(space) { + var j, count; + while(space.firstChild) { + space.removeChild(space.firstChild) + } + space.appendChild(doc.createTextNode(" ")); + count = parseInt(space.getAttributeNS(textns, "c"), 10); + if(count > 1) { + space.removeAttributeNS(textns, "c"); + for(j = 1;j < count;j += 1) { + space.parentNode.insertBefore(space.cloneNode(true), space) + } } - c = c.nextSibling } - node.insertBefore(child, c) - } - function ODFElement() { + spaces = domUtils.getElementsByTagNameNS(odffragment, textns, "s"); + spaces.forEach(expandSpaceElement) } - function ODFDocumentElement(odfcontainer) { - this.OdfContainer = odfcontainer + function expandTabElements(odffragment) { + var tabs; + tabs = domUtils.getElementsByTagNameNS(odffragment, textns, "tab"); + tabs.forEach(function(tab) { + tab.textContent = "\t" + }) } - ODFDocumentElement.prototype = new ODFElement; - ODFDocumentElement.prototype.constructor = ODFDocumentElement; - ODFDocumentElement.namespaceURI = officens; - ODFDocumentElement.localName = "document"; - function OdfPart(name, mimetype, container, zip) { - var self = this; - this.size = 0; - this.type = null; - this.name = name; - this.container = container; - this.url = null; - this.mimetype = null; - this.document = null; - this.onreadystatechange = null; - this.onchange = null; - this.EMPTY = 0; - this.LOADING = 1; - this.DONE = 2; - this.state = this.EMPTY; - this.load = function() { - if(zip === null) { - return + function modifyDrawElements(odfbody, stylesheet) { + var node, drawElements = [], i; + node = odfbody.firstElementChild; + while(node && node !== odfbody) { + if(node.namespaceURI === drawns) { + drawElements[drawElements.length] = node } - this.mimetype = mimetype; - zip.loadAsDataURL(name, mimetype, function(err, url) { - if(err) { - runtime.log(err) - } - self.url = url; - if(self.onchange) { - self.onchange(self) + if(node.firstElementChild) { + node = node.firstElementChild + }else { + while(node && (node !== odfbody && !node.nextElementSibling)) { + node = node.parentElement } - if(self.onstatereadychange) { - self.onstatereadychange(self) + if(node && node.nextElementSibling) { + node = node.nextElementSibling } - }) + } + } + for(i = 0;i < drawElements.length;i += 1) { + node = drawElements[i]; + setDrawElementPosition("frame" + String(i), node, stylesheet) } + formatParagraphAnchors(odfbody) } - OdfPart.prototype.load = function() { - }; - OdfPart.prototype.getUrl = function() { - if(this.data) { - return"data:;base64," + base64.toBase64(this.data) + function cloneMasterPages(odfContainer, shadowContent, odfbody, stylesheet) { + var masterPageName, masterPageElement, styleId, clonedPageElement, clonedElement, pageNumber = 0, i, element, elementToClone, document = odfContainer.rootElement.ownerDocument; + element = odfbody.firstElementChild; + if(!(element && (element.namespaceURI === officens && (element.localName === "presentation" || element.localName === "drawing")))) { + return } - return null - }; - odf.OdfContainer = function OdfContainer(url, onstatereadychange) { - var self = this, zip, partMimetypes = {}, contentElement; - this.onstatereadychange = onstatereadychange; - this.onchange = null; - this.state = null; - this.rootElement = null; - function removeProcessingInstructions(element) { - var n = element.firstChild, next, e; - while(n) { - next = n.nextSibling; - if(n.nodeType === Node.ELEMENT_NODE) { - e = (n); - removeProcessingInstructions(e) - }else { - if(n.nodeType === Node.PROCESSING_INSTRUCTION_NODE) { - element.removeChild(n) + element = element.firstElementChild; + while(element) { + masterPageName = element.getAttributeNS(drawns, "master-page-name"); + masterPageElement = getMasterPageElement(odfContainer, masterPageName); + if(masterPageElement) { + styleId = element.getAttributeNS(webodfhelperns, "styleid"); + clonedPageElement = document.createElementNS(drawns, "draw:page"); + elementToClone = masterPageElement.firstElementChild; + i = 0; + while(elementToClone) { + if(elementToClone.getAttributeNS(presentationns, "placeholder") !== "true") { + clonedElement = (elementToClone.cloneNode(true)); + clonedPageElement.appendChild(clonedElement); + setDrawElementPosition(styleId + "_" + i, clonedElement, stylesheet) } + elementToClone = elementToClone.nextElementSibling; + i += 1 } - n = next + dropTemplateDrawFrames(clonedPageElement); + shadowContent.appendChild(clonedPageElement); + pageNumber = String(shadowContent.getElementsByTagNameNS(drawns, "page").length); + setContainerValue(clonedPageElement, textns, "page-number", pageNumber); + setContainerValue(clonedPageElement, presentationns, "header", getHeaderFooter(odfContainer, (element), "header")); + setContainerValue(clonedPageElement, presentationns, "footer", getHeaderFooter(odfContainer, (element), "footer")); + setDrawElementPosition(styleId, clonedPageElement, stylesheet); + clonedPageElement.setAttributeNS(drawns, "draw:master-page-name", masterPageElement.getAttributeNS(stylens, "name")) } + element = element.nextElementSibling } - function setAutomaticStylesScope(stylesRootElement, scope) { - var n = stylesRootElement && stylesRootElement.firstChild; - while(n) { - if(n.nodeType === Node.ELEMENT_NODE) { - n.setAttributeNS(webodfns, "scope", scope) + } + function setVideo(container, plugin) { + var video, source, url, doc = plugin.ownerDocument, part; + url = plugin.getAttributeNS(xlinkns, "href"); + function callback(url, mimetype) { + var ns = doc.documentElement.namespaceURI; + if(mimetype.substr(0, 6) === "video/") { + video = doc.createElementNS(ns, "video"); + video.setAttribute("controls", "controls"); + source = doc.createElementNS(ns, "source"); + if(url) { + source.setAttribute("src", url) } - n = n.nextSibling + source.setAttribute("type", mimetype); + video.appendChild(source); + plugin.parentNode.appendChild(video) + }else { + plugin.innerHtml = "Unrecognised Plugin" } } - function mergeFontFaceDecls(targetFontFaceDeclsRootElement, sourceFontFaceDeclsRootElement) { - var n, s, fontFaceName, newFontFaceName, targetFontFaceDeclsMap, sourceFontFaceDeclsMap, fontFaceNameChangeMap = {}; - function unusedKey(key, map1, map2) { - var i = 0, postFixedKey; - key = key.replace(/\d+$/, ""); - postFixedKey = key; - while(map1.hasOwnProperty(postFixedKey) || map2.hasOwnProperty(postFixedKey)) { - i += 1; - postFixedKey = key + i - } - return postFixedKey + function onchange(p) { + callback(p.url, p.mimetype) + } + if(url) { + try { + part = container.getPart(url); + part.onchange = onchange; + part.load() + }catch(e) { + runtime.log("slight problem: " + String(e)) } - function mapByFontFaceName(fontFaceDecls) { - var fn, result = {}; - fn = fontFaceDecls.firstChild; - while(fn) { - if(fn.nodeType === Node.ELEMENT_NODE && (fn.namespaceURI === stylens && fn.localName === "font-face")) { - fontFaceName = fn.getAttributeNS(stylens, "name"); - result[fontFaceName] = fn - } - fn = fn.nextSibling - } - return result + }else { + runtime.log("using MP4 data fallback"); + url = getUrlFromBinaryDataElement(plugin); + callback(url, "video/mp4") + } + } + function getNumberRule(node) { + var style = node.getAttributeNS(stylens, "num-format"), suffix = node.getAttributeNS(stylens, "num-suffix") || "", prefix = node.getAttributeNS(stylens, "num-prefix") || "", rule = "", stylemap = {1:"decimal", "a":"lower-latin", "A":"upper-latin", "i":"lower-roman", "I":"upper-roman"}, content; + content = prefix; + if(stylemap.hasOwnProperty(style)) { + content += " counter(list, " + stylemap[style] + ")" + }else { + if(style) { + content += "'" + style + "';" + }else { + content += " ''" } - targetFontFaceDeclsMap = mapByFontFaceName(targetFontFaceDeclsRootElement); - sourceFontFaceDeclsMap = mapByFontFaceName(sourceFontFaceDeclsRootElement); - n = sourceFontFaceDeclsRootElement.firstChild; - while(n) { - s = n.nextSibling; - if(n.nodeType === Node.ELEMENT_NODE && (n.namespaceURI === stylens && n.localName === "font-face")) { - fontFaceName = n.getAttributeNS(stylens, "name"); - if(targetFontFaceDeclsMap.hasOwnProperty(fontFaceName)) { - if(!n.isEqualNode(targetFontFaceDeclsMap[fontFaceName])) { - newFontFaceName = unusedKey(fontFaceName, targetFontFaceDeclsMap, sourceFontFaceDeclsMap); - n.setAttributeNS(stylens, "style:name", newFontFaceName); - targetFontFaceDeclsRootElement.appendChild(n); - targetFontFaceDeclsMap[newFontFaceName] = (n); - delete sourceFontFaceDeclsMap[fontFaceName]; - fontFaceNameChangeMap[fontFaceName] = newFontFaceName - } - }else { - targetFontFaceDeclsRootElement.appendChild(n); - targetFontFaceDeclsMap[fontFaceName] = (n); - delete sourceFontFaceDeclsMap[fontFaceName] + } + if(suffix) { + content += " '" + suffix + "'" + } + rule = "content: " + content + ";"; + return rule + } + function getImageRule() { + var rule = "content: none;"; + return rule + } + function getBulletRule(node) { + var bulletChar = node.getAttributeNS(textns, "bullet-char"); + return"content: '" + bulletChar + "';" + } + function getBulletsRule(node) { + var itemrule; + if(node) { + if(node.localName === "list-level-style-number") { + itemrule = getNumberRule(node) + }else { + if(node.localName === "list-level-style-image") { + itemrule = getImageRule() + }else { + if(node.localName === "list-level-style-bullet") { + itemrule = getBulletRule(node) } } - n = s } - return fontFaceNameChangeMap } - function cloneStylesInScope(stylesRootElement, scope) { - var copy = null, n, s, scopeAttrValue; - if(stylesRootElement) { - copy = stylesRootElement.cloneNode(true); - n = copy.firstChild; - while(n) { - s = n.nextSibling; - if(n.nodeType === Node.ELEMENT_NODE) { - scopeAttrValue = n.getAttributeNS(webodfns, "scope"); - if(scopeAttrValue && scopeAttrValue !== scope) { - copy.removeChild(n) - } - } - n = s - } + return itemrule + } + function loadLists(odffragment, stylesheet, documentns) { + var i, lists, node, id, continueList, styleName, rule, listMap = {}, parentList, listStyles, listStyleMap = {}, bulletRule; + listStyles = window.document.getElementsByTagNameNS(textns, "list-style"); + for(i = 0;i < listStyles.length;i += 1) { + node = (listStyles.item(i)); + styleName = node.getAttributeNS(stylens, "name"); + if(styleName) { + listStyleMap[styleName] = node } - return copy } - function cloneFontFaceDeclsUsedInStyles(fontFaceDeclsRootElement, stylesRootElementList) { - var copy = null, n, nextSibling, fontFaceName, usedFontFaceDeclMap = {}; - if(fontFaceDeclsRootElement) { - stylesRootElementList.forEach(function(stylesRootElement) { - styleInfo.collectUsedFontFaces(usedFontFaceDeclMap, stylesRootElement) - }); - copy = fontFaceDeclsRootElement.cloneNode(true); - n = copy.firstChild; - while(n) { - nextSibling = n.nextSibling; - if(n.nodeType === Node.ELEMENT_NODE) { - fontFaceName = n.getAttributeNS(stylens, "name"); - if(!usedFontFaceDeclMap[fontFaceName]) { - copy.removeChild(n) - } + lists = odffragment.getElementsByTagNameNS(textns, "list"); + for(i = 0;i < lists.length;i += 1) { + node = (lists.item(i)); + id = node.getAttributeNS(xmlns, "id"); + if(id) { + continueList = node.getAttributeNS(textns, "continue-list"); + node.setAttributeNS(documentns, "id", id); + rule = "text|list#" + id + " > text|list-item > *:first-child:before {"; + styleName = node.getAttributeNS(textns, "style-name"); + if(styleName) { + node = listStyleMap[styleName]; + bulletRule = getBulletsRule((odfUtils.getFirstNonWhitespaceChild(node))) + } + if(continueList) { + parentList = listMap[continueList]; + while(parentList) { + parentList = listMap[parentList] + } + rule += "counter-increment:" + continueList + ";"; + if(bulletRule) { + bulletRule = bulletRule.replace("list", continueList); + rule += bulletRule + }else { + rule += "content:counter(" + continueList + ");" + } + }else { + continueList = ""; + if(bulletRule) { + bulletRule = bulletRule.replace("list", id); + rule += bulletRule + }else { + rule += "content: counter(" + id + ");" } - n = nextSibling + rule += "counter-increment:" + id + ";"; + stylesheet.insertRule("text|list#" + id + " {counter-reset:" + id + "}", stylesheet.cssRules.length) + } + rule += "}"; + listMap[id] = continueList; + if(rule) { + stylesheet.insertRule(rule, stylesheet.cssRules.length) } } - return copy - } - function initializeMetadataManager(metaRootElement) { - metadataManager = new odf.MetadataManager(metaRootElement) } - function importRootNode(xmldoc) { - var doc = self.rootElement.ownerDocument, node; - if(xmldoc) { - removeProcessingInstructions(xmldoc.documentElement); - try { - node = doc.importNode(xmldoc.documentElement, true) - }catch(ignore) { - } + } + function addWebODFStyleSheet(document) { + var head = (document.getElementsByTagName("head")[0]), style, href; + if(String(typeof webodf_css) !== "undefined") { + style = document.createElementNS(head.namespaceURI, "style"); + style.setAttribute("media", "screen, print, handheld, projection"); + style.appendChild(document.createTextNode(webodf_css)) + }else { + style = document.createElementNS(head.namespaceURI, "link"); + href = "webodf.css"; + if(runtime.currentDirectory) { + href = runtime.currentDirectory() + "/../" + href } - return node + style.setAttribute("href", href); + style.setAttribute("rel", "stylesheet") } - function setState(state) { - self.state = state; - if(self.onchange) { - self.onchange(self) + style.setAttribute("type", "text/css"); + head.appendChild(style); + return(style) + } + function addStyleSheet(document) { + var head = (document.getElementsByTagName("head")[0]), style = document.createElementNS(head.namespaceURI, "style"), text = ""; + style.setAttribute("type", "text/css"); + style.setAttribute("media", "screen, print, handheld, projection"); + odf.Namespaces.forEachPrefix(function(prefix, ns) { + text += "@namespace " + prefix + " url(" + ns + ");\n" + }); + text += "@namespace webodfhelper url(" + webodfhelperns + ");\n"; + style.appendChild(document.createTextNode(text)); + head.appendChild(style); + return(style) + } + odf.OdfCanvas = function OdfCanvas(element) { + runtime.assert(element !== null && element !== undefined, "odf.OdfCanvas constructor needs DOM element"); + runtime.assert(element.ownerDocument !== null && element.ownerDocument !== undefined, "odf.OdfCanvas constructor needs DOM"); + var self = this, doc = (element.ownerDocument), odfcontainer, formatting = new odf.Formatting, pageSwitcher, sizer = null, annotationsPane = null, allowAnnotations = false, annotationViewManager = null, webodfcss, fontcss, stylesxmlcss, positioncss, shadowContent, zoomLevel = 1, eventHandlers = {}, loadingQueue = new LoadingQueue; + function loadImages(container, odffragment, stylesheet) { + var i, images, node; + function loadImage(name, container, node, stylesheet) { + loadingQueue.addToQueue(function() { + setImage(name, container, node, stylesheet) + }) } - if(self.onstatereadychange) { - self.onstatereadychange(self) + images = odffragment.getElementsByTagNameNS(drawns, "image"); + for(i = 0;i < images.length;i += 1) { + node = (images.item(i)); + loadImage("image" + String(i), container, node, stylesheet) } } - function setRootElement(root) { - contentElement = null; - self.rootElement = root; - root.fontFaceDecls = getDirectChild(root, officens, "font-face-decls"); - root.styles = getDirectChild(root, officens, "styles"); - root.automaticStyles = getDirectChild(root, officens, "automatic-styles"); - root.masterStyles = getDirectChild(root, officens, "master-styles"); - root.body = getDirectChild(root, officens, "body"); - root.meta = getDirectChild(root, officens, "meta") + function loadVideos(container, odffragment) { + var i, plugins, node; + function loadVideo(container, node) { + loadingQueue.addToQueue(function() { + setVideo(container, node) + }) + } + plugins = odffragment.getElementsByTagNameNS(drawns, "plugin"); + for(i = 0;i < plugins.length;i += 1) { + node = (plugins.item(i)); + loadVideo(container, node) + } } - function handleFlatXml(xmldoc) { - var root = importRootNode(xmldoc); - if(!root || (root.localName !== "document" || root.namespaceURI !== officens)) { - setState(OdfContainer.INVALID); - return + function addEventListener(eventType, eventHandler) { + var handlers; + if(eventHandlers.hasOwnProperty(eventType)) { + handlers = eventHandlers[eventType] + }else { + handlers = eventHandlers[eventType] = [] + } + if(eventHandler && handlers.indexOf(eventHandler) === -1) { + handlers.push(eventHandler) } - setRootElement((root)); - setState(OdfContainer.DONE) } - function handleStylesXml(xmldoc) { - var node = importRootNode(xmldoc), root = self.rootElement; - if(!node || (node.localName !== "document-styles" || node.namespaceURI !== officens)) { - setState(OdfContainer.INVALID); + function fireEvent(eventType, args) { + if(!eventHandlers.hasOwnProperty(eventType)) { return } - root.fontFaceDecls = getDirectChild(node, officens, "font-face-decls"); - setChild(root, root.fontFaceDecls); - root.styles = getDirectChild(node, officens, "styles"); - setChild(root, root.styles); - root.automaticStyles = getDirectChild(node, officens, "automatic-styles"); - setAutomaticStylesScope(root.automaticStyles, documentStylesScope); - setChild(root, root.automaticStyles); - root.masterStyles = getDirectChild(node, officens, "master-styles"); - setChild(root, root.masterStyles); - styleInfo.prefixStyleNames(root.automaticStyles, automaticStylePrefix, root.masterStyles) + var handlers = eventHandlers[eventType], i; + for(i = 0;i < handlers.length;i += 1) { + handlers[i].apply(null, args) + } } - function handleContentXml(xmldoc) { - var node = importRootNode(xmldoc), root, automaticStyles, fontFaceDecls, fontFaceNameChangeMap, c; - if(!node || (node.localName !== "document-content" || node.namespaceURI !== officens)) { - setState(OdfContainer.INVALID); + function fixContainerSize() { + var odfdoc = sizer.firstChild; + if(!odfdoc) { return } - root = self.rootElement; - fontFaceDecls = getDirectChild(node, officens, "font-face-decls"); - if(root.fontFaceDecls && fontFaceDecls) { - fontFaceNameChangeMap = mergeFontFaceDecls(root.fontFaceDecls, fontFaceDecls) + if(zoomLevel > 1) { + sizer.style.MozTransformOrigin = "center top"; + sizer.style.WebkitTransformOrigin = "center top"; + sizer.style.OTransformOrigin = "center top"; + sizer.style.msTransformOrigin = "center top" }else { - if(fontFaceDecls) { - root.fontFaceDecls = fontFaceDecls; - setChild(root, fontFaceDecls) - } - } - automaticStyles = getDirectChild(node, officens, "automatic-styles"); - setAutomaticStylesScope(automaticStyles, documentContentScope); - if(fontFaceNameChangeMap) { - styleInfo.changeFontFaceNames(automaticStyles, fontFaceNameChangeMap) - } - if(root.automaticStyles && automaticStyles) { - c = automaticStyles.firstChild; - while(c) { - root.automaticStyles.appendChild(c); - c = automaticStyles.firstChild - } - }else { - if(automaticStyles) { - root.automaticStyles = automaticStyles; - setChild(root, automaticStyles) - } - } - root.body = getDirectChild(node, officens, "body"); - setChild(root, root.body) - } - function handleMetaXml(xmldoc) { - var node = importRootNode(xmldoc), root; - if(!node || (node.localName !== "document-meta" || node.namespaceURI !== officens)) { - return + sizer.style.MozTransformOrigin = "left top"; + sizer.style.WebkitTransformOrigin = "left top"; + sizer.style.OTransformOrigin = "left top"; + sizer.style.msTransformOrigin = "left top" } - root = self.rootElement; - root.meta = getDirectChild(node, officens, "meta"); - setChild(root, root.meta); - initializeMetadataManager(root.meta) + sizer.style.WebkitTransform = "scale(" + zoomLevel + ")"; + sizer.style.MozTransform = "scale(" + zoomLevel + ")"; + sizer.style.OTransform = "scale(" + zoomLevel + ")"; + sizer.style.msTransform = "scale(" + zoomLevel + ")"; + element.style.width = Math.round(zoomLevel * sizer.offsetWidth) + "px"; + element.style.height = Math.round(zoomLevel * sizer.offsetHeight) + "px" } - function handleSettingsXml(xmldoc) { - var node = importRootNode(xmldoc), root; - if(!node || (node.localName !== "document-settings" || node.namespaceURI !== officens)) { - return - } - root = self.rootElement; - root.settings = getDirectChild(node, officens, "settings"); - setChild(root, root.settings) + function handleContent(container, odfnode) { + var css = (positioncss.sheet); + clear(element); + sizer = (doc.createElementNS(element.namespaceURI, "div")); + sizer.style.display = "inline-block"; + sizer.style.background = "white"; + sizer.appendChild(odfnode); + element.appendChild(sizer); + annotationsPane = (doc.createElementNS(element.namespaceURI, "div")); + annotationsPane.id = "annotationsPane"; + shadowContent = doc.createElementNS(element.namespaceURI, "div"); + shadowContent.id = "shadowContent"; + shadowContent.style.position = "absolute"; + shadowContent.style.top = 0; + shadowContent.style.left = 0; + container.getContentElement().appendChild(shadowContent); + modifyDrawElements(odfnode.body, css); + cloneMasterPages(container, shadowContent, odfnode.body, css); + modifyTables(odfnode.body, element.namespaceURI); + modifyLinks(odfnode.body); + modifyLineBreakElements(odfnode.body); + expandSpaceElements(odfnode.body); + expandTabElements(odfnode.body); + loadImages(container, odfnode.body, css); + loadVideos(container, odfnode.body); + loadLists(odfnode.body, css, element.namespaceURI); + sizer.insertBefore(shadowContent, sizer.firstChild); + fixContainerSize() } - function handleManifestXml(xmldoc) { - var node = importRootNode(xmldoc), root, n; - if(!node || (node.localName !== "manifest" || node.namespaceURI !== manifestns)) { - return + function modifyAnnotations(odffragment) { + var annotationNodes = domUtils.getElementsByTagNameNS(odffragment, officens, "annotation"), annotationEnds = domUtils.getElementsByTagNameNS(odffragment, officens, "annotation-end"), currentAnnotationName, i; + function matchAnnotationEnd(element) { + return currentAnnotationName === element.getAttributeNS(officens, "name") } - root = self.rootElement; - root.manifest = node; - n = root.manifest.firstChild; - while(n) { - if(n.nodeType === Node.ELEMENT_NODE && (n.localName === "file-entry" && n.namespaceURI === manifestns)) { - partMimetypes[n.getAttributeNS(manifestns, "full-path")] = n.getAttributeNS(manifestns, "media-type") - } - n = n.nextSibling + for(i = 0;i < annotationNodes.length;i += 1) { + currentAnnotationName = annotationNodes[i].getAttributeNS(officens, "name"); + annotationViewManager.addAnnotation({node:annotationNodes[i], end:annotationEnds.filter(matchAnnotationEnd)[0] || null}) } + annotationViewManager.rerenderAnnotations() } - function loadNextComponent(remainingComponents) { - var component = remainingComponents.shift(), filepath, callback; - if(component) { - filepath = (component[0]); - callback = (component[1]); - zip.loadAsDOM(filepath, function(err, xmldoc) { - callback(xmldoc); - if(err || self.state === OdfContainer.INVALID) { - return - } - loadNextComponent(remainingComponents) - }) + function handleAnnotations(odfnode) { + if(allowAnnotations) { + if(!annotationsPane.parentNode) { + sizer.appendChild(annotationsPane); + fixContainerSize() + } + if(annotationViewManager) { + annotationViewManager.forgetAnnotations() + } + annotationViewManager = new gui.AnnotationViewManager(self, odfnode.body, annotationsPane); + modifyAnnotations(odfnode.body) }else { - setState(OdfContainer.DONE) - } - } - function loadComponents() { - var componentOrder = [["styles.xml", handleStylesXml], ["content.xml", handleContentXml], ["meta.xml", handleMetaXml], ["settings.xml", handleSettingsXml], ["META-INF/manifest.xml", handleManifestXml]]; - loadNextComponent(componentOrder) - } - function createDocumentElement(name) { - var s = ""; - odf.Namespaces.forEachPrefix(function(prefix, ns) { - s += " xmlns:" + prefix + '="' + ns + '"' - }); - return'' - } - function serializeMetaXml() { - var serializer = new xmldom.LSSerializer, s = createDocumentElement("document-meta"); - serializer.filter = new odf.OdfNodeFilter; - s += serializer.writeToString(self.rootElement.meta, odf.Namespaces.namespaceMap); - s += ""; - return s - } - function createManifestEntry(fullPath, mediaType) { - var element = document.createElementNS(manifestns, "manifest:file-entry"); - element.setAttributeNS(manifestns, "manifest:full-path", fullPath); - element.setAttributeNS(manifestns, "manifest:media-type", mediaType); - return element - } - function serializeManifestXml() { - var header = '\n', xml = '', manifest = (runtime.parseXML(xml)), manifestRoot = getDirectChild(manifest, manifestns, "manifest"), serializer = new xmldom.LSSerializer, fullPath; - for(fullPath in partMimetypes) { - if(partMimetypes.hasOwnProperty(fullPath)) { - manifestRoot.appendChild(createManifestEntry(fullPath, partMimetypes[fullPath])) + if(annotationsPane.parentNode) { + sizer.removeChild(annotationsPane); + annotationViewManager.forgetAnnotations(); + fixContainerSize() } } - serializer.filter = new odf.OdfNodeFilter; - return header + serializer.writeToString(manifest, odf.Namespaces.namespaceMap) - } - function serializeSettingsXml() { - var serializer = new xmldom.LSSerializer, s = createDocumentElement("document-settings"); - serializer.filter = new odf.OdfNodeFilter; - s += serializer.writeToString(self.rootElement.settings, odf.Namespaces.namespaceMap); - s += ""; - return s - } - function serializeStylesXml() { - var nsmap = odf.Namespaces.namespaceMap, serializer = new xmldom.LSSerializer, fontFaceDecls, automaticStyles, masterStyles, s = createDocumentElement("document-styles"); - automaticStyles = cloneStylesInScope(self.rootElement.automaticStyles, documentStylesScope); - masterStyles = self.rootElement.masterStyles && self.rootElement.masterStyles.cloneNode(true); - fontFaceDecls = cloneFontFaceDeclsUsedInStyles(self.rootElement.fontFaceDecls, [masterStyles, self.rootElement.styles, automaticStyles]); - styleInfo.removePrefixFromStyleNames(automaticStyles, automaticStylePrefix, masterStyles); - serializer.filter = new OdfStylesFilter(masterStyles, automaticStyles); - s += serializer.writeToString(fontFaceDecls, nsmap); - s += serializer.writeToString(self.rootElement.styles, nsmap); - s += serializer.writeToString(automaticStyles, nsmap); - s += serializer.writeToString(masterStyles, nsmap); - s += ""; - return s - } - function serializeContentXml() { - var nsmap = odf.Namespaces.namespaceMap, serializer = new xmldom.LSSerializer, fontFaceDecls, automaticStyles, s = createDocumentElement("document-content"); - automaticStyles = cloneStylesInScope(self.rootElement.automaticStyles, documentContentScope); - fontFaceDecls = cloneFontFaceDeclsUsedInStyles(self.rootElement.fontFaceDecls, [automaticStyles]); - serializer.filter = new OdfContentFilter(self.rootElement.body, automaticStyles); - s += serializer.writeToString(fontFaceDecls, nsmap); - s += serializer.writeToString(automaticStyles, nsmap); - s += serializer.writeToString(self.rootElement.body, nsmap); - s += ""; - return s } - function createElement(Type) { - var original = document.createElementNS(Type.namespaceURI, Type.localName), method, iface = new Type; - for(method in iface) { - if(iface.hasOwnProperty(method)) { - original[method] = iface[method] + function refreshOdf(suppressEvent) { + function callback() { + clear(element); + element.style.display = "inline-block"; + var odfnode = odfcontainer.rootElement; + element.ownerDocument.importNode(odfnode, true); + formatting.setOdfContainer(odfcontainer); + handleFonts(odfcontainer, fontcss); + handleStyles(odfcontainer, formatting, stylesxmlcss); + handleContent(odfcontainer, odfnode); + handleAnnotations(odfnode); + if(!suppressEvent) { + fireEvent("statereadychange", [odfcontainer]) } } - return original + if(odfcontainer.state === odf.OdfContainer.DONE) { + callback() + }else { + runtime.log("WARNING: refreshOdf called but ODF was not DONE."); + runtime.setTimeout(function later_cb() { + if(odfcontainer.state === odf.OdfContainer.DONE) { + callback() + }else { + runtime.log("will be back later..."); + runtime.setTimeout(later_cb, 500) + } + }, 100) + } } - function loadFromXML(url, callback) { - runtime.loadXML(url, function(err, dom) { - if(err) { - callback(err) - }else { - handleFlatXml(dom) - } + this.refreshCSS = function() { + handleStyles(odfcontainer, formatting, stylesxmlcss); + fixContainerSize() + }; + this.refreshSize = function() { + fixContainerSize() + }; + this.odfContainer = function() { + return odfcontainer + }; + this.setOdfContainer = function(container, suppressEvent) { + odfcontainer = container; + refreshOdf(suppressEvent === true) + }; + function load(url) { + loadingQueue.clearQueue(); + element.innerHTML = runtime.tr("Loading") + " " + url + "..."; + element.removeAttribute("style"); + odfcontainer = new odf.OdfContainer(url, function(container) { + odfcontainer = container; + refreshOdf(false) }) } - this.setRootElement = setRootElement; - this.getContentElement = function() { - var body; - if(!contentElement) { - body = self.rootElement.body; - contentElement = body.getElementsByTagNameNS(officens, "text")[0] || (body.getElementsByTagNameNS(officens, "presentation")[0] || body.getElementsByTagNameNS(officens, "spreadsheet")[0]) + this["load"] = load; + this.load = load; + this.save = function(callback) { + odfcontainer.save(callback) + }; + this.addListener = function(eventName, handler) { + switch(eventName) { + case "click": + listenEvent(element, eventName, handler); + break; + default: + addEventListener(eventName, handler); + break } - return contentElement }; - this.getDocumentType = function() { - var content = self.getContentElement(); - return content && content.localName + this.getFormatting = function() { + return formatting }; - this.getMetadataManager = function() { - return metadataManager + this.getAnnotationViewManager = function() { + return annotationViewManager }; - this.getPart = function(partname) { - return new OdfPart(partname, partMimetypes[partname], self, zip) + this.refreshAnnotations = function() { + handleAnnotations(odfcontainer.rootElement) }; - this.getPartData = function(url, callback) { - zip.load(url, callback) - }; - function updateMetadataForSaving() { - var generatorString, window = runtime.getWindow(); - generatorString = "WebODF/" + (String(typeof webodf_version) !== "undefined" ? webodf_version : "FromSource"); - if(window) { - generatorString = generatorString + " " + window.navigator.userAgent + this.rerenderAnnotations = function() { + if(annotationViewManager) { + annotationViewManager.rerenderAnnotations() } - metadataManager.setMetadata({"meta:generator":generatorString}) - } - function createEmptyTextDocument() { - var emptyzip = new core.Zip("", null), data = runtime.byteArrayFromString("application/vnd.oasis.opendocument.text", "utf8"), root = self.rootElement, text = document.createElementNS(officens, "text"); - emptyzip.save("mimetype", data, false, new Date); - function addToplevelElement(memberName, realLocalName) { - var element; - if(!realLocalName) { - realLocalName = memberName + }; + this.getSizer = function() { + return sizer + }; + this.enableAnnotations = function(allow) { + if(allow !== allowAnnotations) { + allowAnnotations = allow; + if(odfcontainer) { + handleAnnotations(odfcontainer.rootElement) } - element = document.createElementNS(officens, realLocalName); - root[memberName] = element; - root.appendChild(element) } - addToplevelElement("meta"); - addToplevelElement("settings"); - addToplevelElement("scripts"); - addToplevelElement("fontFaceDecls", "font-face-decls"); - addToplevelElement("styles"); - addToplevelElement("automaticStyles", "automatic-styles"); - addToplevelElement("masterStyles", "master-styles"); - addToplevelElement("body"); - root.body.appendChild(text); - initializeMetadataManager(root.meta); - setState(OdfContainer.DONE); - return emptyzip - } - function fillZip() { - var data, date = new Date; - updateMetadataForSaving(); - data = runtime.byteArrayFromString(serializeSettingsXml(), "utf8"); - zip.save("settings.xml", data, true, date); - data = runtime.byteArrayFromString(serializeMetaXml(), "utf8"); - zip.save("meta.xml", data, true, date); - data = runtime.byteArrayFromString(serializeStylesXml(), "utf8"); - zip.save("styles.xml", data, true, date); - data = runtime.byteArrayFromString(serializeContentXml(), "utf8"); - zip.save("content.xml", data, true, date); - data = runtime.byteArrayFromString(serializeManifestXml(), "utf8"); - zip.save("META-INF/manifest.xml", data, true, date) - } - function createByteArray(successCallback, errorCallback) { - fillZip(); - zip.createByteArray(successCallback, errorCallback) - } - this.createByteArray = createByteArray; - function saveAs(newurl, callback) { - fillZip(); - zip.writeAs(newurl, function(err) { - callback(err) - }) - } - this.saveAs = saveAs; - this.save = function(callback) { - saveAs(url, callback) }; - this.getUrl = function() { - return url + this.addAnnotation = function(annotation) { + if(annotationViewManager) { + annotationViewManager.addAnnotation(annotation) + } }; - this.setBlob = function(filename, mimetype, content) { - var data = base64.convertBase64ToByteArray(content), date = new Date; - zip.save(filename, data, false, date); - if(partMimetypes.hasOwnProperty(filename)) { - runtime.log(filename + " has been overwritten.") + this.forgetAnnotations = function() { + if(annotationViewManager) { + annotationViewManager.forgetAnnotations() } - partMimetypes[filename] = mimetype }; - this.removeBlob = function(filename) { - var foundAndRemoved = zip.remove(filename); - runtime.assert(foundAndRemoved, "file is not found: " + filename); - delete partMimetypes[filename] + this.setZoomLevel = function(zoom) { + zoomLevel = zoom; + fixContainerSize() }; - this.state = OdfContainer.LOADING; - this.rootElement = createElement(ODFDocumentElement); - if(url) { - zip = new core.Zip(url, function(err, zipobject) { - zip = zipobject; - if(err) { - loadFromXML(url, function(xmlerr) { - if(err) { - zip.error = err + "\n" + xmlerr; - setState(OdfContainer.INVALID) - } - }) - }else { - loadComponents() + this.getZoomLevel = function() { + return zoomLevel + }; + this.fitToContainingElement = function(width, height) { + var realWidth = element.offsetWidth / zoomLevel, realHeight = element.offsetHeight / zoomLevel; + zoomLevel = width / realWidth; + if(height / realHeight < zoomLevel) { + zoomLevel = height / realHeight + } + fixContainerSize() + }; + this.fitToWidth = function(width) { + var realWidth = element.offsetWidth / zoomLevel; + zoomLevel = width / realWidth; + fixContainerSize() + }; + this.fitSmart = function(width, height) { + var realWidth, realHeight, newScale; + realWidth = element.offsetWidth / zoomLevel; + realHeight = element.offsetHeight / zoomLevel; + newScale = width / realWidth; + if(height !== undefined) { + if(height / realHeight < newScale) { + newScale = height / realHeight } - }) - }else { - zip = createEmptyTextDocument() + } + zoomLevel = Math.min(1, newScale); + fixContainerSize() + }; + this.fitToHeight = function(height) { + var realHeight = element.offsetHeight / zoomLevel; + zoomLevel = height / realHeight; + fixContainerSize() + }; + this.showFirstPage = function() { + pageSwitcher.showFirstPage() + }; + this.showNextPage = function() { + pageSwitcher.showNextPage() + }; + this.showPreviousPage = function() { + pageSwitcher.showPreviousPage() + }; + this.showPage = function(n) { + pageSwitcher.showPage(n); + fixContainerSize() + }; + this.getElement = function() { + return element + }; + this.addCssForFrameWithImage = function(frame) { + var frameName = frame.getAttributeNS(drawns, "name"), fc = frame.firstElementChild; + setDrawElementPosition(frameName, frame, (positioncss.sheet)); + if(fc) { + setImage(frameName + "img", odfcontainer, fc, (positioncss.sheet)) + } + }; + this.destroy = function(callback) { + var head = (doc.getElementsByTagName("head")[0]); + if(annotationsPane && annotationsPane.parentNode) { + annotationsPane.parentNode.removeChild(annotationsPane) + } + if(sizer) { + element.removeChild(sizer); + sizer = null + } + head.removeChild(webodfcss); + head.removeChild(fontcss); + head.removeChild(stylesxmlcss); + head.removeChild(positioncss); + pageSwitcher.destroy(callback) + }; + function init() { + webodfcss = addWebODFStyleSheet(doc); + pageSwitcher = new PageSwitcher(addStyleSheet(doc)); + fontcss = addStyleSheet(doc); + stylesxmlcss = addStyleSheet(doc); + positioncss = addStyleSheet(doc) } - }; - odf.OdfContainer.EMPTY = 0; - odf.OdfContainer.LOADING = 1; - odf.OdfContainer.DONE = 2; - odf.OdfContainer.INVALID = 3; - odf.OdfContainer.SAVING = 4; - odf.OdfContainer.MODIFIED = 5; - odf.OdfContainer.getContainer = function(url) { - return new odf.OdfContainer(url, null) - }; - return odf.OdfContainer -}(); + init() + } +})(); /* Copyright (C) 2012-2013 KO GmbH @@ -8720,1578 +9041,1198 @@ odf.OdfContainer = function() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.Base64"); -runtime.loadClass("xmldom.XPath"); -runtime.loadClass("odf.OdfContainer"); -odf.FontLoader = function() { - var xpath = new xmldom.XPath, base64 = new core.Base64; - function getEmbeddedFontDeclarations(fontFaceDecls) { - var decls = {}, fonts, i, font, name, uris, href, family; - if(!fontFaceDecls) { - return decls - } - fonts = xpath.getODFElementsWithXPath(fontFaceDecls, "style:font-face[svg:font-face-src]", odf.Namespaces.resolvePrefix); - for(i = 0;i < fonts.length;i += 1) { - font = fonts[i]; - name = font.getAttributeNS(odf.Namespaces.stylens, "name"); - family = font.getAttributeNS(odf.Namespaces.svgns, "font-family"); - uris = xpath.getODFElementsWithXPath(font, "svg:font-face-src/svg:font-face-uri", odf.Namespaces.resolvePrefix); - if(uris.length > 0) { - href = uris[0].getAttributeNS(odf.Namespaces.xlinkns, "href"); - decls[name] = {href:href, family:family} +runtime.loadClass("core.DomUtils"); +runtime.loadClass("odf.Namespaces"); +runtime.loadClass("odf.OdfUtils"); +gui.StyleHelper = function StyleHelper(formatting) { + var domUtils = new core.DomUtils, odfUtils = new odf.OdfUtils, textns = odf.Namespaces.textns; + function getAppliedStyles(range) { + var container, nodes; + if(range.collapsed) { + container = range.startContainer; + if(container.hasChildNodes() && range.startOffset < container.childNodes.length) { + container = container.childNodes.item(range.startOffset) } + nodes = [container] + }else { + nodes = odfUtils.getTextNodes(range, true) } - return decls - } - function addFontToCSS(name, font, fontdata, stylesheet) { - var cssFamily = font.family || name, rule = "@font-face { font-family: '" + cssFamily + "'; src: " + "url(data:application/x-font-ttf;charset=binary;base64," + base64.convertUTF8ArrayToBase64(fontdata) + ') format("truetype"); }'; - try { - stylesheet.insertRule(rule, stylesheet.cssRules.length) - }catch(e) { - runtime.log("Problem inserting rule in CSS: " + runtime.toJson(e) + "\nRule: " + rule) - } + return formatting.getAppliedStyles(nodes) } - function loadFontIntoCSS(embeddedFontDeclarations, odfContainer, pos, stylesheet, callback) { - var name, i = 0, n; - for(n in embeddedFontDeclarations) { - if(embeddedFontDeclarations.hasOwnProperty(n)) { - if(i === pos) { - name = n; - break - } - i += 1 + this.getAppliedStyles = getAppliedStyles; + this.applyStyle = function(memberId, range, info) { + var nextTextNodes = domUtils.splitBoundaries(range), textNodes = odfUtils.getTextNodes(range, false), limits; + limits = {startContainer:range.startContainer, startOffset:range.startOffset, endContainer:range.endContainer, endOffset:range.endOffset}; + formatting.applyStyle(memberId, textNodes, limits, info); + nextTextNodes.forEach(domUtils.normalizeTextNodes) + }; + function hasTextPropertyValue(appliedStyles, propertyName, propertyValue) { + var hasOtherValue = true, properties, i; + for(i = 0;i < appliedStyles.length;i += 1) { + properties = appliedStyles[i]["style:text-properties"]; + hasOtherValue = !properties || properties[propertyName] !== propertyValue; + if(hasOtherValue) { + break } } - if(!name) { - if(callback) { - callback() + return!hasOtherValue + } + this.isBold = function(appliedStyles) { + return hasTextPropertyValue(appliedStyles, "fo:font-weight", "bold") + }; + this.isItalic = function(appliedStyles) { + return hasTextPropertyValue(appliedStyles, "fo:font-style", "italic") + }; + this.hasUnderline = function(appliedStyles) { + return hasTextPropertyValue(appliedStyles, "style:text-underline-style", "solid") + }; + this.hasStrikeThrough = function(appliedStyles) { + return hasTextPropertyValue(appliedStyles, "style:text-line-through-style", "solid") + }; + function hasParagraphPropertyValue(range, propertyName, propertyValues) { + var paragraphStyleName, paragraphStyleElement, paragraphStyleAttributes, properties, nodes = odfUtils.getParagraphElements(range), isStyleChecked = {}, isDefaultParagraphStyleChecked = false; + function pickDefaultParagraphStyleElement() { + isDefaultParagraphStyleChecked = true; + paragraphStyleElement = formatting.getDefaultStyleElement("paragraph"); + if(!paragraphStyleElement) { + paragraphStyleElement = null } - return } - odfContainer.getPartData(embeddedFontDeclarations[name].href, function(err, fontdata) { - if(err) { - runtime.log(err) + while(nodes.length > 0) { + paragraphStyleName = nodes[0].getAttributeNS(textns, "style-name"); + if(paragraphStyleName) { + if(!isStyleChecked[paragraphStyleName]) { + paragraphStyleElement = formatting.getStyleElement(paragraphStyleName, "paragraph"); + isStyleChecked[paragraphStyleName] = true; + if(!paragraphStyleElement && !isDefaultParagraphStyleChecked) { + pickDefaultParagraphStyleElement() + } + } }else { - if(!fontdata) { - runtime.log("missing font data for " + embeddedFontDeclarations[name].href) + if(!isDefaultParagraphStyleChecked) { + pickDefaultParagraphStyleElement() }else { - addFontToCSS(name, embeddedFontDeclarations[name], fontdata, stylesheet) + paragraphStyleElement = undefined } } - loadFontIntoCSS(embeddedFontDeclarations, odfContainer, pos + 1, stylesheet, callback) - }) - } - function loadFontsIntoCSS(embeddedFontDeclarations, odfContainer, stylesheet) { - loadFontIntoCSS(embeddedFontDeclarations, odfContainer, 0, stylesheet) - } - odf.FontLoader = function FontLoader() { - this.loadFonts = function(odfContainer, stylesheet) { - var embeddedFontDeclarations, fontFaceDecls = odfContainer.rootElement.fontFaceDecls; - while(stylesheet.cssRules.length) { - stylesheet.deleteRule(stylesheet.cssRules.length - 1) - } - if(fontFaceDecls) { - embeddedFontDeclarations = getEmbeddedFontDeclarations(fontFaceDecls); - loadFontsIntoCSS(embeddedFontDeclarations, odfContainer, stylesheet) - } - } - }; - return odf.FontLoader -}(); -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("core.DomUtils"); -runtime.loadClass("core.Utils"); -odf.ObjectNameGenerator = function ObjectNameGenerator(odfContainer, memberId) { - var stylens = odf.Namespaces.stylens, drawns = odf.Namespaces.drawns, xlinkns = odf.Namespaces.xlinkns, domUtils = new core.DomUtils, utils = new core.Utils, memberIdHash = utils.hashString(memberId), styleNameGenerator = null, frameNameGenerator = null, imageNameGenerator = null, existingFrameNames = {}, existingImageNames = {}; - function NameGenerator(prefix, findExistingNames) { - var reportedNames = {}; - this.generateName = function() { - var existingNames = findExistingNames(), startIndex = 0, name; - do { - name = prefix + startIndex; - startIndex += 1 - }while(reportedNames[name] || existingNames[name]); - reportedNames[name] = true; - return name - } - } - function getAllStyleNames() { - var styleElements = [odfContainer.rootElement.automaticStyles, odfContainer.rootElement.styles], node, styleNames = {}; - styleElements.forEach(function(styleListElement) { - node = styleListElement.firstChild; - while(node) { - if(node.nodeType === Node.ELEMENT_NODE && (node.namespaceURI === stylens && node.localName === "style")) { - styleNames[node.getAttributeNS(stylens, "name")] = true + if(paragraphStyleElement !== undefined) { + if(paragraphStyleElement === null) { + paragraphStyleAttributes = formatting.getSystemDefaultStyleAttributes("paragraph") + }else { + paragraphStyleAttributes = formatting.getInheritedStyleAttributes((paragraphStyleElement), true) + } + properties = paragraphStyleAttributes["style:paragraph-properties"]; + if(properties && propertyValues.indexOf(properties[propertyName]) === -1) { + return false } - node = node.nextSibling } - }); - return styleNames - } - this.generateStyleName = function() { - if(styleNameGenerator === null) { - styleNameGenerator = new NameGenerator("auto" + memberIdHash + "_", function() { - return getAllStyleNames() - }) + nodes.pop() } - return styleNameGenerator.generateName() + return true + } + this.isAlignedLeft = function(range) { + return hasParagraphPropertyValue(range, "fo:text-align", ["left", "start"]) }; - this.generateFrameName = function() { - if(frameNameGenerator === null) { - var nodes = domUtils.getElementsByTagNameNS(odfContainer.rootElement.body, drawns, "frame"); - nodes.forEach(function(frame) { - existingFrameNames[frame.getAttributeNS(drawns, "name")] = true - }); - frameNameGenerator = new NameGenerator("fr" + memberIdHash + "_", function() { - return existingFrameNames - }) - } - return frameNameGenerator.generateName() + this.isAlignedCenter = function(range) { + return hasParagraphPropertyValue(range, "fo:text-align", ["center"]) }; - this.generateImageName = function() { - if(imageNameGenerator === null) { - var nodes = domUtils.getElementsByTagNameNS(odfContainer.rootElement.body, drawns, "image"); - nodes.forEach(function(image) { - var path = image.getAttributeNS(xlinkns, "href"); - path = path.substring("Pictures/".length, path.lastIndexOf(".")); - existingImageNames[path] = true - }); - imageNameGenerator = new NameGenerator("img" + memberIdHash + "_", function() { - return existingImageNames - }) - } - return imageNameGenerator.generateName() + this.isAlignedRight = function(range) { + return hasParagraphPropertyValue(range, "fo:text-align", ["right", "end"]) + }; + this.isAlignedJustified = function(range) { + return hasParagraphPropertyValue(range, "fo:text-align", ["justify"]) } }; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("core.Utils"); -runtime.loadClass("odf.ObjectNameGenerator"); -runtime.loadClass("odf.Namespaces"); -runtime.loadClass("odf.OdfContainer"); -runtime.loadClass("odf.StyleInfo"); -runtime.loadClass("odf.OdfUtils"); -runtime.loadClass("odf.TextStyleApplicator"); -odf.Formatting = function Formatting() { - var self = this, odfContainer, styleInfo = new odf.StyleInfo, svgns = odf.Namespaces.svgns, stylens = odf.Namespaces.stylens, textns = odf.Namespaces.textns, numberns = odf.Namespaces.numberns, fons = odf.Namespaces.fons, odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils, utils = new core.Utils, builtInDefaultStyleAttributesByFamily = {"paragraph":{"style:paragraph-properties":{"fo:text-align":"left"}}}, defaultPageFormatSettings = {width:21.001, height:29.7, margin:2, padding:0}; - function getSystemDefaultStyleAttributes(styleFamily) { - var result, builtInDefaultStyleAttributes = builtInDefaultStyleAttributesByFamily[styleFamily]; - if(builtInDefaultStyleAttributes) { - result = utils.mergeObjects({}, builtInDefaultStyleAttributes) - }else { - result = null - } - return result +core.RawDeflate = function() { + var zip_WSIZE = 32768, zip_STORED_BLOCK = 0, zip_STATIC_TREES = 1, zip_DYN_TREES = 2, zip_DEFAULT_LEVEL = 6, zip_FULL_SEARCH = true, zip_INBUFSIZ = 32768, zip_INBUF_EXTRA = 64, zip_OUTBUFSIZ = 1024 * 8, zip_window_size = 2 * zip_WSIZE, zip_MIN_MATCH = 3, zip_MAX_MATCH = 258, zip_BITS = 16, zip_LIT_BUFSIZE = 8192, zip_HASH_BITS = 13, zip_DIST_BUFSIZE = zip_LIT_BUFSIZE, zip_HASH_SIZE = 1 << zip_HASH_BITS, zip_HASH_MASK = zip_HASH_SIZE - 1, zip_WMASK = zip_WSIZE - 1, zip_NIL = 0, zip_TOO_FAR = 4096, + zip_MIN_LOOKAHEAD = zip_MAX_MATCH + zip_MIN_MATCH + 1, zip_MAX_DIST = zip_WSIZE - zip_MIN_LOOKAHEAD, zip_SMALLEST = 1, zip_MAX_BITS = 15, zip_MAX_BL_BITS = 7, zip_LENGTH_CODES = 29, zip_LITERALS = 256, zip_END_BLOCK = 256, zip_L_CODES = zip_LITERALS + 1 + zip_LENGTH_CODES, zip_D_CODES = 30, zip_BL_CODES = 19, zip_REP_3_6 = 16, zip_REPZ_3_10 = 17, zip_REPZ_11_138 = 18, zip_HEAP_SIZE = 2 * zip_L_CODES + 1, zip_H_SHIFT = parseInt((zip_HASH_BITS + zip_MIN_MATCH - 1) / zip_MIN_MATCH, 10), zip_free_queue, + zip_qhead, zip_qtail, zip_initflag, zip_outbuf = null, zip_outcnt, zip_outoff, zip_complete, zip_window, zip_d_buf, zip_l_buf, zip_prev, zip_bi_buf, zip_bi_valid, zip_block_start, zip_ins_h, zip_hash_head, zip_prev_match, zip_match_available, zip_match_length, zip_prev_length, zip_strstart, zip_match_start, zip_eofile, zip_lookahead, zip_max_chain_length, zip_max_lazy_match, zip_compr_level, zip_good_match, zip_nice_match, zip_dyn_ltree, zip_dyn_dtree, zip_static_ltree, zip_static_dtree, zip_bl_tree, + zip_l_desc, zip_d_desc, zip_bl_desc, zip_bl_count, zip_heap, zip_heap_len, zip_heap_max, zip_depth, zip_length_code, zip_dist_code, zip_base_length, zip_base_dist, zip_flag_buf, zip_last_lit, zip_last_dist, zip_last_flags, zip_flags, zip_flag_bit, zip_opt_len, zip_static_len, zip_deflate_data, zip_deflate_pos, zip_extra_lbits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0], zip_extra_dbits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, + 9, 10, 10, 11, 11, 12, 12, 13, 13], zip_extra_blbits = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7], zip_bl_order = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15], zip_configuration_table; + if(zip_LIT_BUFSIZE > zip_INBUFSIZ) { + runtime.log("error: zip_INBUFSIZ is too small") } - this.getSystemDefaultStyleAttributes = getSystemDefaultStyleAttributes; - this.setOdfContainer = function(odfcontainer) { - odfContainer = odfcontainer - }; - function getFontMap() { - var fontFaceDecls = odfContainer.rootElement.fontFaceDecls, fontFaceDeclsMap = {}, node, name, family; - node = fontFaceDecls && fontFaceDecls.firstChild; - while(node) { - if(node.nodeType === Node.ELEMENT_NODE) { - name = node.getAttributeNS(stylens, "name"); - if(name) { - family = node.getAttributeNS(svgns, "font-family"); - if(family || node.getElementsByTagNameNS(svgns, "font-face-uri")[0]) { - fontFaceDeclsMap[name] = family - } - } - } - node = node.nextSibling - } - return fontFaceDeclsMap + if(zip_WSIZE << 1 > 1 << zip_BITS) { + runtime.log("error: zip_WSIZE is too large") } - this.getFontMap = getFontMap; - this.getAvailableParagraphStyles = function() { - var node = odfContainer.rootElement.styles && odfContainer.rootElement.styles.firstChild, p_family, p_name, p_displayName, paragraphStyles = [], style; - while(node) { - if(node.nodeType === Node.ELEMENT_NODE && (node.localName === "style" && node.namespaceURI === stylens)) { - style = node; - p_family = style.getAttributeNS(stylens, "family"); - if(p_family === "paragraph") { - p_name = style.getAttributeNS(stylens, "name"); - p_displayName = style.getAttributeNS(stylens, "display-name") || p_name; - if(p_name && p_displayName) { - paragraphStyles.push({name:p_name, displayName:p_displayName}) - } - } - } - node = node.nextSibling - } - return paragraphStyles - }; - this.isStyleUsed = function(styleElement) { - var hasDerivedStyles, isUsed; - hasDerivedStyles = styleInfo.hasDerivedStyles(odfContainer.rootElement, odf.Namespaces.resolvePrefix, styleElement); - isUsed = (new styleInfo.UsedStyleList(odfContainer.rootElement.styles)).uses(styleElement) || ((new styleInfo.UsedStyleList(odfContainer.rootElement.automaticStyles)).uses(styleElement) || (new styleInfo.UsedStyleList(odfContainer.rootElement.body)).uses(styleElement)); - return hasDerivedStyles || isUsed - }; - function getDefaultStyleElement(family) { - var node = odfContainer.rootElement.styles.firstChild; - while(node) { - if(node.nodeType === Node.ELEMENT_NODE && (node.namespaceURI === stylens && (node.localName === "default-style" && node.getAttributeNS(stylens, "family") === family))) { - return node - } - node = node.nextSibling - } - return null + if(zip_HASH_BITS > zip_BITS - 1) { + runtime.log("error: zip_HASH_BITS is too large") } - this.getDefaultStyleElement = getDefaultStyleElement; - function getStyleElement(styleName, family, styleElements) { - var node, nodeStyleName, styleListElement; - styleElements = styleElements || [odfContainer.rootElement.automaticStyles, odfContainer.rootElement.styles]; - styleListElement = styleElements.shift(); - while(styleListElement) { - node = styleListElement.firstChild; - while(node) { - if(node.nodeType === Node.ELEMENT_NODE) { - nodeStyleName = node.getAttributeNS(stylens, "name"); - if(node.namespaceURI === stylens && (node.localName === "style" && (node.getAttributeNS(stylens, "family") === family && nodeStyleName === styleName))) { - return node - } - if(family === "list-style" && (node.namespaceURI === textns && (node.localName === "list-style" && nodeStyleName === styleName))) { - return node - } - if(family === "data" && (node.namespaceURI === numberns && nodeStyleName === styleName)) { - return node - } - } - node = node.nextSibling - } - styleListElement = styleElements.shift() - } - return null + if(zip_HASH_BITS < 8 || zip_MAX_MATCH !== 258) { + runtime.log("error: Code too clever") } - this.getStyleElement = getStyleElement; - function getStyleAttributes(styleNode) { - var i, propertiesMap = {}, propertiesNode = styleNode.firstChild; - while(propertiesNode) { - if(propertiesNode.nodeType === Node.ELEMENT_NODE && propertiesNode.namespaceURI === stylens) { - propertiesMap[propertiesNode.nodeName] = {}; - for(i = 0;i < propertiesNode.attributes.length;i += 1) { - propertiesMap[propertiesNode.nodeName][propertiesNode.attributes[i].name] = propertiesNode.attributes[i].value + function Zip_DeflateCT() { + this.fc = 0; + this.dl = 0 + } + function Zip_DeflateTreeDesc() { + this.dyn_tree = null; + this.static_tree = null; + this.extra_bits = null; + this.extra_base = 0; + this.elems = 0; + this.max_length = 0; + this.max_code = 0 + } + function Zip_DeflateConfiguration(a, b, c, d) { + this.good_length = a; + this.max_lazy = b; + this.nice_length = c; + this.max_chain = d + } + function Zip_DeflateBuffer() { + this.next = null; + this.len = 0; + this.ptr = []; + this.ptr.length = zip_OUTBUFSIZ; + this.off = 0 + } + zip_configuration_table = [new Zip_DeflateConfiguration(0, 0, 0, 0), new Zip_DeflateConfiguration(4, 4, 8, 4), new Zip_DeflateConfiguration(4, 5, 16, 8), new Zip_DeflateConfiguration(4, 6, 32, 32), new Zip_DeflateConfiguration(4, 4, 16, 16), new Zip_DeflateConfiguration(8, 16, 32, 32), new Zip_DeflateConfiguration(8, 16, 128, 128), new Zip_DeflateConfiguration(8, 32, 128, 256), new Zip_DeflateConfiguration(32, 128, 258, 1024), new Zip_DeflateConfiguration(32, 258, 258, 4096)]; + function zip_deflate_start(level) { + var i; + if(!level) { + level = zip_DEFAULT_LEVEL + }else { + if(level < 1) { + level = 1 + }else { + if(level > 9) { + level = 9 } } - propertiesNode = propertiesNode.nextSibling } - for(i = 0;i < styleNode.attributes.length;i += 1) { - propertiesMap[styleNode.attributes[i].name] = styleNode.attributes[i].value + zip_compr_level = level; + zip_initflag = false; + zip_eofile = false; + if(zip_outbuf !== null) { + return } - return propertiesMap - } - this.getStyleAttributes = getStyleAttributes; - function getInheritedStyleAttributes(styleNode, includeSystemDefault) { - var styleListElement = odfContainer.rootElement.styles, parentStyleName, propertiesMap, inheritedPropertiesMap = {}, styleFamily = styleNode.getAttributeNS(stylens, "family"), node = styleNode; - while(node) { - propertiesMap = getStyleAttributes(node); - inheritedPropertiesMap = utils.mergeObjects(propertiesMap, inheritedPropertiesMap); - parentStyleName = node.getAttributeNS(stylens, "parent-style-name"); - if(parentStyleName) { - node = getStyleElement(parentStyleName, styleFamily, [styleListElement]) - }else { - node = null - } + zip_free_queue = zip_qhead = zip_qtail = null; + zip_outbuf = []; + zip_outbuf.length = zip_OUTBUFSIZ; + zip_window = []; + zip_window.length = zip_window_size; + zip_d_buf = []; + zip_d_buf.length = zip_DIST_BUFSIZE; + zip_l_buf = []; + zip_l_buf.length = zip_INBUFSIZ + zip_INBUF_EXTRA; + zip_prev = []; + zip_prev.length = 1 << zip_BITS; + zip_dyn_ltree = []; + zip_dyn_ltree.length = zip_HEAP_SIZE; + for(i = 0;i < zip_HEAP_SIZE;i++) { + zip_dyn_ltree[i] = new Zip_DeflateCT } - node = getDefaultStyleElement(styleFamily); - if(node) { - propertiesMap = getStyleAttributes(node); - inheritedPropertiesMap = utils.mergeObjects(propertiesMap, inheritedPropertiesMap) + zip_dyn_dtree = []; + zip_dyn_dtree.length = 2 * zip_D_CODES + 1; + for(i = 0;i < 2 * zip_D_CODES + 1;i++) { + zip_dyn_dtree[i] = new Zip_DeflateCT } - if(includeSystemDefault) { - propertiesMap = getSystemDefaultStyleAttributes(styleFamily); - if(propertiesMap) { - inheritedPropertiesMap = utils.mergeObjects(propertiesMap, inheritedPropertiesMap) - } + zip_static_ltree = []; + zip_static_ltree.length = zip_L_CODES + 2; + for(i = 0;i < zip_L_CODES + 2;i++) { + zip_static_ltree[i] = new Zip_DeflateCT } - return inheritedPropertiesMap - } - this.getInheritedStyleAttributes = getInheritedStyleAttributes; - this.getFirstCommonParentStyleNameOrSelf = function(styleName) { - var automaticStyleElementList = odfContainer.rootElement.automaticStyles, styleElementList = odfContainer.rootElement.styles, styleElement; - styleElement = getStyleElement(styleName, "paragraph", [automaticStyleElementList]); - while(styleElement) { - styleName = styleElement.getAttributeNS(stylens, "parent-style-name"); - styleElement = getStyleElement(styleName, "paragraph", [automaticStyleElementList]) + zip_static_dtree = []; + zip_static_dtree.length = zip_D_CODES; + for(i = 0;i < zip_D_CODES;i++) { + zip_static_dtree[i] = new Zip_DeflateCT } - styleElement = getStyleElement(styleName, "paragraph", [styleElementList]); - if(!styleElement) { - return null + zip_bl_tree = []; + zip_bl_tree.length = 2 * zip_BL_CODES + 1; + for(i = 0;i < 2 * zip_BL_CODES + 1;i++) { + zip_bl_tree[i] = new Zip_DeflateCT } - return styleName + zip_l_desc = new Zip_DeflateTreeDesc; + zip_d_desc = new Zip_DeflateTreeDesc; + zip_bl_desc = new Zip_DeflateTreeDesc; + zip_bl_count = []; + zip_bl_count.length = zip_MAX_BITS + 1; + zip_heap = []; + zip_heap.length = 2 * zip_L_CODES + 1; + zip_depth = []; + zip_depth.length = 2 * zip_L_CODES + 1; + zip_length_code = []; + zip_length_code.length = zip_MAX_MATCH - zip_MIN_MATCH + 1; + zip_dist_code = []; + zip_dist_code.length = 512; + zip_base_length = []; + zip_base_length.length = zip_LENGTH_CODES; + zip_base_dist = []; + zip_base_dist.length = zip_D_CODES; + zip_flag_buf = []; + zip_flag_buf.length = parseInt(zip_LIT_BUFSIZE / 8, 10) + } + var zip_reuse_queue = function(p) { + p.next = zip_free_queue; + zip_free_queue = p }; - this.hasParagraphStyle = function(styleName) { - return Boolean(getStyleElement(styleName, "paragraph")) + var zip_new_queue = function() { + var p; + if(zip_free_queue !== null) { + p = zip_free_queue; + zip_free_queue = zip_free_queue.next + }else { + p = new Zip_DeflateBuffer + } + p.next = null; + p.len = p.off = 0; + return p }; - function buildStyleChain(node, collectedChains) { - var parent = node.nodeType === Node.TEXT_NODE ? node.parentNode : node, nodeStyles, appliedStyles = [], chainKey = "", foundContainer = false; - while(parent) { - if(!foundContainer && odfUtils.isGroupingElement(parent)) { - foundContainer = true - } - nodeStyles = styleInfo.determineStylesForNode((parent)); - if(nodeStyles) { - appliedStyles.push(nodeStyles) + var zip_head1 = function(i) { + return zip_prev[zip_WSIZE + i] + }; + var zip_head2 = function(i, val) { + zip_prev[zip_WSIZE + i] = val; + return val + }; + var zip_qoutbuf = function() { + var q, i; + if(zip_outcnt !== 0) { + q = zip_new_queue(); + if(zip_qhead === null) { + zip_qhead = zip_qtail = q + }else { + zip_qtail = zip_qtail.next = q } - parent = parent.parentNode - } - if(foundContainer) { - appliedStyles.forEach(function(usedStyleMap) { - Object.keys(usedStyleMap).forEach(function(styleFamily) { - Object.keys(usedStyleMap[styleFamily]).forEach(function(styleName) { - chainKey += "|" + styleFamily + ":" + styleName + "|" - }) - }) - }); - if(collectedChains) { - collectedChains[chainKey] = appliedStyles + q.len = zip_outcnt - zip_outoff; + for(i = 0;i < q.len;i++) { + q.ptr[i] = zip_outbuf[zip_outoff + i] } + zip_outcnt = zip_outoff = 0 } - return foundContainer ? appliedStyles : undefined - } - function calculateAppliedStyle(styleChain) { - var mergedChildStyle = {orderedStyles:[]}; - styleChain.forEach(function(elementStyleSet) { - Object.keys((elementStyleSet)).forEach(function(styleFamily) { - var styleName = Object.keys(elementStyleSet[styleFamily])[0], styleElement, parentStyle, displayName; - styleElement = getStyleElement(styleName, styleFamily); - if(styleElement) { - parentStyle = getInheritedStyleAttributes((styleElement)); - mergedChildStyle = utils.mergeObjects(parentStyle, mergedChildStyle); - displayName = styleElement.getAttributeNS(stylens, "display-name") - }else { - runtime.log("No style element found for '" + styleName + "' of family '" + styleFamily + "'") - } - mergedChildStyle.orderedStyles.push({name:styleName, family:styleFamily, displayName:displayName}) - }) - }); - return mergedChildStyle - } - this.getAppliedStyles = function(textNodes) { - var styleChains = {}, styles = []; - textNodes.forEach(function(n) { - buildStyleChain(n, styleChains) - }); - Object.keys(styleChains).forEach(function(key) { - styles.push(calculateAppliedStyle(styleChains[key])) - }); - return styles }; - this.getAppliedStylesForElement = function(node) { - var styleChain; - styleChain = buildStyleChain(node); - return styleChain ? calculateAppliedStyle(styleChain) : undefined + var zip_put_byte = function(c) { + zip_outbuf[zip_outoff + zip_outcnt++] = c; + if(zip_outoff + zip_outcnt === zip_OUTBUFSIZ) { + zip_qoutbuf() + } }; - this.applyStyle = function(memberId, textNodes, limits, info) { - var textStyles = new odf.TextStyleApplicator(new odf.ObjectNameGenerator((odfContainer), memberId), self, odfContainer.rootElement.automaticStyles); - textStyles.applyStyle(textNodes, limits, info) - }; - this.updateStyle = function(styleNode, properties) { - var fontName, fontFaceNode; - domUtils.mapObjOntoNode(styleNode, properties, odf.Namespaces.resolvePrefix); - fontName = properties["style:text-properties"] && properties["style:text-properties"]["style:font-name"]; - if(fontName && !getFontMap().hasOwnProperty(fontName)) { - fontFaceNode = styleNode.ownerDocument.createElementNS(stylens, "style:font-face"); - fontFaceNode.setAttributeNS(stylens, "style:name", fontName); - fontFaceNode.setAttributeNS(svgns, "svg:font-family", fontName); - odfContainer.rootElement.fontFaceDecls.appendChild(fontFaceNode) + var zip_put_short = function(w) { + w &= 65535; + if(zip_outoff + zip_outcnt < zip_OUTBUFSIZ - 2) { + zip_outbuf[zip_outoff + zip_outcnt++] = w & 255; + zip_outbuf[zip_outoff + zip_outcnt++] = w >>> 8 + }else { + zip_put_byte(w & 255); + zip_put_byte(w >>> 8) } }; - function isAutomaticStyleElement(styleNode) { - return styleNode.parentNode === odfContainer.rootElement.automaticStyles - } - this.createDerivedStyleObject = function(parentStyleName, family, overrides) { - var originalStyleElement = (getStyleElement(parentStyleName, family)), newStyleObject; - runtime.assert(Boolean(originalStyleElement), "No style element found for '" + parentStyleName + "' of family '" + family + "'"); - if(isAutomaticStyleElement(originalStyleElement)) { - newStyleObject = getStyleAttributes(originalStyleElement) + var zip_INSERT_STRING = function() { + zip_ins_h = (zip_ins_h << zip_H_SHIFT ^ zip_window[zip_strstart + zip_MIN_MATCH - 1] & 255) & zip_HASH_MASK; + zip_hash_head = zip_head1(zip_ins_h); + zip_prev[zip_strstart & zip_WMASK] = zip_hash_head; + zip_head2(zip_ins_h, zip_strstart) + }; + var zip_Buf_size = 16; + var zip_send_bits = function(value, length) { + if(zip_bi_valid > zip_Buf_size - length) { + zip_bi_buf |= value << zip_bi_valid; + zip_put_short(zip_bi_buf); + zip_bi_buf = value >> zip_Buf_size - zip_bi_valid; + zip_bi_valid += length - zip_Buf_size }else { - newStyleObject = {"style:parent-style-name":parentStyleName} + zip_bi_buf |= value << zip_bi_valid; + zip_bi_valid += length } - newStyleObject["style:family"] = family; - utils.mergeObjects(newStyleObject, overrides); - return newStyleObject }; - this.getDefaultTabStopDistance = function() { - var defaultParagraph = getDefaultStyleElement("paragraph"), paragraphProperties = defaultParagraph && defaultParagraph.getAttributeNS(stylens, "paragraph-properties"), tabStopDistance = paragraphProperties && paragraphProperties.getAttributeNS(stylens, "tab-stop-distance"); - if(!tabStopDistance) { - tabStopDistance = "1.25cm" + var zip_SEND_CODE = function(c, tree) { + zip_send_bits(tree[c].fc, tree[c].dl) + }; + var zip_D_CODE = function(dist) { + return(dist < 256 ? zip_dist_code[dist] : zip_dist_code[256 + (dist >> 7)]) & 255 + }; + var zip_SMALLER = function(tree, n, m) { + return tree[n].fc < tree[m].fc || tree[n].fc === tree[m].fc && zip_depth[n] <= zip_depth[m] + }; + var zip_read_buff = function(buff, offset, n) { + var i; + for(i = 0;i < n && zip_deflate_pos < zip_deflate_data.length;i++) { + buff[offset + i] = zip_deflate_data.charCodeAt(zip_deflate_pos++) & 255 } - return odfUtils.parseNonNegativeLength(tabStopDistance) + return i }; - function getPageLayoutStyleElement(styleName, styleFamily) { - var styleElement = getStyleElement(styleName, styleFamily), masterPageName, layoutName, pageLayoutElements, node, i; - runtime.assert(styleFamily === "paragraph" || styleFamily === "table", "styleFamily has to be either paragraph or table"); - if(styleElement) { - masterPageName = styleElement.getAttributeNS(stylens, "master-page-name") || "Standard"; - node = odfContainer.rootElement.masterStyles.lastChild; - while(node && node.previousSibling) { - if(node.getAttributeNS(stylens, "name") === masterPageName) { - break + var zip_fill_window = function() { + var n, m; + var more = zip_window_size - zip_lookahead - zip_strstart; + if(more === -1) { + more-- + }else { + if(zip_strstart >= zip_WSIZE + zip_MAX_DIST) { + for(n = 0;n < zip_WSIZE;n++) { + zip_window[n] = zip_window[n + zip_WSIZE] } - node = node.previousSibling - } - layoutName = node.getAttributeNS(stylens, "page-layout-name"); - pageLayoutElements = domUtils.getElementsByTagNameNS(odfContainer.rootElement.automaticStyles, stylens, "page-layout"); - for(i = 0;i < pageLayoutElements.length;i += 1) { - node = pageLayoutElements[i]; - if(node.getAttributeNS(stylens, "name") === layoutName) { - return(node) + zip_match_start -= zip_WSIZE; + zip_strstart -= zip_WSIZE; + zip_block_start -= zip_WSIZE; + for(n = 0;n < zip_HASH_SIZE;n++) { + m = zip_head1(n); + zip_head2(n, m >= zip_WSIZE ? m - zip_WSIZE : zip_NIL) + } + for(n = 0;n < zip_WSIZE;n++) { + m = zip_prev[n]; + zip_prev[n] = m >= zip_WSIZE ? m - zip_WSIZE : zip_NIL } + more += zip_WSIZE } } - return null - } - function lengthInCm(length, defaultValue) { - var result = odfUtils.parseLength(length), value = defaultValue; - if(result) { - switch(result.unit) { - case "cm": - value = result.value; - break; - case "mm": - value = result.value * 0.1; - break; - case "in": - value = result.value * 2.54; - break; - case "pt": - value = result.value * 0.035277778; - break; - case "pc": - ; - case "px": - ; - case "em": - break; - default: - runtime.log("Unit identifier: " + result.unit + " is not supported."); - break + if(!zip_eofile) { + n = zip_read_buff(zip_window, zip_strstart + zip_lookahead, more); + if(n <= 0) { + zip_eofile = true + }else { + zip_lookahead += n } } - return value - } - this.getContentSize = function(styleName, styleFamily) { - var pageLayoutElement, props, printOrientation, defaultOrientedPageWidth, defaultOrientedPageHeight, pageWidth, pageHeight, margin, marginLeft, marginRight, marginTop, marginBottom, padding, paddingLeft, paddingRight, paddingTop, paddingBottom; - pageLayoutElement = getPageLayoutStyleElement(styleName, styleFamily); - if(!pageLayoutElement) { - pageLayoutElement = odfContainer.rootElement.styles.getElementsByTagNameNS(stylens, "default-page-layout")[0] + }; + var zip_lm_init = function() { + var j; + for(j = 0;j < zip_HASH_SIZE;j++) { + zip_prev[zip_WSIZE + j] = 0 } - if(pageLayoutElement) { - props = pageLayoutElement.getElementsByTagNameNS(stylens, "page-layout-properties")[0] + zip_max_lazy_match = zip_configuration_table[zip_compr_level].max_lazy; + zip_good_match = zip_configuration_table[zip_compr_level].good_length; + if(!zip_FULL_SEARCH) { + zip_nice_match = zip_configuration_table[zip_compr_level].nice_length } - if(props) { - printOrientation = props.getAttributeNS(stylens, "print-orientation") || "portrait"; - if(printOrientation === "portrait") { - defaultOrientedPageWidth = defaultPageFormatSettings.width; - defaultOrientedPageHeight = defaultPageFormatSettings.height - }else { - defaultOrientedPageWidth = defaultPageFormatSettings.height; - defaultOrientedPageHeight = defaultPageFormatSettings.width + zip_max_chain_length = zip_configuration_table[zip_compr_level].max_chain; + zip_strstart = 0; + zip_block_start = 0; + zip_lookahead = zip_read_buff(zip_window, 0, 2 * zip_WSIZE); + if(zip_lookahead <= 0) { + zip_eofile = true; + zip_lookahead = 0; + return + } + zip_eofile = false; + while(zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) { + zip_fill_window() + } + zip_ins_h = 0; + for(j = 0;j < zip_MIN_MATCH - 1;j++) { + zip_ins_h = (zip_ins_h << zip_H_SHIFT ^ zip_window[j] & 255) & zip_HASH_MASK + } + }; + var zip_longest_match = function(cur_match) { + var chain_length = zip_max_chain_length; + var scanp = zip_strstart; + var matchp; + var len; + var best_len = zip_prev_length; + var limit = zip_strstart > zip_MAX_DIST ? zip_strstart - zip_MAX_DIST : zip_NIL; + var strendp = zip_strstart + zip_MAX_MATCH; + var scan_end1 = zip_window[scanp + best_len - 1]; + var scan_end = zip_window[scanp + best_len]; + if(zip_prev_length >= zip_good_match) { + chain_length >>= 2 + } + do { + matchp = cur_match; + if(zip_window[matchp + best_len] !== scan_end || (zip_window[matchp + best_len - 1] !== scan_end1 || (zip_window[matchp] !== zip_window[scanp] || zip_window[++matchp] !== zip_window[scanp + 1]))) { + continue } - pageWidth = lengthInCm(props.getAttributeNS(fons, "page-width"), defaultOrientedPageWidth); - pageHeight = lengthInCm(props.getAttributeNS(fons, "page-height"), defaultOrientedPageHeight); - margin = lengthInCm(props.getAttributeNS(fons, "margin"), null); - if(margin === null) { - marginLeft = lengthInCm(props.getAttributeNS(fons, "margin-left"), defaultPageFormatSettings.margin); - marginRight = lengthInCm(props.getAttributeNS(fons, "margin-right"), defaultPageFormatSettings.margin); - marginTop = lengthInCm(props.getAttributeNS(fons, "margin-top"), defaultPageFormatSettings.margin); - marginBottom = lengthInCm(props.getAttributeNS(fons, "margin-bottom"), defaultPageFormatSettings.margin) - }else { - marginLeft = marginRight = marginTop = marginBottom = margin + scanp += 2; + matchp++; + do { + ++scanp + }while(zip_window[scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && (zip_window[++scanp] === zip_window[++matchp] && scanp < strendp)))))))); + len = zip_MAX_MATCH - (strendp - scanp); + scanp = strendp - zip_MAX_MATCH; + if(len > best_len) { + zip_match_start = cur_match; + best_len = len; + if(zip_FULL_SEARCH) { + if(len >= zip_MAX_MATCH) { + break + } + }else { + if(len >= zip_nice_match) { + break + } + } + scan_end1 = zip_window[scanp + best_len - 1]; + scan_end = zip_window[scanp + best_len] } - padding = lengthInCm(props.getAttributeNS(fons, "padding"), null); - if(padding === null) { - paddingLeft = lengthInCm(props.getAttributeNS(fons, "padding-left"), defaultPageFormatSettings.padding); - paddingRight = lengthInCm(props.getAttributeNS(fons, "padding-right"), defaultPageFormatSettings.padding); - paddingTop = lengthInCm(props.getAttributeNS(fons, "padding-top"), defaultPageFormatSettings.padding); - paddingBottom = lengthInCm(props.getAttributeNS(fons, "padding-bottom"), defaultPageFormatSettings.padding) - }else { - paddingLeft = paddingRight = paddingTop = paddingBottom = padding + cur_match = zip_prev[cur_match & zip_WMASK] + }while(cur_match > limit && --chain_length !== 0); + return best_len + }; + var zip_ct_tally = function(dist, lc) { + zip_l_buf[zip_last_lit++] = lc; + if(dist === 0) { + zip_dyn_ltree[lc].fc++ + }else { + dist--; + zip_dyn_ltree[zip_length_code[lc] + zip_LITERALS + 1].fc++; + zip_dyn_dtree[zip_D_CODE(dist)].fc++; + zip_d_buf[zip_last_dist++] = dist; + zip_flags |= zip_flag_bit + } + zip_flag_bit <<= 1; + if((zip_last_lit & 7) === 0) { + zip_flag_buf[zip_last_flags++] = zip_flags; + zip_flags = 0; + zip_flag_bit = 1 + } + if(zip_compr_level > 2 && (zip_last_lit & 4095) === 0) { + var out_length = zip_last_lit * 8; + var in_length = zip_strstart - zip_block_start; + var dcode; + for(dcode = 0;dcode < zip_D_CODES;dcode++) { + out_length += zip_dyn_dtree[dcode].fc * (5 + zip_extra_dbits[dcode]) + } + out_length >>= 3; + if(zip_last_dist < parseInt(zip_last_lit / 2, 10) && out_length < parseInt(in_length / 2, 10)) { + return true } } - return{width:pageWidth - marginLeft - marginRight - paddingLeft - paddingRight, height:pageHeight - marginTop - marginBottom - paddingTop - paddingBottom} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("core.DomUtils"); -runtime.loadClass("odf.OdfContainer"); -runtime.loadClass("odf.Formatting"); -runtime.loadClass("xmldom.XPath"); -runtime.loadClass("odf.FontLoader"); -runtime.loadClass("odf.Style2CSS"); -runtime.loadClass("odf.OdfUtils"); -runtime.loadClass("gui.AnnotationViewManager"); -odf.OdfCanvas = function() { - function LoadingQueue() { - var queue = [], taskRunning = false; - function run(task) { - taskRunning = true; - runtime.setTimeout(function() { - try { - task() - }catch(e) { - runtime.log(e) - } - taskRunning = false; - if(queue.length > 0) { - run(queue.pop()) - } - }, 10) - } - this.clearQueue = function() { - queue.length = 0 - }; - this.addToQueue = function(loadingTask) { - if(queue.length === 0 && !taskRunning) { - return run(loadingTask) + return zip_last_lit === zip_LIT_BUFSIZE - 1 || zip_last_dist === zip_DIST_BUFSIZE + }; + var zip_pqdownheap = function(tree, k) { + var v = zip_heap[k]; + var j = k << 1; + while(j <= zip_heap_len) { + if(j < zip_heap_len && zip_SMALLER(tree, zip_heap[j + 1], zip_heap[j])) { + j++ } - queue.push(loadingTask) - } - } - function PageSwitcher(css) { - var sheet = css.sheet, position = 1; - function updateCSS() { - while(sheet.cssRules.length > 0) { - sheet.deleteRule(0) + if(zip_SMALLER(tree, v, zip_heap[j])) { + break } - sheet.insertRule("#shadowContent draw|page {display:none;}", 0); - sheet.insertRule("office|presentation draw|page {display:none;}", 1); - sheet.insertRule("#shadowContent draw|page:nth-of-type(" + position + ") {display:block;}", 2); - sheet.insertRule("office|presentation draw|page:nth-of-type(" + position + ") {display:block;}", 3) + zip_heap[k] = zip_heap[j]; + k = j; + j <<= 1 } - this.showFirstPage = function() { - position = 1; - updateCSS() - }; - this.showNextPage = function() { - position += 1; - updateCSS() - }; - this.showPreviousPage = function() { - if(position > 1) { - position -= 1; - updateCSS() - } - }; - this.showPage = function(n) { - if(n > 0) { - position = n; - updateCSS() - } - }; - this.css = css; - this.destroy = function(callback) { - css.parentNode.removeChild(css); - callback() + zip_heap[k] = v + }; + var zip_gen_bitlen = function(desc) { + var tree = desc.dyn_tree; + var extra = desc.extra_bits; + var base = desc.extra_base; + var max_code = desc.max_code; + var max_length = desc.max_length; + var stree = desc.static_tree; + var h; + var n, m; + var bits; + var xbits; + var f; + var overflow = 0; + for(bits = 0;bits <= zip_MAX_BITS;bits++) { + zip_bl_count[bits] = 0 } - } - function listenEvent(eventTarget, eventType, eventHandler) { - if(eventTarget.addEventListener) { - eventTarget.addEventListener(eventType, eventHandler, false) - }else { - if(eventTarget.attachEvent) { - eventType = "on" + eventType; - eventTarget.attachEvent(eventType, eventHandler) - }else { - eventTarget["on" + eventType] = eventHandler + tree[zip_heap[zip_heap_max]].dl = 0; + for(h = zip_heap_max + 1;h < zip_HEAP_SIZE;h++) { + n = zip_heap[h]; + bits = tree[tree[n].dl].dl + 1; + if(bits > max_length) { + bits = max_length; + overflow++ } - } - } - function removeEvent(eventTarget, eventType, eventHandler) { - var onVariant = "on" + eventType; - if(eventTarget.removeEventListener) { - eventTarget.removeEventListener(eventType, eventHandler, false) - }else { - if(eventTarget.detachEvent) { - eventTarget.detachEvent(onVariant, eventHandler) - }else { - if(eventTarget[onVariant] === eventHandler) { - eventTarget[onVariant] = null - } + tree[n].dl = bits; + if(n > max_code) { + continue } - } - } - function SelectionWatcher(element) { - var selection = [], listeners = []; - function isAncestorOf(ancestor, descendant) { - while(descendant) { - if(descendant === ancestor) { - return true - } - descendant = descendant.parentNode + zip_bl_count[bits]++; + xbits = 0; + if(n >= base) { + xbits = extra[n - base] + } + f = tree[n].fc; + zip_opt_len += f * (bits + xbits); + if(stree !== null) { + zip_static_len += f * (stree[n].dl + xbits) } - return false } - function fallsWithin(element, range) { - return isAncestorOf(element, range.startContainer) && isAncestorOf(element, range.endContainer) + if(overflow === 0) { + return } - function getCurrentSelection() { - var s = [], current = runtime.getWindow().getSelection(), i, r; - for(i = 0;i < current.rangeCount;i += 1) { - r = current.getRangeAt(i); - if(r !== null && fallsWithin(element, r)) { - s.push(r) + do { + bits = max_length - 1; + while(zip_bl_count[bits] === 0) { + bits-- + } + zip_bl_count[bits]--; + zip_bl_count[bits + 1] += 2; + zip_bl_count[max_length]--; + overflow -= 2 + }while(overflow > 0); + for(bits = max_length;bits !== 0;bits--) { + n = zip_bl_count[bits]; + while(n !== 0) { + m = zip_heap[--h]; + if(m > max_code) { + continue + } + if(tree[m].dl !== bits) { + zip_opt_len += (bits - tree[m].dl) * tree[m].fc; + tree[m].fc = bits } + n-- } - return s } - function rangesNotEqual(rangeA, rangeB) { - if(rangeA === rangeB) { - return false - } - if(rangeA === null || rangeB === null) { - return true + }; + var zip_bi_reverse = function(code, len) { + var res = 0; + do { + res |= code & 1; + code >>= 1; + res <<= 1 + }while(--len > 0); + return res >> 1 + }; + var zip_gen_codes = function(tree, max_code) { + var next_code = []; + next_code.length = zip_MAX_BITS + 1; + var code = 0; + var bits; + var n; + for(bits = 1;bits <= zip_MAX_BITS;bits++) { + code = code + zip_bl_count[bits - 1] << 1; + next_code[bits] = code + } + var len; + for(n = 0;n <= max_code;n++) { + len = tree[n].dl; + if(len === 0) { + continue } - return rangeA.startContainer !== rangeB.startContainer || (rangeA.startOffset !== rangeB.startOffset || (rangeA.endContainer !== rangeB.endContainer || rangeA.endOffset !== rangeB.endOffset)) + tree[n].fc = zip_bi_reverse(next_code[len]++, len) } - function emitNewSelection() { - var i, l = listeners.length; - for(i = 0;i < l;i += 1) { - listeners[i](element, selection) + }; + var zip_build_tree = function(desc) { + var tree = desc.dyn_tree; + var stree = desc.static_tree; + var elems = desc.elems; + var n, m; + var max_code = -1; + var node = elems; + zip_heap_len = 0; + zip_heap_max = zip_HEAP_SIZE; + for(n = 0;n < elems;n++) { + if(tree[n].fc !== 0) { + zip_heap[++zip_heap_len] = max_code = n; + zip_depth[n] = 0 + }else { + tree[n].dl = 0 } } - function copySelection(selection) { - var s = [selection.length], i, oldr, r, doc = element.ownerDocument; - for(i = 0;i < selection.length;i += 1) { - oldr = selection[i]; - r = doc.createRange(); - r.setStart(oldr.startContainer, oldr.startOffset); - r.setEnd(oldr.endContainer, oldr.endOffset); - s[i] = r + var xnew; + while(zip_heap_len < 2) { + xnew = zip_heap[++zip_heap_len] = max_code < 2 ? ++max_code : 0; + tree[xnew].fc = 1; + zip_depth[xnew] = 0; + zip_opt_len--; + if(stree !== null) { + zip_static_len -= stree[xnew].dl } - return s } - function checkSelection() { - var s = getCurrentSelection(), i; - if(s.length === selection.length) { - for(i = 0;i < s.length;i += 1) { - if(rangesNotEqual(s[i], selection[i])) { - break + desc.max_code = max_code; + for(n = zip_heap_len >> 1;n >= 1;n--) { + zip_pqdownheap(tree, n) + } + do { + n = zip_heap[zip_SMALLEST]; + zip_heap[zip_SMALLEST] = zip_heap[zip_heap_len--]; + zip_pqdownheap(tree, zip_SMALLEST); + m = zip_heap[zip_SMALLEST]; + zip_heap[--zip_heap_max] = n; + zip_heap[--zip_heap_max] = m; + tree[node].fc = tree[n].fc + tree[m].fc; + if(zip_depth[n] > zip_depth[m] + 1) { + zip_depth[node] = zip_depth[n] + }else { + zip_depth[node] = zip_depth[m] + 1 + } + tree[n].dl = tree[m].dl = node; + zip_heap[zip_SMALLEST] = node++; + zip_pqdownheap(tree, zip_SMALLEST) + }while(zip_heap_len >= 2); + zip_heap[--zip_heap_max] = zip_heap[zip_SMALLEST]; + zip_gen_bitlen(desc); + zip_gen_codes(tree, max_code) + }; + var zip_scan_tree = function(tree, max_code) { + var n; + var prevlen = -1; + var curlen; + var nextlen = tree[0].dl; + var count = 0; + var max_count = 7; + var min_count = 4; + if(nextlen === 0) { + max_count = 138; + min_count = 3 + } + tree[max_code + 1].dl = 65535; + for(n = 0;n <= max_code;n++) { + curlen = nextlen; + nextlen = tree[n + 1].dl; + if(++count < max_count && curlen === nextlen) { + continue + } + if(count < min_count) { + zip_bl_tree[curlen].fc += count + }else { + if(curlen !== 0) { + if(curlen !== prevlen) { + zip_bl_tree[curlen].fc++ + } + zip_bl_tree[zip_REP_3_6].fc++ + }else { + if(count <= 10) { + zip_bl_tree[zip_REPZ_3_10].fc++ + }else { + zip_bl_tree[zip_REPZ_11_138].fc++ } - } - if(i === s.length) { - return } } - selection = s; - selection = copySelection(s); - emitNewSelection() - } - this.addListener = function(eventName, handler) { - var i, l = listeners.length; - for(i = 0;i < l;i += 1) { - if(listeners[i] === handler) { - return + count = 0; + prevlen = curlen; + if(nextlen === 0) { + max_count = 138; + min_count = 3 + }else { + if(curlen === nextlen) { + max_count = 6; + min_count = 3 + }else { + max_count = 7; + min_count = 4 } } - listeners.push(handler) - }; - this.destroy = function(callback) { - removeEvent(element, "mouseup", checkSelection); - removeEvent(element, "keyup", checkSelection); - removeEvent(element, "keydown", checkSelection); - callback() - }; - listenEvent(element, "mouseup", checkSelection); - listenEvent(element, "keyup", checkSelection); - listenEvent(element, "keydown", checkSelection) - } - var drawns = odf.Namespaces.drawns, fons = odf.Namespaces.fons, officens = odf.Namespaces.officens, stylens = odf.Namespaces.stylens, svgns = odf.Namespaces.svgns, tablens = odf.Namespaces.tablens, textns = odf.Namespaces.textns, xlinkns = odf.Namespaces.xlinkns, xmlns = odf.Namespaces.xmlns, presentationns = odf.Namespaces.presentationns, webodfhelperns = "urn:webodf:names:helper", window = runtime.getWindow(), xpath = new xmldom.XPath, odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils; - function clear(element) { - while(element.firstChild) { - element.removeChild(element.firstChild) - } - } - function handleStyles(odfcontainer, formatting, stylesxmlcss) { - var style2css = new odf.Style2CSS; - style2css.style2css(odfcontainer.getDocumentType(), stylesxmlcss.sheet, formatting.getFontMap(), odfcontainer.rootElement.styles, odfcontainer.rootElement.automaticStyles) - } - function handleFonts(odfContainer, fontcss) { - var fontLoader = new odf.FontLoader; - fontLoader.loadFonts(odfContainer, fontcss.sheet) - } - function getMasterPageElement(odfContainer, masterPageName) { - if(!masterPageName) { - return null } - var masterStyles = odfContainer.rootElement.masterStyles, masterPageElements = masterStyles.getElementsByTagNameNS(stylens, "master-page"), masterPageElement = null, i; - for(i = 0;i < masterPageElements.length;i += 1) { - if(masterPageElements[i].getAttributeNS(stylens, "name") === masterPageName) { - masterPageElement = masterPageElements[i]; + }; + var zip_build_bl_tree = function() { + var max_blindex; + zip_scan_tree(zip_dyn_ltree, zip_l_desc.max_code); + zip_scan_tree(zip_dyn_dtree, zip_d_desc.max_code); + zip_build_tree(zip_bl_desc); + for(max_blindex = zip_BL_CODES - 1;max_blindex >= 3;max_blindex--) { + if(zip_bl_tree[zip_bl_order[max_blindex]].dl !== 0) { break } } - return masterPageElement - } - function dropTemplateDrawFrames(clonedNode) { - var i, element, presentationClass, clonedDrawFrameElements = clonedNode.getElementsByTagNameNS(drawns, "frame"); - for(i = 0;i < clonedDrawFrameElements.length;i += 1) { - element = clonedDrawFrameElements[i]; - presentationClass = element.getAttributeNS(presentationns, "class"); - if(presentationClass && !/^(date-time|footer|header|page-number')$/.test(presentationClass)) { - element.parentNode.removeChild(element) + zip_opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + return max_blindex + }; + var zip_bi_windup = function() { + if(zip_bi_valid > 8) { + zip_put_short(zip_bi_buf) + }else { + if(zip_bi_valid > 0) { + zip_put_byte(zip_bi_buf) } } - } - function getHeaderFooter(odfContainer, frame, headerFooterId) { - var headerFooter = null, i, declElements = odfContainer.rootElement.body.getElementsByTagNameNS(presentationns, headerFooterId + "-decl"), headerFooterName = frame.getAttributeNS(presentationns, "use-" + headerFooterId + "-name"); - if(headerFooterName && declElements.length > 0) { - for(i = 0;i < declElements.length;i += 1) { - if(declElements[i].getAttributeNS(presentationns, "name") === headerFooterName) { - headerFooter = declElements[i].textContent; - break + zip_bi_buf = 0; + zip_bi_valid = 0 + }; + var zip_compress_block = function(ltree, dtree) { + var dist; + var lc; + var lx = 0; + var dx = 0; + var fx = 0; + var flag = 0; + var code; + var extra; + if(zip_last_lit !== 0) { + do { + if((lx & 7) === 0) { + flag = zip_flag_buf[fx++] } - } + lc = zip_l_buf[lx++] & 255; + if((flag & 1) === 0) { + zip_SEND_CODE(lc, ltree) + }else { + code = zip_length_code[lc]; + zip_SEND_CODE(code + zip_LITERALS + 1, ltree); + extra = zip_extra_lbits[code]; + if(extra !== 0) { + lc -= zip_base_length[code]; + zip_send_bits(lc, extra) + } + dist = zip_d_buf[dx++]; + code = zip_D_CODE(dist); + zip_SEND_CODE(code, dtree); + extra = zip_extra_dbits[code]; + if(extra !== 0) { + dist -= zip_base_dist[code]; + zip_send_bits(dist, extra) + } + } + flag >>= 1 + }while(lx < zip_last_lit) } - return headerFooter - } - function setContainerValue(rootElement, ns, localName, value) { - var i, containerList, document = rootElement.ownerDocument; - containerList = rootElement.getElementsByTagNameNS(ns, localName); - for(i = 0;i < containerList.length;i += 1) { - clear(containerList[i]); - if(value) { - containerList[i].appendChild(document.createTextNode(value)) - } + zip_SEND_CODE(zip_END_BLOCK, ltree) + }; + var zip_send_tree = function(tree, max_code) { + var n; + var prevlen = -1; + var curlen; + var nextlen = tree[0].dl; + var count = 0; + var max_count = 7; + var min_count = 4; + if(nextlen === 0) { + max_count = 138; + min_count = 3 } - } - function setDrawElementPosition(styleid, frame, stylesheet) { - frame.setAttributeNS(webodfhelperns, "styleid", styleid); - var rule, anchor = frame.getAttributeNS(textns, "anchor-type"), x = frame.getAttributeNS(svgns, "x"), y = frame.getAttributeNS(svgns, "y"), width = frame.getAttributeNS(svgns, "width"), height = frame.getAttributeNS(svgns, "height"), minheight = frame.getAttributeNS(fons, "min-height"), minwidth = frame.getAttributeNS(fons, "min-width"); - if(anchor === "as-char") { - rule = "display: inline-block;" - }else { - if(anchor || (x || y)) { - rule = "position: absolute;" + for(n = 0;n <= max_code;n++) { + curlen = nextlen; + nextlen = tree[n + 1].dl; + if(++count < max_count && curlen === nextlen) { + continue + } + if(count < min_count) { + do { + zip_SEND_CODE(curlen, zip_bl_tree) + }while(--count !== 0) }else { - if(width || (height || (minheight || minwidth))) { - rule = "display: block;" + if(curlen !== 0) { + if(curlen !== prevlen) { + zip_SEND_CODE(curlen, zip_bl_tree); + count-- + } + zip_SEND_CODE(zip_REP_3_6, zip_bl_tree); + zip_send_bits(count - 3, 2) + }else { + if(count <= 10) { + zip_SEND_CODE(zip_REPZ_3_10, zip_bl_tree); + zip_send_bits(count - 3, 3) + }else { + zip_SEND_CODE(zip_REPZ_11_138, zip_bl_tree); + zip_send_bits(count - 11, 7) + } + } + } + count = 0; + prevlen = curlen; + if(nextlen === 0) { + max_count = 138; + min_count = 3 + }else { + if(curlen === nextlen) { + max_count = 6; + min_count = 3 + }else { + max_count = 7; + min_count = 4 } } } - if(x) { - rule += "left: " + x + ";" + }; + var zip_send_all_trees = function(lcodes, dcodes, blcodes) { + var rank; + zip_send_bits(lcodes - 257, 5); + zip_send_bits(dcodes - 1, 5); + zip_send_bits(blcodes - 4, 4); + for(rank = 0;rank < blcodes;rank++) { + zip_send_bits(zip_bl_tree[zip_bl_order[rank]].dl, 3) } - if(y) { - rule += "top: " + y + ";" + zip_send_tree(zip_dyn_ltree, lcodes - 1); + zip_send_tree(zip_dyn_dtree, dcodes - 1) + }; + var zip_init_block = function() { + var n; + for(n = 0;n < zip_L_CODES;n++) { + zip_dyn_ltree[n].fc = 0 } - if(width) { - rule += "width: " + width + ";" - } - if(height) { - rule += "height: " + height + ";" - } - if(minheight) { - rule += "min-height: " + minheight + ";" - } - if(minwidth) { - rule += "min-width: " + minwidth + ";" - } - if(rule) { - rule = "draw|" + frame.localName + '[webodfhelper|styleid="' + styleid + '"] {' + rule + "}"; - stylesheet.insertRule(rule, stylesheet.cssRules.length) + for(n = 0;n < zip_D_CODES;n++) { + zip_dyn_dtree[n].fc = 0 } - } - function getUrlFromBinaryDataElement(image) { - var node = image.firstChild; - while(node) { - if(node.namespaceURI === officens && node.localName === "binary-data") { - return"data:image/png;base64," + node.textContent.replace(/[\r\n\s]/g, "") - } - node = node.nextSibling + for(n = 0;n < zip_BL_CODES;n++) { + zip_bl_tree[n].fc = 0 } - return"" - } - function setImage(id, container, image, stylesheet) { - image.setAttributeNS(webodfhelperns, "styleid", id); - var url = image.getAttributeNS(xlinkns, "href"), part; - function callback(url) { - var rule; - if(url) { - rule = "background-image: url(" + url + ");"; - rule = 'draw|image[webodfhelper|styleid="' + id + '"] {' + rule + "}"; - stylesheet.insertRule(rule, stylesheet.cssRules.length) - } + zip_dyn_ltree[zip_END_BLOCK].fc = 1; + zip_opt_len = zip_static_len = 0; + zip_last_lit = zip_last_dist = zip_last_flags = 0; + zip_flags = 0; + zip_flag_bit = 1 + }; + var zip_flush_block = function(eof) { + var opt_lenb, static_lenb; + var max_blindex; + var stored_len; + stored_len = zip_strstart - zip_block_start; + zip_flag_buf[zip_last_flags] = zip_flags; + zip_build_tree(zip_l_desc); + zip_build_tree(zip_d_desc); + max_blindex = zip_build_bl_tree(); + opt_lenb = zip_opt_len + 3 + 7 >> 3; + static_lenb = zip_static_len + 3 + 7 >> 3; + if(static_lenb <= opt_lenb) { + opt_lenb = static_lenb } - if(url) { - try { - part = container.getPart(url); - part.onchange = function(part) { - callback(part.url) - }; - part.load() - }catch(e) { - runtime.log("slight problem: " + e) + if(stored_len + 4 <= opt_lenb && zip_block_start >= 0) { + var i; + zip_send_bits((zip_STORED_BLOCK << 1) + eof, 3); + zip_bi_windup(); + zip_put_short(stored_len); + zip_put_short(~stored_len); + for(i = 0;i < stored_len;i++) { + zip_put_byte(zip_window[zip_block_start + i]) } }else { - url = getUrlFromBinaryDataElement(image); - callback(url) - } - } - function formatParagraphAnchors(odfbody) { - var n, i, nodes = xpath.getODFElementsWithXPath(odfbody, ".//*[*[@text:anchor-type='paragraph']]", odf.Namespaces.resolvePrefix); - for(i = 0;i < nodes.length;i += 1) { - n = nodes[i]; - if(n.setAttributeNS) { - n.setAttributeNS(webodfhelperns, "containsparagraphanchor", true) - } - } - } - function modifyTables(odffragment) { - var i, tableCells, node; - function modifyTableCell(node) { - if(node.hasAttributeNS(tablens, "number-columns-spanned")) { - node.setAttribute("colspan", node.getAttributeNS(tablens, "number-columns-spanned")) - } - if(node.hasAttributeNS(tablens, "number-rows-spanned")) { - node.setAttribute("rowspan", node.getAttributeNS(tablens, "number-rows-spanned")) + if(static_lenb === opt_lenb) { + zip_send_bits((zip_STATIC_TREES << 1) + eof, 3); + zip_compress_block(zip_static_ltree, zip_static_dtree) + }else { + zip_send_bits((zip_DYN_TREES << 1) + eof, 3); + zip_send_all_trees(zip_l_desc.max_code + 1, zip_d_desc.max_code + 1, max_blindex + 1); + zip_compress_block(zip_dyn_ltree, zip_dyn_dtree) } } - tableCells = odffragment.getElementsByTagNameNS(tablens, "table-cell"); - for(i = 0;i < tableCells.length;i += 1) { - node = (tableCells.item(i)); - modifyTableCell(node) + zip_init_block(); + if(eof !== 0) { + zip_bi_windup() } - } - function modifyLinks(odffragment) { - var i, links, node; - function modifyLink(node) { - var url, clickHandler; - if(!node.hasAttributeNS(xlinkns, "href")) { - return + }; + var zip_deflate_fast = function() { + var flush; + while(zip_lookahead !== 0 && zip_qhead === null) { + zip_INSERT_STRING(); + if(zip_hash_head !== zip_NIL && zip_strstart - zip_hash_head <= zip_MAX_DIST) { + zip_match_length = zip_longest_match(zip_hash_head); + if(zip_match_length > zip_lookahead) { + zip_match_length = zip_lookahead + } } - url = node.getAttributeNS(xlinkns, "href"); - if(url[0] === "#") { - url = url.substring(1); - clickHandler = function() { - var bookmarks = xpath.getODFElementsWithXPath(odffragment, "//text:bookmark-start[@text:name='" + url + "']", odf.Namespaces.resolvePrefix); - if(bookmarks.length === 0) { - bookmarks = xpath.getODFElementsWithXPath(odffragment, "//text:bookmark[@text:name='" + url + "']", odf.Namespaces.resolvePrefix) - } - if(bookmarks.length > 0) { - bookmarks[0].scrollIntoView(true) - } - return false + if(zip_match_length >= zip_MIN_MATCH) { + flush = zip_ct_tally(zip_strstart - zip_match_start, zip_match_length - zip_MIN_MATCH); + zip_lookahead -= zip_match_length; + if(zip_match_length <= zip_max_lazy_match) { + zip_match_length--; + do { + zip_strstart++; + zip_INSERT_STRING() + }while(--zip_match_length !== 0); + zip_strstart++ + }else { + zip_strstart += zip_match_length; + zip_match_length = 0; + zip_ins_h = zip_window[zip_strstart] & 255; + zip_ins_h = (zip_ins_h << zip_H_SHIFT ^ zip_window[zip_strstart + 1] & 255) & zip_HASH_MASK } }else { - clickHandler = function() { - window.open(url) - } - } - node.onclick = clickHandler - } - links = odffragment.getElementsByTagNameNS(textns, "a"); - for(i = 0;i < links.length;i += 1) { - node = (links.item(i)); - modifyLink(node) - } - } - function modifyLineBreakElements(odffragment) { - var document = odffragment.ownerDocument, lineBreakElements = domUtils.getElementsByTagNameNS(odffragment, textns, "line-break"); - lineBreakElements.forEach(function(lineBreak) { - if(!lineBreak.hasChildNodes()) { - lineBreak.appendChild(document.createElement("br")) + flush = zip_ct_tally(0, zip_window[zip_strstart] & 255); + zip_lookahead--; + zip_strstart++ } - }) - } - function expandSpaceElements(odffragment) { - var spaces, doc = odffragment.ownerDocument; - function expandSpaceElement(space) { - var j, count; - while(space.firstChild) { - space.removeChild(space.firstChild) + if(flush) { + zip_flush_block(0); + zip_block_start = zip_strstart } - space.appendChild(doc.createTextNode(" ")); - count = parseInt(space.getAttributeNS(textns, "c"), 10); - if(count > 1) { - space.removeAttributeNS(textns, "c"); - for(j = 1;j < count;j += 1) { - space.parentNode.insertBefore(space.cloneNode(true), space) - } + while(zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) { + zip_fill_window() } } - spaces = domUtils.getElementsByTagNameNS(odffragment, textns, "s"); - spaces.forEach(expandSpaceElement) - } - function expandTabElements(odffragment) { - var tabs; - tabs = domUtils.getElementsByTagNameNS(odffragment, textns, "tab"); - tabs.forEach(function(tab) { - tab.textContent = "\t" - }) - } - function modifyDrawElements(odfbody, stylesheet) { - var node, drawElements, i; - drawElements = []; - node = odfbody.firstChild; - while(node && node !== odfbody) { - if(node.namespaceURI === drawns) { - drawElements[drawElements.length] = node + }; + var zip_deflate_better = function() { + var flush; + while(zip_lookahead !== 0 && zip_qhead === null) { + zip_INSERT_STRING(); + zip_prev_length = zip_match_length; + zip_prev_match = zip_match_start; + zip_match_length = zip_MIN_MATCH - 1; + if(zip_hash_head !== zip_NIL && (zip_prev_length < zip_max_lazy_match && zip_strstart - zip_hash_head <= zip_MAX_DIST)) { + zip_match_length = zip_longest_match(zip_hash_head); + if(zip_match_length > zip_lookahead) { + zip_match_length = zip_lookahead + } + if(zip_match_length === zip_MIN_MATCH && zip_strstart - zip_match_start > zip_TOO_FAR) { + zip_match_length-- + } } - if(node.firstChild) { - node = node.firstChild - }else { - while(node && (node !== odfbody && !node.nextSibling)) { - node = node.parentNode + if(zip_prev_length >= zip_MIN_MATCH && zip_match_length <= zip_prev_length) { + flush = zip_ct_tally(zip_strstart - 1 - zip_prev_match, zip_prev_length - zip_MIN_MATCH); + zip_lookahead -= zip_prev_length - 1; + zip_prev_length -= 2; + do { + zip_strstart++; + zip_INSERT_STRING() + }while(--zip_prev_length !== 0); + zip_match_available = 0; + zip_match_length = zip_MIN_MATCH - 1; + zip_strstart++; + if(flush) { + zip_flush_block(0); + zip_block_start = zip_strstart } - if(node && node.nextSibling) { - node = node.nextSibling + }else { + if(zip_match_available !== 0) { + if(zip_ct_tally(0, zip_window[zip_strstart - 1] & 255)) { + zip_flush_block(0); + zip_block_start = zip_strstart + } + zip_strstart++; + zip_lookahead-- + }else { + zip_match_available = 1; + zip_strstart++; + zip_lookahead-- } } + while(zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) { + zip_fill_window() + } } - for(i = 0;i < drawElements.length;i += 1) { - node = drawElements[i]; - setDrawElementPosition("frame" + String(i), node, stylesheet) + }; + var zip_ct_init = function() { + var n; + var bits; + var length; + var code; + var dist; + if(zip_static_dtree[0].dl !== 0) { + return } - formatParagraphAnchors(odfbody) - } - function cloneMasterPages(odfContainer, shadowContent, odfbody, stylesheet) { - var masterPageName, masterPageElement, styleId, clonedPageElement, clonedElement, pageNumber = 0, i, element, elementToClone, document = odfContainer.rootElement.ownerDocument; - element = odfbody.firstElementChild; - if(!(element && (element.namespaceURI === officens && (element.localName === "presentation" || element.localName === "drawing")))) { - return - } - element = element.firstElementChild; - while(element) { - masterPageName = element.getAttributeNS(drawns, "master-page-name"); - masterPageElement = getMasterPageElement(odfContainer, masterPageName); - if(masterPageElement) { - styleId = element.getAttributeNS(webodfhelperns, "styleid"); - clonedPageElement = document.createElementNS(drawns, "draw:page"); - elementToClone = masterPageElement.firstElementChild; - i = 0; - while(elementToClone) { - if(elementToClone.getAttributeNS(presentationns, "placeholder") !== "true") { - clonedElement = elementToClone.cloneNode(true); - clonedPageElement.appendChild(clonedElement); - setDrawElementPosition(styleId + "_" + i, (clonedElement), stylesheet) - } - elementToClone = elementToClone.nextElementSibling; - i += 1 - } - dropTemplateDrawFrames(clonedPageElement); - shadowContent.appendChild(clonedPageElement); - pageNumber = String(shadowContent.getElementsByTagNameNS(drawns, "page").length); - setContainerValue(clonedPageElement, textns, "page-number", pageNumber); - setContainerValue(clonedPageElement, presentationns, "header", getHeaderFooter(odfContainer, (element), "header")); - setContainerValue(clonedPageElement, presentationns, "footer", getHeaderFooter(odfContainer, (element), "footer")); - setDrawElementPosition(styleId, clonedPageElement, stylesheet); - clonedPageElement.setAttributeNS(drawns, "draw:master-page-name", masterPageElement.getAttributeNS(stylens, "name")) + zip_l_desc.dyn_tree = zip_dyn_ltree; + zip_l_desc.static_tree = zip_static_ltree; + zip_l_desc.extra_bits = zip_extra_lbits; + zip_l_desc.extra_base = zip_LITERALS + 1; + zip_l_desc.elems = zip_L_CODES; + zip_l_desc.max_length = zip_MAX_BITS; + zip_l_desc.max_code = 0; + zip_d_desc.dyn_tree = zip_dyn_dtree; + zip_d_desc.static_tree = zip_static_dtree; + zip_d_desc.extra_bits = zip_extra_dbits; + zip_d_desc.extra_base = 0; + zip_d_desc.elems = zip_D_CODES; + zip_d_desc.max_length = zip_MAX_BITS; + zip_d_desc.max_code = 0; + zip_bl_desc.dyn_tree = zip_bl_tree; + zip_bl_desc.static_tree = null; + zip_bl_desc.extra_bits = zip_extra_blbits; + zip_bl_desc.extra_base = 0; + zip_bl_desc.elems = zip_BL_CODES; + zip_bl_desc.max_length = zip_MAX_BL_BITS; + zip_bl_desc.max_code = 0; + length = 0; + for(code = 0;code < zip_LENGTH_CODES - 1;code++) { + zip_base_length[code] = length; + for(n = 0;n < 1 << zip_extra_lbits[code];n++) { + zip_length_code[length++] = code } - element = element.nextElementSibling } - } - function setVideo(container, plugin) { - var video, source, url, doc = plugin.ownerDocument, part; - url = plugin.getAttributeNS(xlinkns, "href"); - function callback(url, mimetype) { - var ns = doc.documentElement.namespaceURI; - if(mimetype.substr(0, 6) === "video/") { - video = doc.createElementNS(ns, "video"); - video.setAttribute("controls", "controls"); - source = doc.createElementNS(ns, "source"); - source.setAttribute("src", url); - source.setAttribute("type", mimetype); - video.appendChild(source); - plugin.parentNode.appendChild(video) - }else { - plugin.innerHtml = "Unrecognised Plugin" + zip_length_code[length - 1] = code; + dist = 0; + for(code = 0;code < 16;code++) { + zip_base_dist[code] = dist; + for(n = 0;n < 1 << zip_extra_dbits[code];n++) { + zip_dist_code[dist++] = code } } - if(url) { - try { - part = container.getPart(url); - part.onchange = function(part) { - callback(part.url, part.mimetype) - }; - part.load() - }catch(e) { - runtime.log("slight problem: " + e) + dist >>= 7; + n = code; + for(code = n;code < zip_D_CODES;code++) { + zip_base_dist[code] = dist << 7; + for(n = 0;n < 1 << zip_extra_dbits[code] - 7;n++) { + zip_dist_code[256 + dist++] = code } - }else { - runtime.log("using MP4 data fallback"); - url = getUrlFromBinaryDataElement(plugin); - callback(url, "video/mp4") } - } - function getNumberRule(node) { - var style = node.getAttributeNS(stylens, "num-format"), suffix = node.getAttributeNS(stylens, "num-suffix"), prefix = node.getAttributeNS(stylens, "num-prefix"), rule = "", stylemap = {1:"decimal", "a":"lower-latin", "A":"upper-latin", "i":"lower-roman", "I":"upper-roman"}, content; - content = prefix || ""; - if(stylemap.hasOwnProperty(style)) { - content += " counter(list, " + stylemap[style] + ")" - }else { - if(style) { - content += "'" + style + "';" - }else { - content += " ''" - } + for(bits = 0;bits <= zip_MAX_BITS;bits++) { + zip_bl_count[bits] = 0 } - if(suffix) { - content += " '" + suffix + "'" + n = 0; + while(n <= 143) { + zip_static_ltree[n++].dl = 8; + zip_bl_count[8]++ } - rule = "content: " + content + ";"; - return rule - } - function getImageRule() { - var rule = "content: none;"; - return rule - } - function getBulletRule(node) { - var bulletChar = node.getAttributeNS(textns, "bullet-char"); - return"content: '" + bulletChar + "';" - } - function getBulletsRule(node) { - var itemrule; - if(node) { - if(node.localName === "list-level-style-number") { - itemrule = getNumberRule(node) - }else { - if(node.localName === "list-level-style-image") { - itemrule = getImageRule() - }else { - if(node.localName === "list-level-style-bullet") { - itemrule = getBulletRule(node) - } - } - } + while(n <= 255) { + zip_static_ltree[n++].dl = 9; + zip_bl_count[9]++ } - return itemrule - } - function loadLists(odffragment, stylesheet) { - var i, lists, node, id, continueList, styleName, rule, listMap = {}, parentList, listStyles, listStyleMap = {}, bulletRule; - listStyles = window.document.getElementsByTagNameNS(textns, "list-style"); - for(i = 0;i < listStyles.length;i += 1) { - node = (listStyles.item(i)); - styleName = node.getAttributeNS(stylens, "name"); - if(styleName) { - listStyleMap[styleName] = node - } + while(n <= 279) { + zip_static_ltree[n++].dl = 7; + zip_bl_count[7]++ } - lists = odffragment.getElementsByTagNameNS(textns, "list"); - for(i = 0;i < lists.length;i += 1) { - node = (lists.item(i)); - id = node.getAttributeNS(xmlns, "id"); - if(id) { - continueList = node.getAttributeNS(textns, "continue-list"); - node.setAttribute("id", id); - rule = "text|list#" + id + " > text|list-item > *:first-child:before {"; - styleName = node.getAttributeNS(textns, "style-name"); - if(styleName) { - node = listStyleMap[styleName]; - bulletRule = getBulletsRule((odfUtils.getFirstNonWhitespaceChild(node))) - } - if(continueList) { - parentList = listMap[continueList]; - while(parentList) { - continueList = parentList; - parentList = listMap[continueList] - } - rule += "counter-increment:" + continueList + ";"; - if(bulletRule) { - bulletRule = bulletRule.replace("list", continueList); - rule += bulletRule - }else { - rule += "content:counter(" + continueList + ");" - } - }else { - continueList = ""; - if(bulletRule) { - bulletRule = bulletRule.replace("list", id); - rule += bulletRule - }else { - rule += "content: counter(" + id + ");" - } - rule += "counter-increment:" + id + ";"; - stylesheet.insertRule("text|list#" + id + " {counter-reset:" + id + "}", stylesheet.cssRules.length) - } - rule += "}"; - listMap[id] = continueList; - if(rule) { - stylesheet.insertRule(rule, stylesheet.cssRules.length) - } - } + while(n <= 287) { + zip_static_ltree[n++].dl = 8; + zip_bl_count[8]++ } - } - function addWebODFStyleSheet(document) { - var head = document.getElementsByTagName("head")[0], style, href; - if(String(typeof webodf_css) !== "undefined") { - style = document.createElementNS(head.namespaceURI, "style"); - style.setAttribute("media", "screen, print, handheld, projection"); - style.appendChild(document.createTextNode(webodf_css)) + zip_gen_codes(zip_static_ltree, zip_L_CODES + 1); + for(n = 0;n < zip_D_CODES;n++) { + zip_static_dtree[n].dl = 5; + zip_static_dtree[n].fc = zip_bi_reverse(n, 5) + } + zip_init_block() + }; + var zip_init_deflate = function() { + if(zip_eofile) { + return + } + zip_bi_buf = 0; + zip_bi_valid = 0; + zip_ct_init(); + zip_lm_init(); + zip_qhead = null; + zip_outcnt = 0; + zip_outoff = 0; + if(zip_compr_level <= 3) { + zip_prev_length = zip_MIN_MATCH - 1; + zip_match_length = 0 }else { - style = document.createElementNS(head.namespaceURI, "link"); - href = "webodf.css"; - if(runtime.currentDirectory) { - href = runtime.currentDirectory() + "/../" + href + zip_match_length = zip_MIN_MATCH - 1; + zip_match_available = 0 + } + zip_complete = false + }; + var zip_qcopy = function(buff, off, buff_size) { + var n, i, j, p; + n = 0; + while(zip_qhead !== null && n < buff_size) { + i = buff_size - n; + if(i > zip_qhead.len) { + i = zip_qhead.len + } + for(j = 0;j < i;j++) { + buff[off + n + j] = zip_qhead.ptr[zip_qhead.off + j] + } + zip_qhead.off += i; + zip_qhead.len -= i; + n += i; + if(zip_qhead.len === 0) { + p = zip_qhead; + zip_qhead = zip_qhead.next; + zip_reuse_queue(p) } - style.setAttribute("href", href); - style.setAttribute("rel", "stylesheet") } - style.setAttribute("type", "text/css"); - head.appendChild(style); - return(style) - } - function addStyleSheet(document) { - var head = document.getElementsByTagName("head")[0], style = document.createElementNS(head.namespaceURI, "style"), text = ""; - style.setAttribute("type", "text/css"); - style.setAttribute("media", "screen, print, handheld, projection"); - odf.Namespaces.forEachPrefix(function(prefix, ns) { - text += "@namespace " + prefix + " url(" + ns + ");\n" - }); - text += "@namespace webodfhelper url(" + webodfhelperns + ");\n"; - style.appendChild(document.createTextNode(text)); - head.appendChild(style); - return(style) - } - odf.OdfCanvas = function OdfCanvas(element) { - runtime.assert(element !== null && element !== undefined, "odf.OdfCanvas constructor needs DOM element"); - runtime.assert(element.ownerDocument !== null && element.ownerDocument !== undefined, "odf.OdfCanvas constructor needs DOM"); - var self = this, doc = (element.ownerDocument), odfcontainer, formatting = new odf.Formatting, selectionWatcher = new SelectionWatcher(element), pageSwitcher, sizer, annotationsPane, allowAnnotations = false, annotationViewManager, webodfcss, fontcss, stylesxmlcss, positioncss, shadowContent, zoomLevel = 1, eventHandlers = {}, loadingQueue = new LoadingQueue; - function loadImages(container, odffragment, stylesheet) { - var i, images, node; - function loadImage(name, container, node, stylesheet) { - loadingQueue.addToQueue(function() { - setImage(name, container, node, stylesheet) - }) - } - images = odffragment.getElementsByTagNameNS(drawns, "image"); - for(i = 0;i < images.length;i += 1) { - node = (images.item(i)); - loadImage("image" + String(i), container, node, stylesheet) - } + if(n === buff_size) { + return n } - function loadVideos(container, odffragment) { - var i, plugins, node; - function loadVideo(container, node) { - loadingQueue.addToQueue(function() { - setVideo(container, node) - }) - } - plugins = odffragment.getElementsByTagNameNS(drawns, "plugin"); - for(i = 0;i < plugins.length;i += 1) { - node = (plugins.item(i)); - loadVideo(container, node) + if(zip_outoff < zip_outcnt) { + i = buff_size - n; + if(i > zip_outcnt - zip_outoff) { + i = zip_outcnt - zip_outoff } - } - function addEventListener(eventType, eventHandler) { - var handlers = eventHandlers[eventType]; - if(handlers === undefined) { - handlers = eventHandlers[eventType] = [] + for(j = 0;j < i;j++) { + buff[off + n + j] = zip_outbuf[zip_outoff + j] } - if(eventHandler && handlers.indexOf(eventHandler) === -1) { - handlers.push(eventHandler) + zip_outoff += i; + n += i; + if(zip_outcnt === zip_outoff) { + zip_outcnt = zip_outoff = 0 } } - function fireEvent(eventType, args) { - if(!eventHandlers.hasOwnProperty(eventType)) { - return - } - var handlers = eventHandlers[eventType], i; - for(i = 0;i < handlers.length;i += 1) { - handlers[i].apply(null, args) + return n + }; + var zip_deflate_internal = function(buff, off, buff_size) { + var n; + if(!zip_initflag) { + zip_init_deflate(); + zip_initflag = true; + if(zip_lookahead === 0) { + zip_complete = true; + return 0 } } - function fixContainerSize() { - var odfdoc = sizer.firstChild; - if(!odfdoc) { - return - } - if(zoomLevel > 1) { - sizer.style.MozTransformOrigin = "center top"; - sizer.style.WebkitTransformOrigin = "center top"; - sizer.style.OTransformOrigin = "center top"; - sizer.style.msTransformOrigin = "center top" - }else { - sizer.style.MozTransformOrigin = "left top"; - sizer.style.WebkitTransformOrigin = "left top"; - sizer.style.OTransformOrigin = "left top"; - sizer.style.msTransformOrigin = "left top" - } - sizer.style.WebkitTransform = "scale(" + zoomLevel + ")"; - sizer.style.MozTransform = "scale(" + zoomLevel + ")"; - sizer.style.OTransform = "scale(" + zoomLevel + ")"; - sizer.style.msTransform = "scale(" + zoomLevel + ")"; - element.style.width = Math.round(zoomLevel * sizer.offsetWidth) + "px"; - element.style.height = Math.round(zoomLevel * sizer.offsetHeight) + "px" + n = zip_qcopy(buff, off, buff_size); + if(n === buff_size) { + return buff_size } - function handleContent(container, odfnode) { - var css = positioncss.sheet; - clear(element); - sizer = doc.createElementNS(element.namespaceURI, "div"); - sizer.style.display = "inline-block"; - sizer.style.background = "white"; - sizer.appendChild(odfnode); - element.appendChild(sizer); - annotationsPane = doc.createElementNS(element.namespaceURI, "div"); - annotationsPane.id = "annotationsPane"; - shadowContent = doc.createElementNS(element.namespaceURI, "div"); - shadowContent.id = "shadowContent"; - shadowContent.style.position = "absolute"; - shadowContent.style.top = 0; - shadowContent.style.left = 0; - container.getContentElement().appendChild(shadowContent); - modifyDrawElements(odfnode.body, css); - cloneMasterPages(container, shadowContent, odfnode.body, css); - modifyTables(odfnode.body); - modifyLinks(odfnode.body); - modifyLineBreakElements(odfnode.body); - expandSpaceElements(odfnode.body); - expandTabElements(odfnode.body); - loadImages(container, odfnode.body, css); - loadVideos(container, odfnode.body); - loadLists(odfnode.body, css); - sizer.insertBefore(shadowContent, sizer.firstChild); - fixContainerSize() + if(zip_complete) { + return n } - function modifyAnnotations(odffragment) { - var annotationNodes = domUtils.getElementsByTagNameNS(odffragment, officens, "annotation"), annotationEnds = domUtils.getElementsByTagNameNS(odffragment, officens, "annotation-end"), currentAnnotationName, i; - function matchAnnotationEnd(element) { - return currentAnnotationName === element.getAttributeNS(officens, "name") + if(zip_compr_level <= 3) { + zip_deflate_fast() + }else { + zip_deflate_better() + } + if(zip_lookahead === 0) { + if(zip_match_available !== 0) { + zip_ct_tally(0, zip_window[zip_strstart - 1] & 255) } - for(i = 0;i < annotationNodes.length;i += 1) { - currentAnnotationName = annotationNodes[i].getAttributeNS(officens, "name"); - annotationViewManager.addAnnotation({node:annotationNodes[i], end:annotationEnds.filter(matchAnnotationEnd)[0] || null}) + zip_flush_block(1); + zip_complete = true + } + return n + zip_qcopy(buff, n + off, buff_size - n) + }; + var zip_deflate = function(str, level) { + var i, j; + zip_deflate_data = str; + zip_deflate_pos = 0; + if(String(typeof level) === "undefined") { + level = zip_DEFAULT_LEVEL + } + zip_deflate_start(level); + var buff = new Array(1024); + var aout = [], cbuf = []; + i = zip_deflate_internal(buff, 0, buff.length); + while(i > 0) { + cbuf.length = i; + for(j = 0;j < i;j++) { + cbuf[j] = String.fromCharCode(buff[j]) } - annotationViewManager.rerenderAnnotations() + aout[aout.length] = cbuf.join(""); + i = zip_deflate_internal(buff, 0, buff.length) } - function handleAnnotations(odfnode) { - if(allowAnnotations) { - if(!annotationsPane.parentNode) { - sizer.appendChild(annotationsPane); - fixContainerSize() - } - if(annotationViewManager) { - annotationViewManager.forgetAnnotations() - } - annotationViewManager = new gui.AnnotationViewManager(self, odfnode.body, annotationsPane); - modifyAnnotations(odfnode.body) - }else { - if(annotationsPane.parentNode) { - sizer.removeChild(annotationsPane); - annotationViewManager.forgetAnnotations(); - fixContainerSize() - } + zip_deflate_data = ""; + return aout.join("") + }; + this.deflate = zip_deflate +}; +runtime.loadClass("odf.Namespaces"); +gui.ImageSelector = function ImageSelector(odfCanvas) { + var svgns = odf.Namespaces.svgns, imageSelectorId = "imageSelector", selectorBorderWidth = 1, squareClassNames = ["topLeft", "topRight", "bottomRight", "bottomLeft", "topMiddle", "rightMiddle", "bottomMiddle", "leftMiddle"], document = odfCanvas.getElement().ownerDocument, hasSelection = false; + function createSelectorElement() { + var sizerElement = odfCanvas.getSizer(), selectorElement, squareElement; + selectorElement = document.createElement("div"); + selectorElement.id = "imageSelector"; + selectorElement.style.borderWidth = selectorBorderWidth + "px"; + sizerElement.appendChild(selectorElement); + squareClassNames.forEach(function(className) { + squareElement = document.createElement("div"); + squareElement.className = className; + selectorElement.appendChild(squareElement) + }); + return selectorElement + } + function getPosition(element, referenceElement) { + var rect = element.getBoundingClientRect(), refRect = referenceElement.getBoundingClientRect(), zoomLevel = odfCanvas.getZoomLevel(); + return{left:(rect.left - refRect.left) / zoomLevel - selectorBorderWidth, top:(rect.top - refRect.top) / zoomLevel - selectorBorderWidth} + } + this.select = function(frameElement) { + var selectorElement = document.getElementById(imageSelectorId), position; + if(!selectorElement) { + selectorElement = createSelectorElement() + } + hasSelection = true; + position = getPosition(frameElement, (selectorElement.parentNode)); + selectorElement.style.display = "block"; + selectorElement.style.left = position.left + "px"; + selectorElement.style.top = position.top + "px"; + selectorElement.style.width = frameElement.getAttributeNS(svgns, "width"); + selectorElement.style.height = frameElement.getAttributeNS(svgns, "height") + }; + this.clearSelection = function() { + var selectorElement; + if(hasSelection) { + selectorElement = document.getElementById(imageSelectorId); + if(selectorElement) { + selectorElement.style.display = "none" } } - function refreshOdf(suppressEvent) { - function callback() { - clear(element); - element.style.display = "inline-block"; - var odfnode = odfcontainer.rootElement; - element.ownerDocument.importNode(odfnode, true); - formatting.setOdfContainer(odfcontainer); - handleFonts(odfcontainer, fontcss); - handleStyles(odfcontainer, formatting, stylesxmlcss); - handleContent(odfcontainer, odfnode); - handleAnnotations(odfnode); - if(!suppressEvent) { - fireEvent("statereadychange", [odfcontainer]) - } + hasSelection = false + }; + this.isSelectorElement = function(node) { + var selectorElement = document.getElementById(imageSelectorId); + if(!selectorElement) { + return false + } + return node === selectorElement || node.parentNode === selectorElement + } +}; +runtime.loadClass("odf.OdfCanvas"); +odf.CommandLineTools = function CommandLineTools() { + this.roundTrip = function(inputfilepath, outputfilepath, callback) { + function onready(odfcontainer) { + if(odfcontainer.state === odf.OdfContainer.INVALID) { + return callback("Document " + inputfilepath + " is invalid.") } if(odfcontainer.state === odf.OdfContainer.DONE) { - callback() - }else { - runtime.log("WARNING: refreshOdf called but ODF was not DONE."); - runtime.setTimeout(function later_cb() { - if(odfcontainer.state === odf.OdfContainer.DONE) { - callback() - }else { - runtime.log("will be back later..."); - runtime.setTimeout(later_cb, 500) - } - }, 100) - } - } - this.refreshCSS = function() { - handleStyles(odfcontainer, formatting, stylesxmlcss); - fixContainerSize() - }; - this.refreshSize = function() { - fixContainerSize() - }; - this.odfContainer = function() { - return odfcontainer - }; - this.slidevisibilitycss = function() { - return pageSwitcher.css - }; - this.setOdfContainer = function(container, suppressEvent) { - odfcontainer = container; - refreshOdf(suppressEvent === true) - }; - function load(url) { - loadingQueue.clearQueue(); - element.innerHTML = runtime.tr("Loading") + " " + url + "..."; - element.removeAttribute("style"); - odfcontainer = new odf.OdfContainer(url, function(container) { - odfcontainer = container; - refreshOdf(false) - }) - } - this["load"] = load; - this.load = load; - this.save = function(callback) { - odfcontainer.save(callback) - }; - this.addListener = function(eventName, handler) { - switch(eventName) { - case "selectionchange": - selectionWatcher.addListener(eventName, handler); - break; - case "click": - listenEvent(element, eventName, handler); - break; - default: - addEventListener(eventName, handler); - break - } - }; - this.getFormatting = function() { - return formatting - }; - this.getAnnotationViewManager = function() { - return annotationViewManager - }; - this.refreshAnnotations = function() { - handleAnnotations(odfcontainer.rootElement) - }; - this.rerenderAnnotations = function() { - if(annotationViewManager) { - annotationViewManager.rerenderAnnotations() - } - }; - this.getSizer = function() { - return sizer - }; - this.enableAnnotations = function(allow) { - if(allow !== allowAnnotations) { - allowAnnotations = allow; - if(odfcontainer) { - handleAnnotations(odfcontainer.rootElement) - } - } - }; - this.addAnnotation = function(annotation) { - if(annotationViewManager) { - annotationViewManager.addAnnotation(annotation) - } - }; - this.forgetAnnotations = function() { - if(annotationViewManager) { - annotationViewManager.forgetAnnotations() - } - }; - this.setZoomLevel = function(zoom) { - zoomLevel = zoom; - fixContainerSize() - }; - this.getZoomLevel = function() { - return zoomLevel - }; - this.fitToContainingElement = function(width, height) { - var realWidth = element.offsetWidth / zoomLevel, realHeight = element.offsetHeight / zoomLevel; - zoomLevel = width / realWidth; - if(height / realHeight < zoomLevel) { - zoomLevel = height / realHeight - } - fixContainerSize() - }; - this.fitToWidth = function(width) { - var realWidth = element.offsetWidth / zoomLevel; - zoomLevel = width / realWidth; - fixContainerSize() - }; - this.fitSmart = function(width, height) { - var realWidth, realHeight, newScale; - realWidth = element.offsetWidth / zoomLevel; - realHeight = element.offsetHeight / zoomLevel; - newScale = width / realWidth; - if(height !== undefined) { - if(height / realHeight < newScale) { - newScale = height / realHeight - } - } - zoomLevel = Math.min(1, newScale); - fixContainerSize() - }; - this.fitToHeight = function(height) { - var realHeight = element.offsetHeight / zoomLevel; - zoomLevel = height / realHeight; - fixContainerSize() - }; - this.showFirstPage = function() { - pageSwitcher.showFirstPage() - }; - this.showNextPage = function() { - pageSwitcher.showNextPage() - }; - this.showPreviousPage = function() { - pageSwitcher.showPreviousPage() - }; - this.showPage = function(n) { - pageSwitcher.showPage(n); - fixContainerSize() - }; - this.getElement = function() { - return element - }; - this.addCssForFrameWithImage = function(frame) { - var frameName = frame.getAttributeNS(drawns, "name"); - setDrawElementPosition(frameName, frame, positioncss.sheet); - setImage(frameName + "img", odfcontainer, (frame.firstChild), positioncss.sheet) - }; - this.destroy = function(callback) { - var head = doc.getElementsByTagName("head")[0]; - if(annotationsPane && annotationsPane.parentNode) { - annotationsPane.parentNode.removeChild(annotationsPane) - } - if(sizer) { - element.removeChild(sizer) - } - head.removeChild(webodfcss); - head.removeChild(fontcss); - head.removeChild(stylesxmlcss); - head.removeChild(positioncss); - selectionWatcher.destroy(function(err) { - if(err) { - callback(err) - }else { - pageSwitcher.destroy(callback) - } - }) - }; - function init() { - webodfcss = addWebODFStyleSheet(doc); - pageSwitcher = new PageSwitcher(addStyleSheet(doc)); - fontcss = addStyleSheet(doc); - stylesxmlcss = addStyleSheet(doc); - positioncss = addStyleSheet(doc) - } - init() - }; - return odf.OdfCanvas -}(); -runtime.loadClass("odf.OdfCanvas"); -odf.CommandLineTools = function CommandLineTools() { - this.roundTrip = function(inputfilepath, outputfilepath, callback) { - function onready(odfcontainer) { - if(odfcontainer.state === odf.OdfContainer.INVALID) { - return callback("Document " + inputfilepath + " is invalid.") - } - if(odfcontainer.state === odf.OdfContainer.DONE) { - odfcontainer.saveAs(outputfilepath, function(err) { - callback(err) - }) + odfcontainer.saveAs(outputfilepath, function(err) { + callback(err) + }) }else { callback("Document was not completely loaded.") } @@ -10377,161 +10318,6 @@ ops.Member = function Member(memberId, properties) { } init() }; -/* - - Copyright (C) 2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.Server = function Server() { -}; -ops.Server.prototype.connect = function(timeout, cb) { -}; -ops.Server.prototype.networkStatus = function() { -}; -ops.Server.prototype.login = function(login, password, successCb, failCb) { -}; -ops.Server.prototype.joinSession = function(userId, sessionId, successCb, failCb) { -}; -ops.Server.prototype.leaveSession = function(sessionId, memberId, successCb, failCb) { -}; -ops.Server.prototype.getGenesisUrl = function(sessionId) { -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.Operation = function Operation() { -}; -ops.Operation.prototype.init = function(data) { -}; -ops.Operation.prototype.isEdit; -ops.Operation.prototype.execute = function(odtDocument) { -}; -ops.Operation.prototype.spec = function() { -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpAddCursor = function OpAddCursor() { - var memberid, timestamp; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp - }; - this.isEdit = false; - this.execute = function(odtDocument) { - var cursor = odtDocument.getCursor(memberid); - if(cursor) { - return false - } - cursor = new ops.OdtCursor(memberid, odtDocument); - odtDocument.addCursor(cursor); - odtDocument.emit(ops.OdtDocument.signalCursorAdded, cursor); - return true - }; - this.spec = function() { - return{optype:"AddCursor", memberid:memberid, timestamp:timestamp} - } -}; /* Copyright (C) 2012-2013 KO GmbH @@ -10570,1429 +10356,708 @@ ops.OpAddCursor = function OpAddCursor() { @source: https://github.com/kogmbh/WebODF/ */ runtime.loadClass("core.DomUtils"); -runtime.loadClass("odf.Namespaces"); +runtime.loadClass("core.PositionFilter"); runtime.loadClass("odf.OdfUtils"); -gui.StyleHelper = function StyleHelper(formatting) { - var domUtils = new core.DomUtils, odfUtils = new odf.OdfUtils, textns = odf.Namespaces.textns; - function getAppliedStyles(range) { - var container, nodes; - if(range.collapsed) { - container = range.startContainer; - if(container.hasChildNodes() && range.startOffset < container.childNodes.length) { - container = container.childNodes[range.startOffset] +(function() { + var nextNodeId = 0, PREVIOUS_STEP = 0, NEXT_STEP = 1; + function StepsCache(rootNode, filter, bucketSize) { + var coordinatens = "urn:webodf:names:steps", stepToDomPoint = {}, nodeToBookmark = {}, odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils, basePoint, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; + function ParagraphBookmark(steps, paragraphNode) { + this.steps = steps; + this.node = paragraphNode; + function positionInContainer(node) { + var position = 0; + while(node && node.previousSibling) { + position += 1; + node = node.previousSibling + } + return position } - nodes = [container] - }else { - nodes = odfUtils.getTextNodes(range, true) - } - return formatting.getAppliedStyles(nodes) - } - this.getAppliedStyles = getAppliedStyles; - this.applyStyle = function(memberId, range, info) { - var nextTextNodes = domUtils.splitBoundaries(range), textNodes = odfUtils.getTextNodes(range, false), limits; - limits = {startContainer:range.startContainer, startOffset:range.startOffset, endContainer:range.endContainer, endOffset:range.endOffset}; - formatting.applyStyle(memberId, textNodes, limits, info); - nextTextNodes.forEach(domUtils.normalizeTextNodes) - }; - function hasTextPropertyValue(appliedStyles, propertyName, propertyValue) { - var hasOtherValue = true, properties, i; - for(i = 0;i < appliedStyles.length;i += 1) { - properties = appliedStyles[i]["style:text-properties"]; - hasOtherValue = !properties || properties[propertyName] !== propertyValue; - if(hasOtherValue) { - break + this.setIteratorPosition = function(iterator) { + iterator.setUnfilteredPosition(paragraphNode.parentNode, positionInContainer(paragraphNode)); + do { + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + break + } + }while(iterator.nextPosition()) } } - return!hasOtherValue - } - this.isBold = function(appliedStyles) { - return hasTextPropertyValue(appliedStyles, "fo:font-weight", "bold") - }; - this.isItalic = function(appliedStyles) { - return hasTextPropertyValue(appliedStyles, "fo:font-style", "italic") - }; - this.hasUnderline = function(appliedStyles) { - return hasTextPropertyValue(appliedStyles, "style:text-underline-style", "solid") - }; - this.hasStrikeThrough = function(appliedStyles) { - return hasTextPropertyValue(appliedStyles, "style:text-line-through-style", "solid") - }; - function hasParagraphPropertyValue(range, propertyName, propertyValues) { - var nodes = odfUtils.getParagraphElements(range), isStyleChecked = {}, isDefaultParagraphStyleChecked = false, paragraphStyleName, paragraphStyleElement, paragraphStyleAttributes, properties; - function pickDefaultParagraphStyleElement() { - isDefaultParagraphStyleChecked = true; - paragraphStyleElement = formatting.getDefaultStyleElement("paragraph"); - if(!paragraphStyleElement) { - paragraphStyleElement = null + function RootBookmark(steps, rootNode) { + this.steps = steps; + this.node = rootNode; + this.setIteratorPosition = function(iterator) { + iterator.setUnfilteredPosition(rootNode, 0); + do { + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + break + } + }while(iterator.nextPosition()) } } - while(nodes.length > 0) { - paragraphStyleName = nodes[0].getAttributeNS(textns, "style-name"); - if(paragraphStyleName) { - if(!isStyleChecked[paragraphStyleName]) { - paragraphStyleElement = formatting.getStyleElement(paragraphStyleName, "paragraph"); - isStyleChecked[paragraphStyleName] = true; - if(!paragraphStyleElement && !isDefaultParagraphStyleChecked) { - pickDefaultParagraphStyleElement() - } - } + function getBucket(steps) { + return Math.floor(steps / bucketSize) * bucketSize + } + function getDestinationBucket(steps) { + return Math.ceil(steps / bucketSize) * bucketSize + } + function clearNodeId(node) { + node.removeAttributeNS(coordinatens, "nodeId") + } + function getNodeId(node) { + return node.nodeType === Node.ELEMENT_NODE && node.getAttributeNS(coordinatens, "nodeId") + } + function setNodeId(node) { + var nodeId = nextNodeId; + node.setAttributeNS(coordinatens, "nodeId", nodeId.toString()); + nextNodeId += 1; + return nodeId + } + function isValidBookmarkForNode(node, bookmark) { + return bookmark.node === node + } + function getNodeBookmark(node, steps) { + var nodeId = getNodeId(node) || setNodeId(node), existingBookmark; + existingBookmark = nodeToBookmark[nodeId]; + if(!existingBookmark) { + existingBookmark = nodeToBookmark[nodeId] = new ParagraphBookmark(steps, node) }else { - if(!isDefaultParagraphStyleChecked) { - pickDefaultParagraphStyleElement() + if(!isValidBookmarkForNode(node, existingBookmark)) { + runtime.log("Cloned node detected. Creating new bookmark"); + nodeId = setNodeId(node); + existingBookmark = nodeToBookmark[nodeId] = new ParagraphBookmark(steps, node) }else { - paragraphStyleElement = undefined + existingBookmark.steps = steps } } - if(paragraphStyleElement !== undefined) { - if(paragraphStyleElement === null) { - paragraphStyleAttributes = formatting.getSystemDefaultStyleAttributes("paragraph") - }else { - paragraphStyleAttributes = formatting.getInheritedStyleAttributes((paragraphStyleElement), true) + return existingBookmark + } + function isFirstPositionInParagraph(node, offset) { + return offset === 0 && odfUtils.isParagraph(node) + } + this.updateCache = function(steps, node, offset, isWalkable) { + var stablePoint, cacheBucket, existingCachePoint, bookmark; + if(isFirstPositionInParagraph(node, offset)) { + stablePoint = true; + if(!isWalkable) { + steps += 1 } - properties = paragraphStyleAttributes["style:paragraph-properties"]; - if(properties && propertyValues.indexOf(properties[propertyName]) === -1) { - return false + }else { + if(node.hasChildNodes() && node.childNodes[offset]) { + node = node.childNodes[offset]; + offset = 0; + stablePoint = isFirstPositionInParagraph(node, offset); + if(stablePoint) { + steps += 1 + } } } - nodes.pop() - } - return true - } - this.isAlignedLeft = function(range) { - return hasParagraphPropertyValue(range, "fo:text-align", ["left", "start"]) - }; - this.isAlignedCenter = function(range) { - return hasParagraphPropertyValue(range, "fo:text-align", ["center"]) - }; - this.isAlignedRight = function(range) { - return hasParagraphPropertyValue(range, "fo:text-align", ["right", "end"]) - }; - this.isAlignedJustified = function(range) { - return hasParagraphPropertyValue(range, "fo:text-align", ["justify"]) - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("gui.StyleHelper"); -runtime.loadClass("odf.OdfUtils"); -ops.OpApplyDirectStyling = function OpApplyDirectStyling() { - var memberid, timestamp, position, length, setProperties, odfUtils = new odf.OdfUtils; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - position = parseInt(data.position, 10); - length = parseInt(data.length, 10); - setProperties = data.setProperties - }; - this.isEdit = true; - function getRange(odtDocument) { - var point1 = length >= 0 ? position : position + length, point2 = length >= 0 ? position + length : position, p1 = odtDocument.getIteratorAtPosition(point1), p2 = length ? odtDocument.getIteratorAtPosition(point2) : p1, range = odtDocument.getDOM().createRange(); - range.setStart(p1.container(), p1.unfilteredDomOffset()); - range.setEnd(p2.container(), p2.unfilteredDomOffset()); - return range - } - this.execute = function(odtDocument) { - var range = getRange(odtDocument), impactedParagraphs = odfUtils.getImpactedParagraphs(range), styleHelper = new gui.StyleHelper(odtDocument.getFormatting()); - styleHelper.applyStyle(memberid, range, setProperties); - range.detach(); - odtDocument.getOdfCanvas().refreshCSS(); - odtDocument.fixCursorPositions(); - impactedParagraphs.forEach(function(n) { - odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:n, memberId:memberid, timeStamp:timestamp}) - }); - odtDocument.getOdfCanvas().rerenderAnnotations(); - return true - }; - this.spec = function() { - return{optype:"ApplyDirectStyling", memberid:memberid, timestamp:timestamp, position:position, length:length, setProperties:setProperties} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpRemoveCursor = function OpRemoveCursor() { - var memberid, timestamp; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp - }; - this.isEdit = false; - this.execute = function(odtDocument) { - if(!odtDocument.removeCursor(memberid)) { - return false - } - return true - }; - this.spec = function() { - return{optype:"RemoveCursor", memberid:memberid, timestamp:timestamp} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpMoveCursor = function OpMoveCursor() { - var memberid, timestamp, position, length, selectionType; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - position = data.position; - length = data.length || 0; - selectionType = data.selectionType || ops.OdtCursor.RangeSelection - }; - this.isEdit = false; - this.execute = function(odtDocument) { - var cursor = odtDocument.getCursor(memberid), selectedRange; - if(!cursor) { - return false - } - selectedRange = odtDocument.convertCursorToDomRange(position, length); - cursor.setSelectedRange(selectedRange, length >= 0); - cursor.setSelectionType(selectionType); - odtDocument.emit(ops.OdtDocument.signalCursorMoved, cursor); - return true - }; - this.spec = function() { - return{optype:"MoveCursor", memberid:memberid, timestamp:timestamp, position:position, length:length, selectionType:selectionType} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpSetBlob = function OpSetBlob() { - var memberid, timestamp, filename, mimetype, content; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - filename = data.filename; - mimetype = data.mimetype; - content = data.content - }; - this.isEdit = true; - this.execute = function(odtDocument) { - odtDocument.getOdfCanvas().odfContainer().setBlob(filename, mimetype, content); - return true - }; - this.spec = function() { - return{optype:"SetBlob", memberid:memberid, timestamp:timestamp, filename:filename, mimetype:mimetype, content:content} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpRemoveBlob = function OpRemoveBlob() { - var memberid, timestamp, filename; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - filename = data.filename - }; - this.isEdit = true; - this.execute = function(odtDocument) { - odtDocument.getOdfCanvas().odfContainer().removeBlob(filename); - return true - }; - this.spec = function() { - return{optype:"RemoveBlob", memberid:memberid, timestamp:timestamp, filename:filename} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpInsertImage = function OpInsertImage() { - var memberid, timestamp, position, filename, frameWidth, frameHeight, frameStyleName, frameName, drawns = odf.Namespaces.drawns, svgns = odf.Namespaces.svgns, textns = odf.Namespaces.textns, xlinkns = odf.Namespaces.xlinkns; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - position = data.position; - filename = data.filename; - frameWidth = data.frameWidth; - frameHeight = data.frameHeight; - frameStyleName = data.frameStyleName; - frameName = data.frameName - }; - this.isEdit = true; - function createFrameElement(document) { - var imageNode = document.createElementNS(drawns, "draw:image"), frameNode = document.createElementNS(drawns, "draw:frame"); - imageNode.setAttributeNS(xlinkns, "xlink:href", filename); - imageNode.setAttributeNS(xlinkns, "xlink:type", "simple"); - imageNode.setAttributeNS(xlinkns, "xlink:show", "embed"); - imageNode.setAttributeNS(xlinkns, "xlink:actuate", "onLoad"); - frameNode.setAttributeNS(drawns, "draw:style-name", frameStyleName); - frameNode.setAttributeNS(drawns, "draw:name", frameName); - frameNode.setAttributeNS(textns, "text:anchor-type", "as-char"); - frameNode.setAttributeNS(svgns, "svg:width", frameWidth); - frameNode.setAttributeNS(svgns, "svg:height", frameHeight); - frameNode.appendChild(imageNode); - return frameNode - } - this.execute = function(odtDocument) { - var odfCanvas = odtDocument.getOdfCanvas(), domPosition = odtDocument.getTextNodeAtStep(position, memberid), textNode, refNode, paragraphElement, frameElement; - if(!domPosition) { - return false - } - textNode = domPosition.textNode; - paragraphElement = odtDocument.getParagraphElement(textNode); - refNode = domPosition.offset !== textNode.length ? textNode.splitText(domPosition.offset) : textNode.nextSibling; - frameElement = createFrameElement(odtDocument.getDOM()); - textNode.parentNode.insertBefore(frameElement, refNode); - odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:1}); - if(textNode.length === 0) { - textNode.parentNode.removeChild(textNode) - } - odfCanvas.addCssForFrameWithImage(frameElement); - odfCanvas.refreshCSS(); - odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:paragraphElement, memberId:memberid, timeStamp:timestamp}); - odfCanvas.rerenderAnnotations(); - return true - }; - this.spec = function() { - return{optype:"InsertImage", memberid:memberid, timestamp:timestamp, filename:filename, position:position, frameWidth:frameWidth, frameHeight:frameHeight, frameStyleName:frameStyleName, frameName:frameName} - } -}; -/* - - Copyright (C) 2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpInsertTable = function OpInsertTable() { - var memberid, timestamp, initialRows, initialColumns, position, tableName, tableStyleName, tableColumnStyleName, tableCellStyleMatrix, tablens = "urn:oasis:names:tc:opendocument:xmlns:table:1.0", textns = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - position = data.position; - initialRows = data.initialRows; - initialColumns = data.initialColumns; - tableName = data.tableName; - tableStyleName = data.tableStyleName; - tableColumnStyleName = data.tableColumnStyleName; - tableCellStyleMatrix = data.tableCellStyleMatrix - }; - this.isEdit = true; - function getCellStyleName(row, column) { - var rowStyles; - if(tableCellStyleMatrix.length === 1) { - rowStyles = tableCellStyleMatrix[0] - }else { - if(tableCellStyleMatrix.length === 3) { - switch(row) { - case 0: - rowStyles = tableCellStyleMatrix[0]; - break; - case initialRows - 1: - rowStyles = tableCellStyleMatrix[2]; - break; - default: - rowStyles = tableCellStyleMatrix[1]; - break - } - }else { - rowStyles = tableCellStyleMatrix[row] - } - } - if(rowStyles.length === 1) { - return rowStyles[0] - } - if(rowStyles.length === 3) { - switch(column) { - case 0: - return rowStyles[0]; - case initialColumns - 1: - return rowStyles[2]; - default: - return rowStyles[1] - } - } - return rowStyles[column] - } - function createTableNode(document) { - var tableNode = document.createElementNS(tablens, "table:table"), columns = document.createElementNS(tablens, "table:table-column"), row, cell, paragraph, rowCounter, columnCounter, cellStyleName; - if(tableStyleName) { - tableNode.setAttributeNS(tablens, "table:style-name", tableStyleName) - } - if(tableName) { - tableNode.setAttributeNS(tablens, "table:name", tableName) - } - columns.setAttributeNS(tablens, "table:number-columns-repeated", initialColumns); - if(tableColumnStyleName) { - columns.setAttributeNS(tablens, "table:style-name", tableColumnStyleName) - } - tableNode.appendChild(columns); - for(rowCounter = 0;rowCounter < initialRows;rowCounter += 1) { - row = document.createElementNS(tablens, "table:table-row"); - for(columnCounter = 0;columnCounter < initialColumns;columnCounter += 1) { - cell = document.createElementNS(tablens, "table:table-cell"); - cellStyleName = getCellStyleName(rowCounter, columnCounter); - if(cellStyleName) { - cell.setAttributeNS(tablens, "table:style-name", cellStyleName) - } - paragraph = document.createElementNS(textns, "text:p"); - cell.appendChild(paragraph); - row.appendChild(cell) - } - tableNode.appendChild(row) - } - return tableNode - } - this.execute = function(odtDocument) { - var domPosition = odtDocument.getTextNodeAtStep(position), rootNode = odtDocument.getRootNode(), previousSibling, tableNode; - if(domPosition) { - tableNode = createTableNode(odtDocument.getDOM()); - previousSibling = odtDocument.getParagraphElement(domPosition.textNode); - rootNode.insertBefore(tableNode, previousSibling.nextSibling); - odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:initialColumns * initialRows + 1}); - odtDocument.getOdfCanvas().refreshSize(); - odtDocument.emit(ops.OdtDocument.signalTableAdded, {tableElement:tableNode, memberId:memberid, timeStamp:timestamp}); - odtDocument.getOdfCanvas().rerenderAnnotations(); - return true - } - return false - }; - this.spec = function() { - return{optype:"InsertTable", memberid:memberid, timestamp:timestamp, position:position, initialRows:initialRows, initialColumns:initialColumns, tableName:tableName, tableStyleName:tableStyleName, tableColumnStyleName:tableColumnStyleName, tableCellStyleMatrix:tableCellStyleMatrix} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpInsertText = function OpInsertText() { - var space = " ", tab = "\t", memberid, timestamp, position, text; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - position = data.position; - text = data.text - }; - this.isEdit = true; - function triggerLayoutInWebkit(textNode) { - var parent = textNode.parentNode, next = textNode.nextSibling; - parent.removeChild(textNode); - parent.insertBefore(textNode, next) - } - function requiresSpaceElement(text, index) { - return text[index] === space && (index === 0 || (index === text.length - 1 || text[index - 1] === space)) - } - this.execute = function(odtDocument) { - var domPosition, previousNode, parentElement, nextNode = null, ownerDocument = odtDocument.getDOM(), paragraphElement, textns = "urn:oasis:names:tc:opendocument:xmlns:text:1.0", toInsertIndex = 0, spaceTag, spaceElement, i; - function insertTextNode(toInsertText) { - parentElement.insertBefore(ownerDocument.createTextNode(toInsertText), nextNode) - } - odtDocument.upgradeWhitespacesAtPosition(position); - domPosition = odtDocument.getTextNodeAtStep(position, memberid); - if(domPosition) { - previousNode = domPosition.textNode; - nextNode = previousNode.nextSibling; - parentElement = previousNode.parentNode; - paragraphElement = odtDocument.getParagraphElement(previousNode); - for(i = 0;i < text.length;i += 1) { - if(requiresSpaceElement(text, i) || text[i] === tab) { - if(toInsertIndex === 0) { - if(domPosition.offset !== previousNode.length) { - nextNode = previousNode.splitText(domPosition.offset) - } - if(0 < i) { - previousNode.appendData(text.substring(0, i)) - } - }else { - if(toInsertIndex < i) { - insertTextNode(text.substring(toInsertIndex, i)) - } - } - toInsertIndex = i + 1; - spaceTag = text[i] === space ? "text:s" : "text:tab"; - spaceElement = ownerDocument.createElementNS(textns, spaceTag); - spaceElement.appendChild(ownerDocument.createTextNode(text[i])); - parentElement.insertBefore(spaceElement, nextNode) - } - } - if(toInsertIndex === 0) { - previousNode.insertData(domPosition.offset, text) - }else { - if(toInsertIndex < text.length) { - insertTextNode(text.substring(toInsertIndex)) - } - } - triggerLayoutInWebkit(previousNode); - if(previousNode.length === 0) { - previousNode.parentNode.removeChild(previousNode) - } - odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:text.length}); - if(position > 0) { - if(position > 1) { - odtDocument.downgradeWhitespacesAtPosition(position - 2) - } - odtDocument.downgradeWhitespacesAtPosition(position - 1) - } - odtDocument.downgradeWhitespacesAtPosition(position); - odtDocument.downgradeWhitespacesAtPosition(position + text.length - 1); - odtDocument.downgradeWhitespacesAtPosition(position + text.length); - odtDocument.getOdfCanvas().refreshSize(); - odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:paragraphElement, memberId:memberid, timeStamp:timestamp}); - odtDocument.getOdfCanvas().rerenderAnnotations(); - return true - } - return false - }; - this.spec = function() { - return{optype:"InsertText", memberid:memberid, timestamp:timestamp, position:position, text:text} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("odf.Namespaces"); -runtime.loadClass("odf.OdfUtils"); -runtime.loadClass("core.DomUtils"); -ops.OpRemoveText = function OpRemoveText() { - var memberid, timestamp, position, length, odfUtils, domUtils, editinfons = "urn:webodf:names:editinfo", odfNodeNamespaceMap = {}; - this.init = function(data) { - runtime.assert(data.length >= 0, "OpRemoveText only supports positive lengths"); - memberid = data.memberid; - timestamp = data.timestamp; - position = parseInt(data.position, 10); - length = parseInt(data.length, 10); - odfUtils = new odf.OdfUtils; - domUtils = new core.DomUtils; - odfNodeNamespaceMap[odf.Namespaces.dbns] = true; - odfNodeNamespaceMap[odf.Namespaces.dcns] = true; - odfNodeNamespaceMap[odf.Namespaces.dr3dns] = true; - odfNodeNamespaceMap[odf.Namespaces.drawns] = true; - odfNodeNamespaceMap[odf.Namespaces.chartns] = true; - odfNodeNamespaceMap[odf.Namespaces.formns] = true; - odfNodeNamespaceMap[odf.Namespaces.numberns] = true; - odfNodeNamespaceMap[odf.Namespaces.officens] = true; - odfNodeNamespaceMap[odf.Namespaces.presentationns] = true; - odfNodeNamespaceMap[odf.Namespaces.stylens] = true; - odfNodeNamespaceMap[odf.Namespaces.svgns] = true; - odfNodeNamespaceMap[odf.Namespaces.tablens] = true; - odfNodeNamespaceMap[odf.Namespaces.textns] = true - }; - this.isEdit = true; - function CollapsingRules(rootNode) { - function isOdfNode(node) { - return odfNodeNamespaceMap.hasOwnProperty(node.namespaceURI) - } - function shouldRemove(node) { - return isOdfNode(node) || (node.localName === "br" && odfUtils.isLineBreak(node.parentNode) || node.nodeType === Node.TEXT_NODE && isOdfNode((node.parentNode))) - } - function isEmpty(node) { - var childNode; - if(odfUtils.isCharacterElement(node)) { - return false + if(stablePoint) { + bookmark = getNodeBookmark(node, steps); + cacheBucket = getDestinationBucket(bookmark.steps); + existingCachePoint = stepToDomPoint[cacheBucket]; + if(!existingCachePoint || bookmark.steps > existingCachePoint.steps) { + stepToDomPoint[cacheBucket] = bookmark + } } - if(node.nodeType === Node.TEXT_NODE) { - return node.textContent.length === 0 + }; + this.setToClosestStep = function(steps, iterator) { + var cacheBucket = getBucket(steps), cachePoint; + while(!cachePoint && cacheBucket !== 0) { + cachePoint = stepToDomPoint[cacheBucket]; + cacheBucket -= bucketSize } - childNode = node.firstChild; - while(childNode) { - if(isOdfNode(childNode) || !isEmpty(childNode)) { - return false + cachePoint = cachePoint || basePoint; + cachePoint.setIteratorPosition(iterator); + return cachePoint.steps + }; + function findBookmarkedAncestor(node, offset) { + var nodeId, bookmark = null; + node = node.childNodes[offset] || node; + while(!bookmark && (node && node !== rootNode)) { + nodeId = getNodeId(node); + if(nodeId) { + bookmark = nodeToBookmark[nodeId]; + if(bookmark && !isValidBookmarkForNode(node, bookmark)) { + runtime.log("Cloned node detected. Creating new bookmark"); + bookmark = null; + clearNodeId(node) + } } - childNode = childNode.nextSibling + node = node.parentNode } - return true - } - this.isEmpty = isEmpty; - function isCollapsibleContainer(node) { - return!odfUtils.isParagraph(node) && (node !== rootNode && isEmpty(node)) + return bookmark } - function mergeChildrenIntoParent(targetNode) { - var parent; - if(targetNode.nodeType === Node.TEXT_NODE) { - parent = targetNode.parentNode; - parent.removeChild(targetNode) + this.setToClosestDomPoint = function(node, offset, iterator) { + var bookmark; + if(node === rootNode && offset === 0) { + bookmark = basePoint }else { - parent = domUtils.removeUnwantedNodes(targetNode, shouldRemove) - } - if(isCollapsibleContainer(parent)) { - return mergeChildrenIntoParent(parent) + if(node === rootNode && offset === rootNode.childNodes.length) { + bookmark = Object.keys(stepToDomPoint).map(function(cacheBucket) { + return stepToDomPoint[cacheBucket] + }).reduce(function(largestBookmark, bookmark) { + return bookmark.steps > largestBookmark.steps ? bookmark : largestBookmark + }, basePoint) + }else { + bookmark = findBookmarkedAncestor(node, offset); + if(!bookmark) { + iterator.setUnfilteredPosition(node, offset); + while(!bookmark && iterator.previousNode()) { + bookmark = findBookmarkedAncestor(iterator.container(), iterator.unfilteredDomOffset()) + } + } + } } - return parent + bookmark = bookmark || basePoint; + bookmark.setIteratorPosition(iterator); + return bookmark.steps + }; + this.updateCacheAtPoint = function(inflectionStep, doUpdate) { + var affectedBookmarks, updatedBuckets = {}; + affectedBookmarks = Object.keys(nodeToBookmark).map(function(nodeId) { + return nodeToBookmark[nodeId] + }).filter(function(bookmark) { + return bookmark.steps > inflectionStep + }); + affectedBookmarks.forEach(function(bookmark) { + var originalCacheBucket = getDestinationBucket(bookmark.steps), newCacheBucket, existingBookmark; + if(domUtils.containsNode(rootNode, bookmark.node)) { + doUpdate(bookmark); + newCacheBucket = getDestinationBucket(bookmark.steps); + existingBookmark = updatedBuckets[newCacheBucket]; + if(!existingBookmark || bookmark.steps > existingBookmark.steps) { + updatedBuckets[newCacheBucket] = bookmark + } + }else { + delete nodeToBookmark[getNodeId(bookmark.node)] + } + if(stepToDomPoint[originalCacheBucket] === bookmark) { + delete stepToDomPoint[originalCacheBucket] + } + }); + Object.keys(updatedBuckets).forEach(function(cacheBucket) { + stepToDomPoint[cacheBucket] = updatedBuckets[cacheBucket] + }) + }; + function init() { + basePoint = new RootBookmark(0, rootNode) } - this.mergeChildrenIntoParent = mergeChildrenIntoParent + init() } - function mergeParagraphs(first, second, collapseRules) { - var child, mergeForward = false, destination = first, source = second, secondParent, insertionPoint = null; - if(collapseRules.isEmpty(first)) { - mergeForward = true; - if(second.parentNode !== first.parentNode) { - secondParent = second.parentNode; - first.parentNode.insertBefore(second, first.nextSibling) + ops.StepsTranslator = function StepsTranslator(getRootNode, newIterator, filter, bucketSize) { + var rootNode = getRootNode(), stepsCache = new StepsCache(rootNode, filter, bucketSize), domUtils = new core.DomUtils, iterator = newIterator(getRootNode()), FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; + function verifyRootNode() { + var currentRootNode = getRootNode(); + if(currentRootNode !== rootNode) { + runtime.log("Undo detected. Resetting steps cache"); + rootNode = currentRootNode; + stepsCache = new StepsCache(rootNode, filter, bucketSize); + iterator = newIterator(rootNode) } - source = first; - destination = second; - insertionPoint = destination.getElementsByTagNameNS(editinfons, "editinfo")[0] || destination.firstChild } - while(source.hasChildNodes()) { - child = mergeForward ? source.lastChild : source.firstChild; - source.removeChild(child); - if(child.localName !== "editinfo") { - destination.insertBefore(child, insertionPoint) + this.convertStepsToDomPoint = function(steps) { + var stepsFromRoot, isWalkable; + if(steps < 0) { + runtime.log("warn", "Requested steps were negative (" + steps + ")"); + steps = 0 } + verifyRootNode(); + stepsFromRoot = stepsCache.setToClosestStep(steps, iterator); + while(stepsFromRoot < steps && iterator.nextPosition()) { + isWalkable = filter.acceptPosition(iterator) === FILTER_ACCEPT; + if(isWalkable) { + stepsFromRoot += 1 + } + stepsCache.updateCache(stepsFromRoot, iterator.container(), iterator.unfilteredDomOffset(), isWalkable) + } + if(stepsFromRoot !== steps) { + runtime.log("warn", "Requested " + steps + " steps but only " + stepsFromRoot + " are available") + } + return{node:iterator.container(), offset:iterator.unfilteredDomOffset()} + }; + function roundToPreferredStep(iterator, roundDirection) { + if(!roundDirection || filter.acceptPosition(iterator) === FILTER_ACCEPT) { + return true + } + while(iterator.previousPosition()) { + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + if(roundDirection(PREVIOUS_STEP, iterator.container(), iterator.unfilteredDomOffset())) { + return true + } + break + } + } + while(iterator.nextPosition()) { + if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { + if(roundDirection(NEXT_STEP, iterator.container(), iterator.unfilteredDomOffset())) { + return true + } + break + } + } + return false + } + this.convertDomPointToSteps = function(node, offset, roundDirection) { + var stepsFromRoot, beforeRoot, destinationNode, destinationOffset, rounding = 0, isWalkable; + verifyRootNode(); + if(!domUtils.containsNode(rootNode, node)) { + beforeRoot = domUtils.comparePoints(rootNode, 0, node, offset) < 0; + node = rootNode; + offset = beforeRoot ? 0 : rootNode.childNodes.length + } + iterator.setUnfilteredPosition(node, offset); + if(!roundToPreferredStep(iterator, roundDirection)) { + iterator.setUnfilteredPosition(node, offset) + } + destinationNode = iterator.container(); + destinationOffset = iterator.unfilteredDomOffset(); + stepsFromRoot = stepsCache.setToClosestDomPoint(destinationNode, destinationOffset, iterator); + if(domUtils.comparePoints(iterator.container(), iterator.unfilteredDomOffset(), destinationNode, destinationOffset) < 0) { + return stepsFromRoot > 0 ? stepsFromRoot - 1 : stepsFromRoot + } + while(!(iterator.container() === destinationNode && iterator.unfilteredDomOffset() === destinationOffset) && iterator.nextPosition()) { + isWalkable = filter.acceptPosition(iterator) === FILTER_ACCEPT; + if(isWalkable) { + stepsFromRoot += 1 + } + stepsCache.updateCache(stepsFromRoot, iterator.container(), iterator.unfilteredDomOffset(), isWalkable) + } + return stepsFromRoot + rounding + }; + this.prime = function() { + var stepsFromRoot, isWalkable; + verifyRootNode(); + stepsFromRoot = stepsCache.setToClosestStep(0, iterator); + while(iterator.nextPosition()) { + isWalkable = filter.acceptPosition(iterator) === FILTER_ACCEPT; + if(isWalkable) { + stepsFromRoot += 1 + } + stepsCache.updateCache(stepsFromRoot, iterator.container(), iterator.unfilteredDomOffset(), isWalkable) + } + }; + this.handleStepsInserted = function(eventArgs) { + verifyRootNode(); + stepsCache.updateCacheAtPoint(eventArgs.position, function(bucket) { + bucket.steps += eventArgs.length + }) + }; + this.handleStepsRemoved = function(eventArgs) { + verifyRootNode(); + stepsCache.updateCacheAtPoint(eventArgs.position, function(bucket) { + bucket.steps -= eventArgs.length; + if(bucket.steps < 0) { + bucket.steps = 0 + } + }) } - if(secondParent && collapseRules.isEmpty(secondParent)) { - collapseRules.mergeChildrenIntoParent(secondParent) - } - collapseRules.mergeChildrenIntoParent(source); - return destination - } - this.execute = function(odtDocument) { - var paragraphElement, destinationParagraph, range, textNodes, paragraphs, cursor = odtDocument.getCursor(memberid), collapseRules = new CollapsingRules(odtDocument.getRootNode()); - odtDocument.upgradeWhitespacesAtPosition(position); - odtDocument.upgradeWhitespacesAtPosition(position + length); - range = odtDocument.convertCursorToDomRange(position, length); - domUtils.splitBoundaries(range); - paragraphElement = odtDocument.getParagraphElement(range.startContainer); - textNodes = odfUtils.getTextElements(range, false, true); - paragraphs = odfUtils.getParagraphElements(range); - range.detach(); - textNodes.forEach(function(element) { - collapseRules.mergeChildrenIntoParent(element) - }); - destinationParagraph = paragraphs.reduce(function(destination, paragraph) { - return mergeParagraphs(destination, paragraph, collapseRules) - }); - odtDocument.emit(ops.OdtDocument.signalStepsRemoved, {position:position, length:length}); - odtDocument.downgradeWhitespacesAtPosition(position); - odtDocument.fixCursorPositions(); - odtDocument.getOdfCanvas().refreshSize(); - odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:destinationParagraph || paragraphElement, memberId:memberid, timeStamp:timestamp}); - if(cursor) { - cursor.resetSelectionType(); - odtDocument.emit(ops.OdtDocument.signalCursorMoved, cursor) - } - odtDocument.getOdfCanvas().rerenderAnnotations(); - return true - }; - this.spec = function() { - return{optype:"RemoveText", memberid:memberid, timestamp:timestamp, position:position, length:length} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpSplitParagraph = function OpSplitParagraph() { - var memberid, timestamp, position, odfUtils; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - position = data.position; - odfUtils = new odf.OdfUtils }; - this.isEdit = true; - this.execute = function(odtDocument) { - var domPosition, paragraphNode, targetNode, node, splitNode, splitChildNode, keptChildNode; - odtDocument.upgradeWhitespacesAtPosition(position); - domPosition = odtDocument.getTextNodeAtStep(position, memberid); - if(!domPosition) { - return false + ops.StepsTranslator.PREVIOUS_STEP = PREVIOUS_STEP; + ops.StepsTranslator.NEXT_STEP = NEXT_STEP; + return ops.StepsTranslator +})(); +xmldom.RNG = {}; +xmldom.RNG.Name; +xmldom.RNG.Attr; +xmldom.RNG.Element; +xmldom.RelaxNGParser = function RelaxNGParser() { + var self = this, rngns = "http://relaxng.org/ns/structure/1.0", xmlnsns = "http://www.w3.org/2000/xmlns/", start, nsmap = {"http://www.w3.org/XML/1998/namespace":"xml"}, parse; + function RelaxNGParseError(error, context) { + this.message = function() { + if(context) { + error += context.nodeType === 1 ? " Element " : " Node "; + error += context.nodeName; + if(context.nodeValue) { + error += " with value '" + context.nodeValue + "'" + } + error += "." + } + return error } - paragraphNode = odtDocument.getParagraphElement(domPosition.textNode); - if(!paragraphNode) { - return false + } + function splitToDuos(e) { + if(e.e.length <= 2) { + return e } - if(odfUtils.isListItem(paragraphNode.parentNode)) { - targetNode = paragraphNode.parentNode + var o = {name:e.name, e:e.e.slice(0, 2)}; + return splitToDuos({name:e.name, e:[o].concat(e.e.slice(2))}) + } + function splitQName(name) { + var r = name.split(":", 2), prefix = "", i; + if(r.length === 1) { + r = ["", r[0]] }else { - targetNode = paragraphNode + prefix = r[0] } - if(domPosition.offset === 0) { - keptChildNode = domPosition.textNode.previousSibling; - splitChildNode = null - }else { - keptChildNode = domPosition.textNode; - if(domPosition.offset >= domPosition.textNode.length) { - splitChildNode = null - }else { - splitChildNode = (domPosition.textNode.splitText(domPosition.offset)) + for(i in nsmap) { + if(nsmap[i] === prefix) { + r[0] = i } } - node = domPosition.textNode; - while(node !== targetNode) { - node = node.parentNode; - splitNode = node.cloneNode(false); - if(splitChildNode) { - splitNode.appendChild(splitChildNode) - } - if(keptChildNode) { - while(keptChildNode && keptChildNode.nextSibling) { - splitNode.appendChild(keptChildNode.nextSibling) + return r + } + function splitQNames(def) { + var i, l = def.names ? def.names.length : 0, name, localnames = [], namespaces = []; + for(i = 0;i < l;i += 1) { + name = splitQName(def.names[i]); + namespaces[i] = name[0]; + localnames[i] = name[1] + } + def.localnames = localnames; + def.namespaces = namespaces + } + function trim(str) { + str = str.replace(/^\s\s*/, ""); + var ws = /\s/, i = str.length - 1; + while(ws.test(str.charAt(i))) { + i -= 1 + } + return str.slice(0, i + 1) + } + function copyAttributes(atts, name, names) { + var a = {}, i, att; + for(i = 0;atts && i < atts.length;i += 1) { + att = (atts.item(i)); + if(!att.namespaceURI) { + if(att.localName === "name" && (name === "element" || name === "attribute")) { + names.push(att.value) + } + if(att.localName === "name" || (att.localName === "combine" || att.localName === "type")) { + att.value = trim(att.value) } + a[att.localName] = att.value }else { - while(node.firstChild) { - splitNode.appendChild(node.firstChild) + if(att.namespaceURI === xmlnsns) { + nsmap[att.value] = att.localName } } - node.parentNode.insertBefore(splitNode, node.nextSibling); - keptChildNode = node; - splitChildNode = splitNode } - if(odfUtils.isListItem(splitChildNode)) { - splitChildNode = splitChildNode.childNodes[0] + return a + } + function parseChildren(c, e, elements, names) { + var text = "", ce; + while(c) { + if(c.nodeType === Node.ELEMENT_NODE && c.namespaceURI === rngns) { + ce = parse((c), elements, e); + if(ce) { + if(ce.name === "name") { + names.push(nsmap[ce.a.ns] + ":" + ce.text); + e.push(ce) + }else { + if(ce.name === "choice" && (ce.names && ce.names.length)) { + names = names.concat(ce.names); + delete ce.names; + e.push(ce) + }else { + e.push(ce) + } + } + } + }else { + if(c.nodeType === Node.TEXT_NODE) { + text += c.nodeValue + } + } + c = c.nextSibling } - if(domPosition.textNode.length === 0) { - domPosition.textNode.parentNode.removeChild(domPosition.textNode) + return text + } + function combineDefines(combine, name, e, siblings) { + var i, ce; + for(i = 0;siblings && i < siblings.length;i += 1) { + ce = siblings[i]; + if(ce.name === "define" && (ce.a && ce.a.name === name)) { + ce.e = [{name:combine, e:ce.e.concat(e)}]; + return ce + } } - odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:1}); - odtDocument.fixCursorPositions(); - odtDocument.getOdfCanvas().refreshSize(); - odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:paragraphNode, memberId:memberid, timeStamp:timestamp}); - odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:splitChildNode, memberId:memberid, timeStamp:timestamp}); - odtDocument.getOdfCanvas().rerenderAnnotations(); - return true - }; - this.spec = function() { - return{optype:"SplitParagraph", memberid:memberid, timestamp:timestamp, position:position} + return null } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpSetParagraphStyle = function OpSetParagraphStyle() { - var memberid, timestamp, position, styleName, textns = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - position = data.position; - styleName = data.styleName - }; - this.isEdit = true; - this.execute = function(odtDocument) { - var iterator, paragraphNode; - iterator = odtDocument.getIteratorAtPosition(position); - paragraphNode = odtDocument.getParagraphElement(iterator.container()); - if(paragraphNode) { - if(styleName !== "") { - paragraphNode.setAttributeNS(textns, "text:style-name", styleName) - }else { - paragraphNode.removeAttributeNS(textns, "style-name") + parse = function parse(element, elements, siblings) { + var e = [], a, ce, i, text, name = element.localName, names = []; + a = copyAttributes(element.attributes, name, names); + a.combine = a.combine || undefined; + text = parseChildren(element.firstChild, e, elements, names); + if(name !== "value" && name !== "param") { + text = /^\s*([\s\S]*\S)?\s*$/.exec(text)[1] + } + if(name === "value" && a.type === undefined) { + a.type = "token"; + a.datatypeLibrary = "" + } + if((name === "attribute" || name === "element") && a.name !== undefined) { + i = splitQName(a.name); + e = [{name:"name", text:i[1], a:{ns:i[0]}}].concat(e); + delete a.name + } + if(name === "name" || (name === "nsName" || name === "value")) { + if(a.ns === undefined) { + a.ns = "" } - odtDocument.getOdfCanvas().refreshSize(); - odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:paragraphNode, timeStamp:timestamp, memberId:memberid}); - odtDocument.getOdfCanvas().rerenderAnnotations(); - return true + }else { + delete a.ns } - return false - }; - this.spec = function() { - return{optype:"SetParagraphStyle", memberid:memberid, timestamp:timestamp, position:position, styleName:styleName} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("odf.Namespaces"); -ops.OpUpdateParagraphStyle = function OpUpdateParagraphStyle() { - var memberid, timestamp, styleName, setProperties, removedProperties, paragraphPropertiesName = "style:paragraph-properties", textPropertiesName = "style:text-properties", stylens = odf.Namespaces.stylens; - function removedAttributesFromStyleNode(node, removedAttributeNames) { - var i, attributeNameParts, attributeNameList = removedAttributeNames ? removedAttributeNames.split(",") : []; - for(i = 0;i < attributeNameList.length;i += 1) { - attributeNameParts = attributeNameList[i].split(":"); - node.removeAttributeNS(odf.Namespaces.resolvePrefix(attributeNameParts[0]), attributeNameParts[1]) + if(name === "name") { + i = splitQName(text); + a.ns = i[0]; + text = i[1] } - } - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - styleName = data.styleName; - setProperties = data.setProperties; - removedProperties = data.removedProperties - }; - this.isEdit = true; - this.execute = function(odtDocument) { - var formatting = odtDocument.getFormatting(), styleNode, paragraphPropertiesNode, textPropertiesNode; - if(styleName !== "") { - styleNode = odtDocument.getParagraphStyleElement(styleName) + if(e.length > 1 && (name === "define" || (name === "oneOrMore" || (name === "zeroOrMore" || (name === "optional" || (name === "list" || name === "mixed")))))) { + e = [{name:"group", e:splitToDuos({name:"group", e:e}).e}] + } + if(e.length > 2 && name === "element") { + e = [e[0]].concat({name:"group", e:splitToDuos({name:"group", e:e.slice(1)}).e}) + } + if(e.length === 1 && name === "attribute") { + e.push({name:"text", text:text}) + } + if(e.length === 1 && (name === "choice" || (name === "group" || name === "interleave"))) { + name = e[0].name; + names = e[0].names; + a = e[0].a; + text = e[0].text; + e = e[0].e }else { - styleNode = formatting.getDefaultStyleElement("paragraph") + if(e.length > 2 && (name === "choice" || (name === "group" || name === "interleave"))) { + e = splitToDuos({name:name, e:e}).e + } } - if(styleNode) { - paragraphPropertiesNode = styleNode.getElementsByTagNameNS(stylens, "paragraph-properties")[0]; - textPropertiesNode = styleNode.getElementsByTagNameNS(stylens, "text-properties")[0]; - if(setProperties) { - formatting.updateStyle(styleNode, setProperties) + if(name === "mixed") { + name = "interleave"; + e = [e[0], {name:"text"}] + } + if(name === "optional") { + name = "choice"; + e = [e[0], {name:"empty"}] + } + if(name === "zeroOrMore") { + name = "choice"; + e = [{name:"oneOrMore", e:[e[0]]}, {name:"empty"}] + } + if(name === "define" && a.combine) { + ce = combineDefines(a.combine, a.name, e, siblings); + if(ce) { + return null } - if(removedProperties) { - if(removedProperties[paragraphPropertiesName]) { - removedAttributesFromStyleNode(paragraphPropertiesNode, removedProperties[paragraphPropertiesName].attributes); - if(paragraphPropertiesNode.attributes.length === 0) { - styleNode.removeChild(paragraphPropertiesNode) - } + } + ce = {name:name}; + if(e && e.length > 0) { + ce.e = e + } + for(i in a) { + if(a.hasOwnProperty(i)) { + ce.a = a; + break + } + } + if(text !== undefined) { + ce.text = text + } + if(names && names.length > 0) { + ce.names = names + } + if(name === "element") { + ce.id = elements.length; + elements.push(ce); + ce = {name:"elementref", id:ce.id} + } + return ce + }; + function resolveDefines(def, defines) { + var i = 0, e, defs, end, name = def.name; + while(def.e && i < def.e.length) { + e = def.e[i]; + if(e.name === "ref") { + defs = defines[e.a.name]; + if(!defs) { + throw e.a.name + " was not defined."; } - if(removedProperties[textPropertiesName]) { - removedAttributesFromStyleNode(textPropertiesNode, removedProperties[textPropertiesName].attributes); - if(textPropertiesNode.attributes.length === 0) { - styleNode.removeChild(textPropertiesNode) - } + end = def.e.slice(i + 1); + def.e = def.e.slice(0, i); + def.e = def.e.concat(defs.e); + def.e = def.e.concat(end) + }else { + i += 1; + resolveDefines(e, defines) + } + } + e = def.e; + if(name === "choice") { + if(!e || (!e[1] || e[1].name === "empty")) { + if(!e || (!e[0] || e[0].name === "empty")) { + delete def.e; + def.name = "empty" + }else { + e[1] = e[0]; + e[0] = {name:"empty"} } - removedAttributesFromStyleNode(styleNode, removedProperties.attributes) } - odtDocument.getOdfCanvas().refreshCSS(); - odtDocument.emit(ops.OdtDocument.signalParagraphStyleModified, styleName); - odtDocument.getOdfCanvas().rerenderAnnotations(); - return true } - return false - }; - this.spec = function() { - return{optype:"UpdateParagraphStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, setProperties:setProperties, removedProperties:removedProperties} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("odf.Namespaces"); -ops.OpAddStyle = function OpAddStyle() { - var memberid, timestamp, styleName, styleFamily, isAutomaticStyle, setProperties, stylens = odf.Namespaces.stylens; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - styleName = data.styleName; - styleFamily = data.styleFamily; - isAutomaticStyle = data.isAutomaticStyle === "true" || data.isAutomaticStyle === true; - setProperties = data.setProperties - }; - this.isEdit = true; - this.execute = function(odtDocument) { - var odfContainer = odtDocument.getOdfCanvas().odfContainer(), formatting = odtDocument.getFormatting(), dom = odtDocument.getDOM(), styleNode = dom.createElementNS(stylens, "style:style"); - if(!styleNode) { - return false + if(name === "group" || name === "interleave") { + if(e[0].name === "empty") { + if(e[1].name === "empty") { + delete def.e; + def.name = "empty" + }else { + name = def.name = e[1].name; + def.names = e[1].names; + e = def.e = e[1].e + } + }else { + if(e[1].name === "empty") { + name = def.name = e[0].name; + def.names = e[0].names; + e = def.e = e[0].e + } + } } - if(setProperties) { - formatting.updateStyle(styleNode, setProperties) + if(name === "oneOrMore" && e[0].name === "empty") { + delete def.e; + def.name = "empty" } - styleNode.setAttributeNS(stylens, "style:family", styleFamily); - styleNode.setAttributeNS(stylens, "style:name", styleName); - if(isAutomaticStyle) { - odfContainer.rootElement.automaticStyles.appendChild(styleNode) - }else { - odfContainer.rootElement.styles.appendChild(styleNode) + if(name === "attribute") { + splitQNames(def) } - odtDocument.getOdfCanvas().refreshCSS(); - if(!isAutomaticStyle) { - odtDocument.emit(ops.OdtDocument.signalCommonStyleCreated, {name:styleName, family:styleFamily}) + if(name === "interleave") { + if(e[0].name === "interleave") { + if(e[1].name === "interleave") { + e = def.e = e[0].e.concat(e[1].e) + }else { + e = def.e = [e[1]].concat(e[0].e) + } + }else { + if(e[1].name === "interleave") { + e = def.e = [e[0]].concat(e[1].e) + } + } } - return true - }; - this.spec = function() { - return{optype:"AddStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, styleFamily:styleFamily, isAutomaticStyle:isAutomaticStyle, setProperties:setProperties} } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpRemoveStyle = function OpRemoveStyle() { - var memberid, timestamp, styleName, styleFamily; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - styleName = data.styleName; - styleFamily = data.styleFamily - }; - this.isEdit = true; - this.execute = function(odtDocument) { - var styleNode = odtDocument.getStyleElement(styleName, styleFamily); - if(!styleNode) { - return false + function resolveElements(def, elements) { + var i = 0, e; + while(def.e && i < def.e.length) { + e = def.e[i]; + if(e.name === "elementref") { + e.id = e.id || 0; + def.e[i] = elements[e.id] + }else { + if(e.name !== "element") { + resolveElements(e, elements) + } + } + i += 1 } - styleNode.parentNode.removeChild(styleNode); - odtDocument.getOdfCanvas().refreshCSS(); - odtDocument.emit(ops.OdtDocument.signalCommonStyleDeleted, {name:styleName, family:styleFamily}); - return true - }; - this.spec = function() { - return{optype:"RemoveStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, styleFamily:styleFamily} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -ops.OpAddAnnotation = function OpAddAnnotation() { - var memberid, timestamp, position, length, name; - this.init = function(data) { - memberid = data.memberid; - timestamp = parseInt(data.timestamp, 10); - position = parseInt(data.position, 10); - length = parseInt(data.length, 10) || 0; - name = data.name - }; - this.isEdit = true; - function createAnnotationNode(odtDocument, date) { - var annotationNode, creatorNode, dateNode, listNode, listItemNode, paragraphNode, doc = odtDocument.getDOM(); - annotationNode = doc.createElementNS(odf.Namespaces.officens, "office:annotation"); - annotationNode.setAttributeNS(odf.Namespaces.officens, "office:name", name); - creatorNode = doc.createElementNS(odf.Namespaces.dcns, "dc:creator"); - creatorNode.setAttributeNS("urn:webodf:names:editinfo", "editinfo:memberid", memberid); - creatorNode.textContent = odtDocument.getMember(memberid).getProperties().fullName; - dateNode = doc.createElementNS(odf.Namespaces.dcns, "dc:date"); - dateNode.appendChild(doc.createTextNode(date.toISOString())); - listNode = doc.createElementNS(odf.Namespaces.textns, "text:list"); - listItemNode = doc.createElementNS(odf.Namespaces.textns, "text:list-item"); - paragraphNode = doc.createElementNS(odf.Namespaces.textns, "text:p"); - listItemNode.appendChild(paragraphNode); - listNode.appendChild(listItemNode); - annotationNode.appendChild(creatorNode); - annotationNode.appendChild(dateNode); - annotationNode.appendChild(listNode); - return annotationNode - } - function createAnnotationEnd(odtDocument) { - var annotationEnd, doc = odtDocument.getDOM(); - annotationEnd = doc.createElementNS(odf.Namespaces.officens, "office:annotation-end"); - annotationEnd.setAttributeNS(odf.Namespaces.officens, "office:name", name); - return annotationEnd } - function insertNodeAtPosition(odtDocument, node, insertPosition) { - var previousNode, parentNode, domPosition = odtDocument.getTextNodeAtStep(insertPosition, memberid); - if(domPosition) { - previousNode = domPosition.textNode; - parentNode = previousNode.parentNode; - if(domPosition.offset !== previousNode.length) { - previousNode.splitText(domPosition.offset) + function main(dom, callback) { + var elements = [], grammar = parse(dom && dom.documentElement, elements, undefined), i, e, defines = {}; + for(i = 0;i < grammar.e.length;i += 1) { + e = grammar.e[i]; + if(e.name === "define") { + defines[e.a.name] = e + }else { + if(e.name === "start") { + start = e + } } - parentNode.insertBefore(node, previousNode.nextSibling); - if(previousNode.length === 0) { - parentNode.removeChild(previousNode) + } + if(!start) { + return[new RelaxNGParseError("No Relax NG start element was found.")] + } + resolveDefines(start, defines); + for(i in defines) { + if(defines.hasOwnProperty(i)) { + resolveDefines(defines[i], defines) } } - } - this.execute = function(odtDocument) { - var annotation = {}, positionFilter = odtDocument.getPositionFilter(), cursor = odtDocument.getCursor(memberid), oldCursorPosition = odtDocument.getCursorPosition(memberid), lengthToMove = position - oldCursorPosition - 1, stepsToParagraph; - annotation.node = createAnnotationNode(odtDocument, new Date(timestamp)); - if(!annotation.node) { - return false + for(i = 0;i < elements.length;i += 1) { + resolveDefines(elements[i], defines) } - if(length) { - annotation.end = createAnnotationEnd(odtDocument); - if(!annotation.end) { - return false + if(callback) { + self.rootPattern = callback(start.e[0], elements) + } + resolveElements(start, elements); + for(i = 0;i < elements.length;i += 1) { + resolveElements(elements[i], elements) + } + self.start = start; + self.elements = elements; + self.nsmap = nsmap; + return null + } + this.parseRelaxNGDOM = main +}; +runtime.loadClass("core.Cursor"); +runtime.loadClass("gui.SelectionMover"); +ops.OdtCursor = function OdtCursor(memberId, odtDocument) { + var self = this, validSelectionTypes = {}, selectionType, selectionMover, cursor; + this.removeFromOdtDocument = function() { + cursor.remove() + }; + this.move = function(number, extend) { + var moved = 0; + if(number > 0) { + moved = selectionMover.movePointForward(number, extend) + }else { + if(number <= 0) { + moved = -selectionMover.movePointBackward(-number, extend) } - insertNodeAtPosition(odtDocument, annotation.end, position + length) } - insertNodeAtPosition(odtDocument, annotation.node, position); - odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:length}); - if(cursor) { - stepsToParagraph = cursor.getStepCounter().countSteps(lengthToMove, positionFilter); - cursor.move(stepsToParagraph); - cursor.resetSelectionType(); - odtDocument.emit(ops.OdtDocument.signalCursorMoved, cursor) + self.handleUpdate(); + return moved + }; + this.handleUpdate = function() { + }; + this.getStepCounter = function() { + return selectionMover.getStepCounter() + }; + this.getMemberId = function() { + return memberId + }; + this.getNode = function() { + return cursor.getNode() + }; + this.getAnchorNode = function() { + return cursor.getAnchorNode() + }; + this.getSelectedRange = function() { + return cursor.getSelectedRange() + }; + this.setSelectedRange = function(range, isForwardSelection) { + cursor.setSelectedRange(range, isForwardSelection); + self.handleUpdate() + }; + this.hasForwardSelection = function() { + return cursor.hasForwardSelection() + }; + this.getOdtDocument = function() { + return odtDocument + }; + this.getSelectionType = function() { + return selectionType + }; + this.setSelectionType = function(value) { + if(validSelectionTypes.hasOwnProperty(value)) { + selectionType = value + }else { + runtime.log("Invalid selection type: " + value) } - odtDocument.getOdfCanvas().addAnnotation(annotation); - odtDocument.fixCursorPositions(); - return true }; - this.spec = function() { - return{optype:"AddAnnotation", memberid:memberid, timestamp:timestamp, position:position, length:length, name:name} + this.resetSelectionType = function() { + self.setSelectionType(ops.OdtCursor.RangeSelection) + }; + function init() { + cursor = new core.Cursor(odtDocument.getDOM(), memberId); + selectionMover = new gui.SelectionMover(cursor, odtDocument.getRootNode()); + validSelectionTypes[ops.OdtCursor.RangeSelection] = true; + validSelectionTypes[ops.OdtCursor.RegionSelection] = true; + self.resetSelectionType() } + init() }; +ops.OdtCursor.RangeSelection = "Range"; +ops.OdtCursor.RegionSelection = "Region"; +(function() { + return ops.OdtCursor +})(); /* Copyright (C) 2012-2013 KO GmbH @@ -12030,1019 +11095,1583 @@ ops.OpAddAnnotation = function OpAddAnnotation() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.Namespaces"); +runtime.loadClass("core.EventNotifier"); runtime.loadClass("core.DomUtils"); -ops.OpRemoveAnnotation = function OpRemoveAnnotation() { - var memberid, timestamp, position, length, domUtils; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - position = parseInt(data.position, 10); - length = parseInt(data.length, 10); - domUtils = new core.DomUtils - }; - this.isEdit = true; - this.execute = function(odtDocument) { - var iterator = odtDocument.getIteratorAtPosition(position), container = iterator.container(), annotationName, annotationNode, annotationEnd, cursors; - while(!(container.namespaceURI === odf.Namespaces.officens && container.localName === "annotation")) { - container = container.parentNode +runtime.loadClass("odf.OdfUtils"); +runtime.loadClass("odf.Namespaces"); +runtime.loadClass("gui.SelectionMover"); +runtime.loadClass("core.PositionFilterChain"); +runtime.loadClass("ops.StepsTranslator"); +runtime.loadClass("ops.TextPositionFilter"); +runtime.loadClass("ops.Member"); +ops.OdtDocument = function OdtDocument(odfCanvas) { + var self = this, odfUtils, domUtils, cursors = {}, members = {}, eventNotifier = new core.EventNotifier([ops.OdtDocument.signalMemberAdded, ops.OdtDocument.signalMemberUpdated, ops.OdtDocument.signalMemberRemoved, ops.OdtDocument.signalCursorAdded, ops.OdtDocument.signalCursorRemoved, ops.OdtDocument.signalCursorMoved, ops.OdtDocument.signalParagraphChanged, ops.OdtDocument.signalParagraphStyleModified, ops.OdtDocument.signalCommonStyleCreated, ops.OdtDocument.signalCommonStyleDeleted, ops.OdtDocument.signalTableAdded, + ops.OdtDocument.signalOperationExecuted, ops.OdtDocument.signalUndoStackChanged, ops.OdtDocument.signalStepsInserted, ops.OdtDocument.signalStepsRemoved]), FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, FILTER_REJECT = core.PositionFilter.FilterResult.FILTER_REJECT, filter, stepsTranslator, lastEditingOp, unsupportedMetadataRemoved = false; + function getRootNode() { + var element = odfCanvas.odfContainer().getContentElement(), localName = element && element.localName; + runtime.assert(localName === "text", "Unsupported content element type '" + localName + "' for OdtDocument"); + return element + } + function getDOM() { + return(getRootNode().ownerDocument) + } + this.getDOM = getDOM; + function isRoot(node) { + if(node.namespaceURI === odf.Namespaces.officens && node.localName === "text" || node.namespaceURI === odf.Namespaces.officens && node.localName === "annotation") { + return true } - if(container === null) { - return false + return false + } + function getRoot(node) { + while(node && !isRoot(node)) { + node = (node.parentNode) } - annotationNode = container; - annotationName = annotationNode.getAttributeNS(odf.Namespaces.officens, "name"); - if(annotationName) { - annotationEnd = domUtils.getElementsByTagNameNS(odtDocument.getRootNode(), odf.Namespaces.officens, "annotation-end").filter(function(element) { - return annotationName === element.getAttributeNS(odf.Namespaces.officens, "name") - })[0] || null + return node + } + this.getRootElement = getRoot; + function RootFilter(anchor) { + this.acceptPosition = function(iterator) { + var node = iterator.container(), anchorNode; + if(typeof anchor === "string") { + anchorNode = cursors[anchor].getNode() + }else { + anchorNode = anchor + } + if(getRoot(node) === getRoot(anchorNode)) { + return FILTER_ACCEPT + } + return FILTER_REJECT } - odtDocument.getOdfCanvas().forgetAnnotations(); - cursors = domUtils.getElementsByTagNameNS(annotationNode, "urn:webodf:names:cursor", "cursor"); - while(cursors.length) { - annotationNode.parentNode.insertBefore(cursors.pop(), annotationNode) + } + function getIteratorAtPosition(position) { + var iterator = gui.SelectionMover.createPositionIterator(getRootNode()), point = stepsTranslator.convertStepsToDomPoint(position); + iterator.setUnfilteredPosition(point.node, point.offset); + return iterator + } + this.getIteratorAtPosition = getIteratorAtPosition; + this.convertDomPointToCursorStep = function(node, offset, roundDirection) { + return stepsTranslator.convertDomPointToSteps(node, offset, roundDirection) + }; + this.convertDomToCursorRange = function(selection, constraint) { + var point1, point2, anchorConstraint = constraint(selection.anchorNode, selection.anchorOffset), focusConstraint; + point1 = stepsTranslator.convertDomPointToSteps(selection.anchorNode, selection.anchorOffset, anchorConstraint); + if(!constraint && (selection.anchorNode === selection.focusNode && selection.anchorOffset === selection.focusOffset)) { + point2 = point1 + }else { + focusConstraint = constraint(selection.focusNode, selection.focusOffset); + point2 = stepsTranslator.convertDomPointToSteps(selection.focusNode, selection.focusOffset, focusConstraint) } - annotationNode.parentNode.removeChild(annotationNode); - if(annotationEnd) { - annotationEnd.parentNode.removeChild(annotationEnd) + return{position:point1, length:point2 - point1} + }; + this.convertCursorToDomRange = function(position, length) { + var range = getDOM().createRange(), point1, point2; + point1 = stepsTranslator.convertStepsToDomPoint(position); + if(length) { + point2 = stepsTranslator.convertStepsToDomPoint(position + length); + if(length > 0) { + range.setStart(point1.node, point1.offset); + range.setEnd(point2.node, point2.offset) + }else { + range.setStart(point2.node, point2.offset); + range.setEnd(point1.node, point1.offset) + } + }else { + range.setStart(point1.node, point1.offset) } - odtDocument.emit(ops.OdtDocument.signalStepsRemoved, {position:position > 0 ? position - 1 : position, length:length}); - odtDocument.fixCursorPositions(); - odtDocument.getOdfCanvas().refreshAnnotations(); - return true + return range }; - this.spec = function() { - return{optype:"RemoveAnnotation", memberid:memberid, timestamp:timestamp, position:position, length:length} + function getTextNodeAtStep(steps, memberid) { + var iterator = getIteratorAtPosition(steps), node = iterator.container(), lastTextNode, nodeOffset = 0, cursorNode = null; + if(node.nodeType === Node.TEXT_NODE) { + lastTextNode = (node); + nodeOffset = iterator.unfilteredDomOffset() + }else { + lastTextNode = getDOM().createTextNode(""); + nodeOffset = 0; + node.insertBefore(lastTextNode, iterator.rightNode()) + } + if(memberid && (cursors[memberid] && self.getCursorPosition(memberid) === steps)) { + cursorNode = cursors[memberid].getNode(); + while(cursorNode.nextSibling && cursorNode.nextSibling.localName === "cursor") { + cursorNode.parentNode.insertBefore(cursorNode.nextSibling, cursorNode) + } + if(lastTextNode.length > 0 && lastTextNode.nextSibling !== cursorNode) { + lastTextNode = getDOM().createTextNode(""); + nodeOffset = 0 + } + cursorNode.parentNode.insertBefore(lastTextNode, cursorNode) + } + while(lastTextNode.previousSibling && lastTextNode.previousSibling.nodeType === Node.TEXT_NODE) { + lastTextNode.previousSibling.appendData(lastTextNode.data); + nodeOffset = lastTextNode.previousSibling.length; + lastTextNode = (lastTextNode.previousSibling); + lastTextNode.parentNode.removeChild(lastTextNode.nextSibling) + } + return{textNode:lastTextNode, offset:nodeOffset} } -}; -/* - - Copyright (C) 2013 KO GmbH - - @licstart - This file is part of WebODF. - - WebODF 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. - - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend - - @source: http://www.webodf.org/ - @source: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("ops.Member"); -ops.OpAddMember = function OpAddMember() { - var memberid, timestamp, setProperties; - this.init = function(data) { - memberid = data.memberid; - timestamp = parseInt(data.timestamp, 10); - setProperties = data.setProperties - }; - this.isEdit = false; - this.execute = function(odtDocument) { - if(odtDocument.getMember(memberid)) { - return false + function getParagraphElement(node) { + return odfUtils.getParagraphElement(node) + } + function getStyleElement(styleName, styleFamily) { + return odfCanvas.getFormatting().getStyleElement(styleName, styleFamily) + } + this.getStyleElement = getStyleElement; + function getParagraphStyleElement(styleName) { + return getStyleElement(styleName, "paragraph") + } + function getParagraphStyleAttributes(styleName) { + var node = getParagraphStyleElement(styleName); + if(node) { + return odfCanvas.getFormatting().getInheritedStyleAttributes(node) + } + return null + } + function handleOperationExecuted(op) { + var spec = op.spec(), memberId = spec.memberid, date = (new Date(spec.timestamp)).toISOString(), metadataManager = odfCanvas.odfContainer().getMetadataManager(), fullName; + if(op.isEdit) { + fullName = self.getMember(memberId).getProperties().fullName; + metadataManager.setMetadata({"dc:creator":fullName, "dc:date":date}, null); + if(!lastEditingOp) { + metadataManager.incrementEditingCycles(); + if(!unsupportedMetadataRemoved) { + metadataManager.setMetadata(null, ["meta:editing-duration", "meta:document-statistic"]) + } + } + lastEditingOp = op + } + } + function upgradeWhitespaceToElement(textNode, offset) { + runtime.assert(textNode.data[offset] === " ", "upgradeWhitespaceToElement: textNode.data[offset] should be a literal space"); + var space = textNode.ownerDocument.createElementNS(odf.Namespaces.textns, "text:s"); + space.appendChild(textNode.ownerDocument.createTextNode(" ")); + textNode.deleteData(offset, 1); + if(offset > 0) { + textNode = (textNode.splitText(offset)) } - var member = new ops.Member(memberid, setProperties); - odtDocument.addMember(member); - odtDocument.emit(ops.OdtDocument.signalMemberAdded, member); - return true - }; - this.spec = function() { - return{optype:"AddMember", memberid:memberid, timestamp:timestamp, setProperties:setProperties} + textNode.parentNode.insertBefore(space, textNode); + return space } -}; -/* - - Copyright (C) 2013 KO GmbH - - @licstart - This file is part of WebODF. - - WebODF 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. - - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend - - @source: http://www.webodf.org/ - @source: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("ops.Member"); -runtime.loadClass("xmldom.XPath"); -ops.OpUpdateMember = function OpUpdateMember() { - var memberid, timestamp, setProperties, removedProperties, doc; - this.init = function(data) { - memberid = data.memberid; - timestamp = parseInt(data.timestamp, 10); - setProperties = data.setProperties; - removedProperties = data.removedProperties - }; - this.isEdit = false; - function updateCreators() { - var xpath = new xmldom.XPath, xp = "//dc:creator[@editinfo:memberid='" + memberid + "']", creators = xpath.getODFElementsWithXPath(doc.getRootNode(), xp, function(prefix) { - if(prefix === "editinfo") { - return"urn:webodf:names:editinfo" + function upgradeWhitespacesAtPosition(position) { + var iterator = getIteratorAtPosition(position), container, offset, i; + iterator.previousPosition(); + iterator.previousPosition(); + for(i = -1;i <= 1;i += 1) { + container = iterator.container(); + offset = iterator.unfilteredDomOffset(); + if(container.nodeType === Node.TEXT_NODE && (container.data[offset] === " " && odfUtils.isSignificantWhitespace(container, offset))) { + container = upgradeWhitespaceToElement((container), offset); + iterator.moveToEndOfNode(container) } - return odf.Namespaces.resolvePrefix(prefix) - }), i; - for(i = 0;i < creators.length;i += 1) { - creators[i].textContent = setProperties.fullName + iterator.nextPosition() } } - this.execute = function(odtDocument) { - doc = odtDocument; - var member = odtDocument.getMember(memberid); - if(!member) { - return false + this.upgradeWhitespacesAtPosition = upgradeWhitespacesAtPosition; + this.downgradeWhitespacesAtPosition = function(position) { + var iterator = getIteratorAtPosition(position), container, offset, firstSpaceElementChild, lastSpaceElementChild; + container = iterator.container(); + offset = iterator.unfilteredDomOffset(); + while(!odfUtils.isCharacterElement(container) && container.childNodes[offset]) { + container = container.childNodes[offset]; + offset = 0 } - if(removedProperties) { - member.removeProperties(removedProperties) + if(container.nodeType === Node.TEXT_NODE) { + container = container.parentNode } - if(setProperties) { - member.setProperties(setProperties); - if(setProperties.fullName) { - updateCreators() + if(odfUtils.isDowngradableSpaceElement(container)) { + firstSpaceElementChild = container.firstChild; + lastSpaceElementChild = container.lastChild; + domUtils.mergeIntoParent(container); + if(lastSpaceElementChild !== firstSpaceElementChild) { + domUtils.normalizeTextNodes(lastSpaceElementChild) } + domUtils.normalizeTextNodes(firstSpaceElementChild) } - odtDocument.emit(ops.OdtDocument.signalMemberUpdated, member); - return true }; - this.spec = function() { - return{optype:"UpdateMember", memberid:memberid, timestamp:timestamp, setProperties:setProperties, removedProperties:removedProperties} - } -}; -/* - - Copyright (C) 2013 KO GmbH - - @licstart - This file is part of WebODF. - - WebODF 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. - - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend - - @source: http://www.webodf.org/ - @source: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("ops.Member"); -ops.OpRemoveMember = function OpRemoveMember() { - var memberid, timestamp; - this.init = function(data) { - memberid = data.memberid; - timestamp = parseInt(data.timestamp, 10) + this.getParagraphStyleElement = getParagraphStyleElement; + this.getParagraphElement = getParagraphElement; + this.getParagraphStyleAttributes = getParagraphStyleAttributes; + this.getTextNodeAtStep = getTextNodeAtStep; + this.fixCursorPositions = function() { + var rootConstrainedFilter = new core.PositionFilterChain; + rootConstrainedFilter.addFilter("BaseFilter", filter); + Object.keys(cursors).forEach(function(memberId) { + var cursor = cursors[memberId], stepCounter = cursor.getStepCounter(), stepsSelectionLength, positionsToAdjustFocus, positionsToAdjustAnchor, positionsToAnchor, cursorMoved = false; + rootConstrainedFilter.addFilter("RootFilter", self.createRootFilter(memberId)); + stepsSelectionLength = stepCounter.countStepsToPosition(cursor.getAnchorNode(), 0, rootConstrainedFilter); + if(!stepCounter.isPositionWalkable(rootConstrainedFilter)) { + cursorMoved = true; + positionsToAdjustFocus = stepCounter.countPositionsToNearestStep(cursor.getNode(), 0, rootConstrainedFilter); + positionsToAdjustAnchor = stepCounter.countPositionsToNearestStep(cursor.getAnchorNode(), 0, rootConstrainedFilter); + cursor.move(positionsToAdjustFocus); + if(stepsSelectionLength !== 0) { + if(positionsToAdjustAnchor > 0) { + stepsSelectionLength += 1 + } + if(positionsToAdjustFocus > 0) { + stepsSelectionLength -= 1 + } + positionsToAnchor = stepCounter.countSteps(stepsSelectionLength, rootConstrainedFilter); + cursor.move(positionsToAnchor); + cursor.move(-positionsToAnchor, true) + } + }else { + if(stepsSelectionLength === 0) { + cursorMoved = true; + cursor.move(0) + } + } + if(cursorMoved) { + self.emit(ops.OdtDocument.signalCursorMoved, cursor) + } + rootConstrainedFilter.removeFilter("RootFilter") + }) }; - this.isEdit = false; - this.execute = function(odtDocument) { - if(!odtDocument.getMember(memberid)) { - return false + this.getDistanceFromCursor = function(memberid, node, offset) { + var cursor = cursors[memberid], focusPosition, targetPosition; + runtime.assert(node !== null && node !== undefined, "OdtDocument.getDistanceFromCursor called without node"); + if(cursor) { + focusPosition = stepsTranslator.convertDomPointToSteps(cursor.getNode(), 0); + targetPosition = stepsTranslator.convertDomPointToSteps(node, offset) } - odtDocument.removeMember(memberid); - odtDocument.emit(ops.OdtDocument.signalMemberRemoved, memberid); - return true + return targetPosition - focusPosition + }; + this.getCursorPosition = function(memberid) { + var cursor = cursors[memberid]; + return cursor ? stepsTranslator.convertDomPointToSteps(cursor.getNode(), 0) : 0 + }; + this.getCursorSelection = function(memberid) { + var cursor = cursors[memberid], focusPosition = 0, anchorPosition = 0; + if(cursor) { + focusPosition = stepsTranslator.convertDomPointToSteps(cursor.getNode(), 0); + anchorPosition = stepsTranslator.convertDomPointToSteps(cursor.getAnchorNode(), 0) + } + return{position:anchorPosition, length:focusPosition - anchorPosition} + }; + this.getPositionFilter = function() { + return filter + }; + this.getOdfCanvas = function() { + return odfCanvas + }; + this.getRootNode = getRootNode; + this.addMember = function(member) { + runtime.assert(members[member.getMemberId()] === undefined, "This member already exists"); + members[member.getMemberId()] = member + }; + this.getMember = function(memberId) { + return members.hasOwnProperty(memberId) ? members[memberId] : null + }; + this.removeMember = function(memberId) { + delete members[memberId] + }; + this.getCursor = function(memberid) { + return cursors[memberid] + }; + this.getCursors = function() { + var list = [], i; + for(i in cursors) { + if(cursors.hasOwnProperty(i)) { + list.push(cursors[i]) + } + } + return list + }; + this.addCursor = function(cursor) { + runtime.assert(Boolean(cursor), "OdtDocument::addCursor without cursor"); + var distanceToFirstTextNode = cursor.getStepCounter().countSteps(1, filter), memberid = cursor.getMemberId(); + runtime.assert(typeof memberid === "string", "OdtDocument::addCursor has cursor without memberid"); + runtime.assert(!cursors[memberid], "OdtDocument::addCursor is adding a duplicate cursor with memberid " + memberid); + cursor.move(distanceToFirstTextNode); + cursors[memberid] = cursor + }; + this.removeCursor = function(memberid) { + var cursor = cursors[memberid]; + if(cursor) { + cursor.removeFromOdtDocument(); + delete cursors[memberid]; + self.emit(ops.OdtDocument.signalCursorRemoved, memberid); + return true + } + return false + }; + this.getFormatting = function() { + return odfCanvas.getFormatting() + }; + this.emit = function(eventid, args) { + eventNotifier.emit(eventid, args) + }; + this.subscribe = function(eventid, cb) { + eventNotifier.subscribe(eventid, cb) + }; + this.unsubscribe = function(eventid, cb) { + eventNotifier.unsubscribe(eventid, cb) + }; + this.createRootFilter = function(inputMemberId) { + return new RootFilter(inputMemberId) }; - this.spec = function() { - return{optype:"RemoveMember", memberid:memberid, timestamp:timestamp} + this.close = function(callback) { + callback() + }; + this.destroy = function(callback) { + callback() + }; + function init() { + filter = new ops.TextPositionFilter(getRootNode); + odfUtils = new odf.OdfUtils; + domUtils = new core.DomUtils; + stepsTranslator = new ops.StepsTranslator(getRootNode, gui.SelectionMover.createPositionIterator, filter, 500); + eventNotifier.subscribe(ops.OdtDocument.signalStepsInserted, stepsTranslator.handleStepsInserted); + eventNotifier.subscribe(ops.OdtDocument.signalStepsRemoved, stepsTranslator.handleStepsRemoved); + eventNotifier.subscribe(ops.OdtDocument.signalOperationExecuted, handleOperationExecuted) } + init() }; +ops.OdtDocument.signalMemberAdded = "member/added"; +ops.OdtDocument.signalMemberUpdated = "member/updated"; +ops.OdtDocument.signalMemberRemoved = "member/removed"; +ops.OdtDocument.signalCursorAdded = "cursor/added"; +ops.OdtDocument.signalCursorRemoved = "cursor/removed"; +ops.OdtDocument.signalCursorMoved = "cursor/moved"; +ops.OdtDocument.signalParagraphChanged = "paragraph/changed"; +ops.OdtDocument.signalTableAdded = "table/added"; +ops.OdtDocument.signalCommonStyleCreated = "style/created"; +ops.OdtDocument.signalCommonStyleDeleted = "style/deleted"; +ops.OdtDocument.signalParagraphStyleModified = "paragraphstyle/modified"; +ops.OdtDocument.signalOperationExecuted = "operation/executed"; +ops.OdtDocument.signalUndoStackChanged = "undo/changed"; +ops.OdtDocument.signalStepsInserted = "steps/inserted"; +ops.OdtDocument.signalStepsRemoved = "steps/removed"; +(function() { + return ops.OdtDocument +})(); /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart - This file is part of WebODF. + 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. - WebODF 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. + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License 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. - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend + 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: https://github.com/kogmbh/WebODF/ */ -ops.OpUpdateMetadata = function OpUpdateMetadata() { - var memberid, timestamp, setProperties, removedProperties; - this.init = function(data) { - memberid = data.memberid; - timestamp = parseInt(data.timestamp, 10); - setProperties = data.setProperties; - removedProperties = data.removedProperties - }; - this.isEdit = true; - this.execute = function(odtDocument) { - var metadataManager = odtDocument.getOdfCanvas().odfContainer().getMetadataManager(), removedPropertiesArray = [], blockedProperties = ["dc:date", "dc:creator", "meta:editing-cycles"]; - if(setProperties) { - blockedProperties.forEach(function(el) { - if(setProperties[el]) { - return false +ops.Operation = function Operation() { +}; +ops.Operation.prototype.init = function(data) { +}; +ops.Operation.prototype.isEdit; +ops.Operation.prototype.execute = function(odtDocument) { +}; +ops.Operation.prototype.spec = function() { +}; +runtime.loadClass("xmldom.RelaxNGParser"); +xmldom.RelaxNGItem; +xmldom.RelaxNG = function RelaxNG() { + var xmlnsns = "http://www.w3.org/2000/xmlns/", createChoice, createInterleave, createGroup, createAfter, createOneOrMore, createValue, createAttribute, createNameClass, createData, makePattern, applyAfter, childDeriv, rootPattern, notAllowed = {type:"notAllowed", nullable:false, hash:"notAllowed", nc:undefined, p:undefined, p1:undefined, p2:undefined, textDeriv:function() { + return notAllowed + }, startTagOpenDeriv:function() { + return notAllowed + }, attDeriv:function() { + return notAllowed + }, startTagCloseDeriv:function() { + return notAllowed + }, endTagDeriv:function() { + return notAllowed + }}, empty = {type:"empty", nullable:true, hash:"empty", nc:undefined, p:undefined, p1:undefined, p2:undefined, textDeriv:function() { + return notAllowed + }, startTagOpenDeriv:function() { + return notAllowed + }, attDeriv:function() { + return notAllowed + }, startTagCloseDeriv:function() { + return empty + }, endTagDeriv:function() { + return notAllowed + }}, text = {type:"text", nullable:true, hash:"text", nc:undefined, p:undefined, p1:undefined, p2:undefined, textDeriv:function() { + return text + }, startTagOpenDeriv:function() { + return notAllowed + }, attDeriv:function() { + return notAllowed + }, startTagCloseDeriv:function() { + return text + }, endTagDeriv:function() { + return notAllowed + }}; + function memoize0arg(func) { + function f() { + var cache; + function g() { + if(cache === undefined) { + cache = func() + } + return cache + } + return g + } + return f() + } + function memoize1arg(type, func) { + function f() { + var cache = {}, cachecount = 0; + function g(a) { + var ahash = a.hash || a.toString(), v; + if(cache.hasOwnProperty(ahash)) { + return cache[ahash] + } + cache[ahash] = v = func(a); + v.hash = type + cachecount.toString(); + cachecount += 1; + return v + } + return g + } + return f() + } + function memoizeNode(func) { + function f() { + var cache = {}; + function g(node) { + var v, m; + if(!cache.hasOwnProperty(node.localName)) { + cache[node.localName] = m = {} + }else { + m = cache[node.localName]; + v = m[node.namespaceURI]; + if(v !== undefined) { + return v + } + } + m[node.namespaceURI] = v = func(node); + return v + } + return g + } + return f() + } + function memoize2arg(type, fastfunc, func) { + function f() { + var cache = {}, cachecount = 0; + function g(a, b) { + var v = fastfunc && fastfunc(a, b), ahash, bhash, m; + if(v !== undefined) { + return v + } + ahash = a.hash || a.toString(); + bhash = b.hash || b.toString(); + if(!cache.hasOwnProperty(ahash)) { + cache[ahash] = m = {} + }else { + m = cache[ahash]; + if(m.hasOwnProperty(bhash)) { + return m[bhash] + } + } + m[bhash] = v = func(a, b); + v.hash = type + cachecount.toString(); + cachecount += 1; + return v + } + return g + } + return f() + } + function unorderedMemoize2arg(type, fastfunc, func) { + function f() { + var cache = {}, cachecount = 0; + function g(a, b) { + var v = fastfunc && fastfunc(a, b), ahash, bhash, hash, m; + if(v !== undefined) { + return v + } + ahash = a.hash || a.toString(); + bhash = b.hash || b.toString(); + if(ahash < bhash) { + hash = ahash; + ahash = bhash; + bhash = hash; + hash = a; + a = b; + b = hash + } + if(!cache.hasOwnProperty(ahash)) { + cache[ahash] = m = {} + }else { + m = cache[ahash]; + if(m.hasOwnProperty(bhash)) { + return m[bhash] + } + } + m[bhash] = v = func(a, b); + v.hash = type + cachecount.toString(); + cachecount += 1; + return v + } + return g + } + return f() + } + function getUniqueLeaves(leaves, pattern) { + if(pattern.p1.type === "choice") { + getUniqueLeaves(leaves, pattern.p1) + }else { + leaves[pattern.p1.hash] = pattern.p1 + } + if(pattern.p2.type === "choice") { + getUniqueLeaves(leaves, pattern.p2) + }else { + leaves[pattern.p2.hash] = pattern.p2 + } + } + createChoice = memoize2arg("choice", function(p1, p2) { + if(p1 === notAllowed) { + return p2 + } + if(p2 === notAllowed) { + return p1 + } + if(p1 === p2) { + return p1 + } + }, function(p1, p2) { + function makeChoice(p1, p2) { + return{type:"choice", nullable:p1.nullable || p2.nullable, hash:undefined, nc:undefined, p:undefined, p1:p1, p2:p2, textDeriv:function(context, text) { + return createChoice(p1.textDeriv(context, text), p2.textDeriv(context, text)) + }, startTagOpenDeriv:memoizeNode(function(node) { + return createChoice(p1.startTagOpenDeriv(node), p2.startTagOpenDeriv(node)) + }), attDeriv:function(context, attribute) { + return createChoice(p1.attDeriv(context, attribute), p2.attDeriv(context, attribute)) + }, startTagCloseDeriv:memoize0arg(function() { + return createChoice(p1.startTagCloseDeriv(), p2.startTagCloseDeriv()) + }), endTagDeriv:memoize0arg(function() { + return createChoice(p1.endTagDeriv(), p2.endTagDeriv()) + })} + } + var leaves = {}, i; + getUniqueLeaves(leaves, {p1:p1, p2:p2}); + p1 = undefined; + p2 = undefined; + for(i in leaves) { + if(leaves.hasOwnProperty(i)) { + if(p1 === undefined) { + p1 = leaves[i] + }else { + if(p2 === undefined) { + p2 = leaves[i] + }else { + p2 = createChoice(p2, leaves[i]) + } } - }) + } } - if(removedProperties) { - blockedProperties.forEach(function(el) { - if(removedPropertiesArray.indexOf(el) !== -1) { - return false - } - }); - removedPropertiesArray = removedProperties.attributes.split(",") + return makeChoice(p1, p2) + }); + createInterleave = unorderedMemoize2arg("interleave", function(p1, p2) { + if(p1 === notAllowed || p2 === notAllowed) { + return notAllowed } - metadataManager.setMetadata(setProperties, removedPropertiesArray); - return true - }; - this.spec = function() { - return{optype:"UpdateMetadata", memberid:memberid, timestamp:timestamp, setProperties:setProperties, removedProperties:removedProperties} - } -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("ops.OpAddMember"); -runtime.loadClass("ops.OpUpdateMember"); -runtime.loadClass("ops.OpRemoveMember"); -runtime.loadClass("ops.OpAddCursor"); -runtime.loadClass("ops.OpApplyDirectStyling"); -runtime.loadClass("ops.OpRemoveCursor"); -runtime.loadClass("ops.OpMoveCursor"); -runtime.loadClass("ops.OpSetBlob"); -runtime.loadClass("ops.OpRemoveBlob"); -runtime.loadClass("ops.OpInsertImage"); -runtime.loadClass("ops.OpInsertTable"); -runtime.loadClass("ops.OpInsertText"); -runtime.loadClass("ops.OpRemoveText"); -runtime.loadClass("ops.OpSplitParagraph"); -runtime.loadClass("ops.OpSetParagraphStyle"); -runtime.loadClass("ops.OpUpdateParagraphStyle"); -runtime.loadClass("ops.OpAddStyle"); -runtime.loadClass("ops.OpRemoveStyle"); -runtime.loadClass("ops.OpAddAnnotation"); -runtime.loadClass("ops.OpRemoveAnnotation"); -runtime.loadClass("ops.OpUpdateMetadata"); -ops.OperationFactory = function OperationFactory() { - var specs; - this.register = function(specName, specConstructor) { - specs[specName] = specConstructor - }; - this.create = function(spec) { - var op = null, specConstructor = specs[spec.optype]; - if(specConstructor) { - op = specConstructor(spec); - op.init(spec) + if(p1 === empty) { + return p2 } - return op - }; - function constructor(OperationType) { - return function() { - return new OperationType + if(p2 === empty) { + return p1 } + }, function(p1, p2) { + return{type:"interleave", nullable:p1.nullable && p2.nullable, hash:undefined, p1:p1, p2:p2, textDeriv:function(context, text) { + return createChoice(createInterleave(p1.textDeriv(context, text), p2), createInterleave(p1, p2.textDeriv(context, text))) + }, startTagOpenDeriv:memoizeNode(function(node) { + return createChoice(applyAfter(function(p) { + return createInterleave(p, p2) + }, p1.startTagOpenDeriv(node)), applyAfter(function(p) { + return createInterleave(p1, p) + }, p2.startTagOpenDeriv(node))) + }), attDeriv:function(context, attribute) { + return createChoice(createInterleave(p1.attDeriv(context, attribute), p2), createInterleave(p1, p2.attDeriv(context, attribute))) + }, startTagCloseDeriv:memoize0arg(function() { + return createInterleave(p1.startTagCloseDeriv(), p2.startTagCloseDeriv()) + }), endTagDeriv:undefined} + }); + createGroup = memoize2arg("group", function(p1, p2) { + if(p1 === notAllowed || p2 === notAllowed) { + return notAllowed + } + if(p1 === empty) { + return p2 + } + if(p2 === empty) { + return p1 + } + }, function(p1, p2) { + return{type:"group", p1:p1, p2:p2, nullable:p1.nullable && p2.nullable, textDeriv:function(context, text) { + var p = createGroup(p1.textDeriv(context, text), p2); + if(p1.nullable) { + return createChoice(p, p2.textDeriv(context, text)) + } + return p + }, startTagOpenDeriv:function(node) { + var x = applyAfter(function(p) { + return createGroup(p, p2) + }, p1.startTagOpenDeriv(node)); + if(p1.nullable) { + return createChoice(x, p2.startTagOpenDeriv(node)) + } + return x + }, attDeriv:function(context, attribute) { + return createChoice(createGroup(p1.attDeriv(context, attribute), p2), createGroup(p1, p2.attDeriv(context, attribute))) + }, startTagCloseDeriv:memoize0arg(function() { + return createGroup(p1.startTagCloseDeriv(), p2.startTagCloseDeriv()) + })} + }); + createAfter = memoize2arg("after", function(p1, p2) { + if(p1 === notAllowed || p2 === notAllowed) { + return notAllowed + } + }, function(p1, p2) { + return{type:"after", p1:p1, p2:p2, nullable:false, textDeriv:function(context, text) { + return createAfter(p1.textDeriv(context, text), p2) + }, startTagOpenDeriv:memoizeNode(function(node) { + return applyAfter(function(p) { + return createAfter(p, p2) + }, p1.startTagOpenDeriv(node)) + }), attDeriv:function(context, attribute) { + return createAfter(p1.attDeriv(context, attribute), p2) + }, startTagCloseDeriv:memoize0arg(function() { + return createAfter(p1.startTagCloseDeriv(), p2) + }), endTagDeriv:memoize0arg(function() { + return p1.nullable ? p2 : notAllowed + })} + }); + createOneOrMore = memoize1arg("oneormore", function(p) { + if(p === notAllowed) { + return notAllowed + } + return{type:"oneOrMore", p:p, nullable:p.nullable, textDeriv:function(context, text) { + return createGroup(p.textDeriv(context, text), createChoice(this, empty)) + }, startTagOpenDeriv:function(node) { + var oneOrMore = this; + return applyAfter(function(pf) { + return createGroup(pf, createChoice(oneOrMore, empty)) + }, p.startTagOpenDeriv(node)) + }, attDeriv:function(context, attribute) { + var oneOrMore = this; + return createGroup(p.attDeriv(context, attribute), createChoice(oneOrMore, empty)) + }, startTagCloseDeriv:memoize0arg(function() { + return createOneOrMore(p.startTagCloseDeriv()) + })} + }); + function createElement(nc, p) { + return{type:"element", nc:nc, nullable:false, textDeriv:function() { + return notAllowed + }, startTagOpenDeriv:function(node) { + if(nc.contains(node)) { + return createAfter(p, empty) + } + return notAllowed + }, attDeriv:function() { + return notAllowed + }, startTagCloseDeriv:function() { + return this + }} } - function init() { - specs = {AddMember:constructor(ops.OpAddMember), UpdateMember:constructor(ops.OpUpdateMember), RemoveMember:constructor(ops.OpRemoveMember), AddCursor:constructor(ops.OpAddCursor), ApplyDirectStyling:constructor(ops.OpApplyDirectStyling), SetBlob:constructor(ops.OpSetBlob), RemoveBlob:constructor(ops.OpRemoveBlob), InsertImage:constructor(ops.OpInsertImage), InsertTable:constructor(ops.OpInsertTable), InsertText:constructor(ops.OpInsertText), RemoveText:constructor(ops.OpRemoveText), SplitParagraph:constructor(ops.OpSplitParagraph), - SetParagraphStyle:constructor(ops.OpSetParagraphStyle), UpdateParagraphStyle:constructor(ops.OpUpdateParagraphStyle), AddStyle:constructor(ops.OpAddStyle), RemoveStyle:constructor(ops.OpRemoveStyle), MoveCursor:constructor(ops.OpMoveCursor), RemoveCursor:constructor(ops.OpRemoveCursor), AddAnnotation:constructor(ops.OpAddAnnotation), RemoveAnnotation:constructor(ops.OpRemoveAnnotation), UpdateMetadata:constructor(ops.OpUpdateMetadata)} - } - init() -}; -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("core.Cursor"); -runtime.loadClass("core.DomUtils"); -runtime.loadClass("core.PositionIterator"); -runtime.loadClass("core.PositionFilter"); -runtime.loadClass("core.LoopWatchDog"); -runtime.loadClass("odf.OdfUtils"); -gui.SelectionMover = function SelectionMover(cursor, rootNode) { - var odfUtils, domUtils, positionIterator, cachedXOffset, timeoutHandle, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; - function getIteratorAtCursor() { - positionIterator.setUnfilteredPosition(cursor.getNode(), 0); - return positionIterator + function valueMatch(context, pattern, text) { + return pattern.nullable && /^\s+$/.test(text) || pattern.textDeriv(context, text).nullable } - function getMaximumNodePosition(node) { - return node.nodeType === Node.TEXT_NODE ? node.textContent.length : node.childNodes.length + createAttribute = memoize2arg("attribute", undefined, function(nc, p) { + return{type:"attribute", nullable:false, hash:undefined, nc:nc, p:p, p1:undefined, p2:undefined, textDeriv:undefined, startTagOpenDeriv:undefined, attDeriv:function(context, attribute) { + if(nc.contains(attribute) && valueMatch(context, p, attribute.nodeValue)) { + return empty + } + return notAllowed + }, startTagCloseDeriv:function() { + return notAllowed + }, endTagDeriv:undefined} + }); + function createList() { + return{type:"list", nullable:false, hash:"list", textDeriv:function() { + return empty + }} } - function getClientRect(clientRectangles, useRightEdge) { - var rectangle, simplifiedRectangle = null; - if(clientRectangles) { - rectangle = useRightEdge ? clientRectangles[clientRectangles.length - 1] : clientRectangles[0] + createValue = memoize1arg("value", function(value) { + return{type:"value", nullable:false, value:value, textDeriv:function(context, text) { + return text === value ? empty : notAllowed + }, attDeriv:function() { + return notAllowed + }, startTagCloseDeriv:function() { + return this + }} + }); + createData = memoize1arg("data", function(type) { + return{type:"data", nullable:false, dataType:type, textDeriv:function() { + return empty + }, attDeriv:function() { + return notAllowed + }, startTagCloseDeriv:function() { + return this + }} + }); + applyAfter = function applyAfter(f, p) { + var result; + if(p.type === "after") { + result = createAfter(p.p1, f(p.p2)) + }else { + if(p.type === "choice") { + result = createChoice(applyAfter(f, p.p1), applyAfter(f, p.p2)) + }else { + result = p + } } - if(rectangle) { - simplifiedRectangle = {top:rectangle.top, left:useRightEdge ? rectangle.right : rectangle.left, bottom:rectangle.bottom} + return result + }; + function attsDeriv(context, pattern, attributes, position) { + if(pattern === notAllowed) { + return notAllowed } - return simplifiedRectangle - } - function getVisibleRect(container, offset, range, useRightEdge) { - var rectangle, nodeType = container.nodeType; - range.setStart(container, offset); - range.collapse(!useRightEdge); - rectangle = getClientRect(range.getClientRects(), useRightEdge === true); - if(!rectangle && offset > 0) { - range.setStart(container, offset - 1); - range.setEnd(container, offset); - rectangle = getClientRect(range.getClientRects(), true) + if(position >= attributes.length) { + return pattern } - if(!rectangle) { - if(nodeType === Node.ELEMENT_NODE && container.childNodes[offset - 1]) { - rectangle = getVisibleRect(container, offset - 1, range, true) + if(position === 0) { + position = 0 + } + var a = attributes.item(position); + while(a.namespaceURI === xmlnsns) { + position += 1; + if(position >= attributes.length) { + return pattern + } + a = attributes.item(position) + } + a = attsDeriv(context, pattern.attDeriv(context, attributes.item(position)), attributes, position + 1); + return a + } + function childrenDeriv(context, pattern, walker) { + var element = walker.currentNode, childNode = walker.firstChild(), childNodes = [], i, p; + while(childNode) { + if(childNode.nodeType === Node.ELEMENT_NODE) { + childNodes.push(childNode) }else { - if(container.nodeType === Node.TEXT_NODE && offset > 0) { - rectangle = getVisibleRect(container, offset - 1, range, true) + if(childNode.nodeType === Node.TEXT_NODE && !/^\s*$/.test(childNode.nodeValue)) { + childNodes.push(childNode.nodeValue) + } + } + childNode = walker.nextSibling() + } + if(childNodes.length === 0) { + childNodes = [""] + } + p = pattern; + for(i = 0;p !== notAllowed && i < childNodes.length;i += 1) { + childNode = childNodes[i]; + if(typeof childNode === "string") { + if(/^\s*$/.test(childNode)) { + p = createChoice(p, p.textDeriv(context, childNode)) }else { - if(container.previousSibling) { - rectangle = getVisibleRect(container.previousSibling, getMaximumNodePosition(container.previousSibling), range, true) - }else { - if(container.parentNode && container.parentNode !== rootNode) { - rectangle = getVisibleRect(container.parentNode, 0, range, false) - }else { - range.selectNode(rootNode); - rectangle = getClientRect(range.getClientRects(), false) - } - } + p = p.textDeriv(context, childNode) } + }else { + walker.currentNode = childNode; + p = childDeriv(context, p, walker) } } - runtime.assert(Boolean(rectangle), "No visible rectangle found"); - return(rectangle) + walker.currentNode = element; + return p } - function doMove(positions, extend, move) { - var left = positions, iterator = getIteratorAtCursor(), initialRect, range = (rootNode.ownerDocument.createRange()), selectionRange = cursor.getSelectedRange() ? cursor.getSelectedRange().cloneRange() : rootNode.ownerDocument.createRange(), newRect, horizontalMovement, o, c, isForwardSelection; - initialRect = getVisibleRect(iterator.container(), iterator.unfilteredDomOffset(), range); - while(left > 0 && move()) { - left -= 1 + childDeriv = function childDeriv(context, pattern, walker) { + var childNode = walker.currentNode, p; + p = pattern.startTagOpenDeriv(childNode); + p = attsDeriv(context, p, childNode.attributes, 0); + p = p.startTagCloseDeriv(); + p = childrenDeriv(context, p, walker); + p = p.endTagDeriv(); + return p + }; + function addNames(name, ns, pattern) { + if(pattern.e[0].a) { + name.push(pattern.e[0].text); + ns.push(pattern.e[0].a.ns) + }else { + addNames(name, ns, pattern.e[0]) } - if(extend) { - c = iterator.container(); - o = iterator.unfilteredDomOffset(); - if(domUtils.comparePoints(selectionRange.startContainer, selectionRange.startOffset, c, o) === -1) { - selectionRange.setStart(c, o); - isForwardSelection = false + if(pattern.e[1].a) { + name.push(pattern.e[1].text); + ns.push(pattern.e[1].a.ns) + }else { + addNames(name, ns, pattern.e[1]) + } + } + createNameClass = function createNameClass(pattern) { + var name, ns, hash, i, result; + if(pattern.name === "name") { + name = pattern.text; + ns = pattern.a.ns; + result = {name:name, ns:ns, hash:"{" + ns + "}" + name, contains:function(node) { + return node.namespaceURI === ns && node.localName === name + }} + }else { + if(pattern.name === "choice") { + name = []; + ns = []; + addNames(name, ns, pattern); + hash = ""; + for(i = 0;i < name.length;i += 1) { + hash += "{" + ns[i] + "}" + name[i] + "," + } + result = {hash:hash, contains:function(node) { + var j; + for(j = 0;j < name.length;j += 1) { + if(name[j] === node.localName && ns[j] === node.namespaceURI) { + return true + } + } + return false + }} }else { - selectionRange.setEnd(c, o) + result = {hash:"anyName", contains:function() { + return true + }} } - }else { - selectionRange.setStart(iterator.container(), iterator.unfilteredDomOffset()); - selectionRange.collapse(true) } - cursor.setSelectedRange(selectionRange, isForwardSelection); - iterator = getIteratorAtCursor(); - newRect = getVisibleRect(iterator.container(), iterator.unfilteredDomOffset(), range); - horizontalMovement = newRect.top === initialRect.top ? true : false; - if(horizontalMovement || cachedXOffset === undefined) { - cachedXOffset = newRect.left + return result + }; + function resolveElement(pattern, elements) { + var element, p, i, hash; + hash = "element" + pattern.id.toString(); + p = elements[pattern.id] = {hash:hash}; + element = createElement(createNameClass(pattern.e[0]), makePattern(pattern.e[1], elements)); + for(i in element) { + if(element.hasOwnProperty(i)) { + p[i] = element[i] + } } - runtime.clearTimeout(timeoutHandle); - timeoutHandle = runtime.setTimeout(function() { - cachedXOffset = undefined - }, 2E3); - range.detach(); - return positions - left + return p } - this.movePointForward = function(positions, extend) { - return doMove(positions, extend || false, positionIterator.nextPosition) + makePattern = function makePattern(pattern, elements) { + var p, i; + if(pattern.name === "elementref") { + p = pattern.id || 0; + pattern = elements[p]; + if(pattern.name !== undefined) { + return resolveElement(pattern, elements) + } + return pattern + } + switch(pattern.name) { + case "empty": + return empty; + case "notAllowed": + return notAllowed; + case "text": + return text; + case "choice": + return createChoice(makePattern(pattern.e[0], elements), makePattern(pattern.e[1], elements)); + case "interleave": + p = makePattern(pattern.e[0], elements); + for(i = 1;i < pattern.e.length;i += 1) { + p = createInterleave(p, makePattern(pattern.e[i], elements)) + } + return p; + case "group": + return createGroup(makePattern(pattern.e[0], elements), makePattern(pattern.e[1], elements)); + case "oneOrMore": + return createOneOrMore(makePattern(pattern.e[0], elements)); + case "attribute": + return createAttribute(createNameClass(pattern.e[0]), makePattern(pattern.e[1], elements)); + case "value": + return createValue(pattern.text); + case "data": + p = pattern.a && pattern.a.type; + if(p === undefined) { + p = "" + } + return createData(p); + case "list": + return createList() + } + throw"No support for " + pattern.name; + }; + this.makePattern = function(pattern, elements) { + var copy = {}, i; + for(i in elements) { + if(elements.hasOwnProperty(i)) { + copy[i] = elements[i] + } + } + i = makePattern(pattern, copy); + return i }; - this.movePointBackward = function(positions, extend) { - return doMove(positions, extend || false, positionIterator.previousPosition) + this.validate = function validate(walker, callback) { + var errors; + walker.currentNode = walker.root; + errors = childDeriv(null, rootPattern, walker); + if(!errors.nullable) { + runtime.log("Error in Relax NG validation: " + errors); + callback(["Error in Relax NG validation: " + errors]) + }else { + callback(null) + } }; - function isPositionWalkable(filter) { - var iterator = getIteratorAtCursor(); - if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { - iterator.setUnfilteredPosition(cursor.getAnchorNode(), 0); - if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { - return true + this.init = function init(rootPattern1) { + rootPattern = rootPattern1 + } +}; +runtime.loadClass("xmldom.RelaxNGParser"); +xmldom.RelaxNG2 = function RelaxNG2() { + var start, validateNonEmptyPattern, nsmap; + function RelaxNGParseError(error, context) { + this.message = function() { + if(context) { + error += context.nodeType === Node.ELEMENT_NODE ? " Element " : " Node "; + error += context.nodeName; + if(context.nodeValue) { + error += " with value '" + context.nodeValue + "'" + } + error += "." } + return error } - return false } - function countSteps(iterator, steps, filter) { - var watch = new core.LoopWatchDog(1E4), positions = 0, positionsCount = 0, increment = steps >= 0 ? 1 : -1, delegate = (steps >= 0 ? iterator.nextPosition : iterator.previousPosition); - while(steps !== 0 && delegate()) { - watch.check(); - positionsCount += increment; - if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { - steps -= increment; - positions += positionsCount; - positionsCount = 0 - } + function validateOneOrMore(elementdef, walker, element) { + var node, i = 0, err; + do { + node = walker.currentNode; + err = validateNonEmptyPattern(elementdef.e[0], walker, element); + i += 1 + }while(!err && node !== walker.currentNode); + if(i > 1) { + walker.currentNode = node; + return null } - return positions + return err } - function convertForwardStepsBetweenFilters(stepsFilter1, filter1, filter2) { - var iterator = getIteratorAtCursor(), watch = new core.LoopWatchDog(1E4), pendingStepsFilter2 = 0, stepsFilter2 = 0; - while(stepsFilter1 > 0 && iterator.nextPosition()) { - watch.check(); - if(filter2.acceptPosition(iterator) === FILTER_ACCEPT) { - pendingStepsFilter2 += 1; - if(filter1.acceptPosition(iterator) === FILTER_ACCEPT) { - stepsFilter2 += pendingStepsFilter2; - pendingStepsFilter2 = 0; - stepsFilter1 -= 1 - } - } + function qName(node) { + return nsmap[node.namespaceURI] + ":" + node.localName + } + function isWhitespace(node) { + return node && (node.nodeType === Node.TEXT_NODE && /^\s+$/.test(node.nodeValue)) + } + function validatePattern(elementdef, walker, element, data) { + if(elementdef.name === "empty") { + return null } - return stepsFilter2 + return validateNonEmptyPattern(elementdef, walker, element, data) } - function convertBackwardStepsBetweenFilters(stepsFilter1, filter1, filter2) { - var iterator = getIteratorAtCursor(), watch = new core.LoopWatchDog(1E4), pendingStepsFilter2 = 0, stepsFilter2 = 0; - while(stepsFilter1 > 0 && iterator.previousPosition()) { - watch.check(); - if(filter2.acceptPosition(iterator) === FILTER_ACCEPT) { - pendingStepsFilter2 += 1; - if(filter1.acceptPosition(iterator) === FILTER_ACCEPT) { - stepsFilter2 += pendingStepsFilter2; - pendingStepsFilter2 = 0; - stepsFilter1 -= 1 - } + function validateAttribute(elementdef, walker, element) { + if(elementdef.e.length !== 2) { + throw"Attribute with wrong # of elements: " + elementdef.e.length; + } + var att, a, l = elementdef.localnames.length, i; + for(i = 0;i < l;i += 1) { + a = element.getAttributeNS(elementdef.namespaces[i], elementdef.localnames[i]); + if(a === "" && !element.hasAttributeNS(elementdef.namespaces[i], elementdef.localnames[i])) { + a = undefined + } + if(att !== undefined && a !== undefined) { + return[new RelaxNGParseError("Attribute defined too often.", element)] } + att = a } - return stepsFilter2 + if(att === undefined) { + return[new RelaxNGParseError("Attribute not found: " + elementdef.names, element)] + } + return validatePattern(elementdef.e[1], walker, element, att) } - function countStepsPublic(steps, filter) { - var iterator = getIteratorAtCursor(); - return countSteps(iterator, steps, filter) + function validateTop(elementdef, walker, element) { + return validatePattern(elementdef, walker, element) } - function countPositionsToClosestStep(container, offset, filter) { - var iterator = getIteratorAtCursor(), paragraphNode = odfUtils.getParagraphElement(iterator.getCurrentNode()), count = 0; - iterator.setUnfilteredPosition(container, offset); - if(filter.acceptPosition(iterator) !== FILTER_ACCEPT) { - count = countSteps(iterator, -1, filter); - if(count === 0 || paragraphNode && paragraphNode !== odfUtils.getParagraphElement(iterator.getCurrentNode())) { - iterator.setUnfilteredPosition(container, offset); - count = countSteps(iterator, 1, filter) + function validateElement(elementdef, walker) { + if(elementdef.e.length !== 2) { + throw"Element with wrong # of elements: " + elementdef.e.length; + } + var node = walker.currentNode, type = node ? node.nodeType : 0, error = null; + while(type > Node.ELEMENT_NODE) { + if(type !== Node.COMMENT_NODE && (type !== Node.TEXT_NODE || !/^\s+$/.test(walker.currentNode.nodeValue))) { + return[new RelaxNGParseError("Not allowed node of type " + type + ".")] } + node = walker.nextSibling(); + type = node ? node.nodeType : 0 } - return count - } - function countLineSteps(filter, direction, iterator) { - var c = iterator.container(), steps = 0, bestContainer = null, bestOffset, bestXDiff = 10, xDiff, bestCount = 0, top, left, lastTop, rect, range = (rootNode.ownerDocument.createRange()), watch = new core.LoopWatchDog(1E4); - rect = getVisibleRect(c, iterator.unfilteredDomOffset(), range); - top = rect.top; - if(cachedXOffset === undefined) { - left = rect.left - }else { - left = cachedXOffset + if(!node) { + return[new RelaxNGParseError("Missing element " + elementdef.names)] } - lastTop = top; - while((direction < 0 ? iterator.previousPosition() : iterator.nextPosition()) === true) { - watch.check(); - if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { - steps += 1; - c = iterator.container(); - rect = getVisibleRect(c, iterator.unfilteredDomOffset(), range); - if(rect.top !== top) { - if(rect.top !== lastTop && lastTop !== top) { - break - } - lastTop = rect.top; - xDiff = Math.abs(left - rect.left); - if(bestContainer === null || xDiff < bestXDiff) { - bestContainer = c; - bestOffset = iterator.unfilteredDomOffset(); - bestXDiff = xDiff; - bestCount = steps - } + if(elementdef.names && elementdef.names.indexOf(qName(node)) === -1) { + return[new RelaxNGParseError("Found " + node.nodeName + " instead of " + elementdef.names + ".", node)] + } + if(walker.firstChild()) { + error = validateTop(elementdef.e[1], walker, node); + while(walker.nextSibling()) { + type = walker.currentNode.nodeType; + if(!isWhitespace(walker.currentNode) && type !== Node.COMMENT_NODE) { + return[new RelaxNGParseError("Spurious content.", walker.currentNode)] } } - } - if(bestContainer !== null) { - iterator.setUnfilteredPosition(bestContainer, (bestOffset)); - steps = bestCount + if(walker.parentNode() !== node) { + return[new RelaxNGParseError("Implementation error.")] + } }else { - steps = 0 + error = validateTop(elementdef.e[1], walker, node) } - range.detach(); - return steps + node = walker.nextSibling(); + return error } - function countLinesSteps(lines, filter) { - var iterator = getIteratorAtCursor(), stepCount = 0, steps = 0, direction = lines < 0 ? -1 : 1; - lines = Math.abs(lines); - while(lines > 0) { - stepCount += countLineSteps(filter, direction, iterator); - if(stepCount === 0) { - break + function validateChoice(elementdef, walker, element, data) { + if(elementdef.e.length !== 2) { + throw"Choice with wrong # of options: " + elementdef.e.length; + } + var node = walker.currentNode, err; + if(elementdef.e[0].name === "empty") { + err = validateNonEmptyPattern(elementdef.e[1], walker, element, data); + if(err) { + walker.currentNode = node } - steps += stepCount; - lines -= 1 + return null } - return steps * direction - } - function countStepsToLineBoundary(direction, filter) { - var fnNextPos, increment, lastRect, rect, onSameLine, iterator = getIteratorAtCursor(), paragraphNode = odfUtils.getParagraphElement(iterator.getCurrentNode()), steps = 0, range = (rootNode.ownerDocument.createRange()); - if(direction < 0) { - fnNextPos = iterator.previousPosition; - increment = -1 - }else { - fnNextPos = iterator.nextPosition; - increment = 1 + err = validatePattern(elementdef.e[0], walker, element, data); + if(err) { + walker.currentNode = node; + err = validateNonEmptyPattern(elementdef.e[1], walker, element, data) } - lastRect = getVisibleRect(iterator.container(), iterator.unfilteredDomOffset(), range); - while(fnNextPos.call(iterator)) { - if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { - if(odfUtils.getParagraphElement(iterator.getCurrentNode()) !== paragraphNode) { - break - } - rect = getVisibleRect(iterator.container(), iterator.unfilteredDomOffset(), range); - if(rect.bottom !== lastRect.bottom) { - onSameLine = rect.top >= lastRect.top && rect.bottom < lastRect.bottom || rect.top <= lastRect.top && rect.bottom > lastRect.bottom; - if(!onSameLine) { - break + return err + } + function validateInterleave(elementdef, walker, element) { + var l = elementdef.e.length, n = [l], err, i, todo = l, donethisround, node, subnode, e; + while(todo > 0) { + donethisround = 0; + node = walker.currentNode; + for(i = 0;i < l;i += 1) { + subnode = walker.currentNode; + if(n[i] !== true && n[i] !== subnode) { + e = elementdef.e[i]; + err = validateNonEmptyPattern(e, walker, element); + if(err) { + walker.currentNode = subnode; + if(n[i] === undefined) { + n[i] = false + } + }else { + if(subnode === walker.currentNode || (e.name === "oneOrMore" || e.name === "choice" && (e.e[0].name === "oneOrMore" || e.e[1].name === "oneOrMore"))) { + donethisround += 1; + n[i] = subnode + }else { + donethisround += 1; + n[i] = true + } + } + } + } + if(node === walker.currentNode && donethisround === todo) { + return null + } + if(donethisround === 0) { + for(i = 0;i < l;i += 1) { + if(n[i] === false) { + return[new RelaxNGParseError("Interleave does not match.", element)] } } - steps += increment; - lastRect = rect + return null + } + todo = 0; + for(i = 0;i < l;i += 1) { + if(n[i] !== true) { + todo += 1 + } } } - range.detach(); - return steps + return null } - function countStepsToPosition(targetNode, targetOffset, filter) { - runtime.assert(targetNode !== null, "SelectionMover.countStepsToPosition called with element===null"); - var iterator = getIteratorAtCursor(), c = iterator.container(), o = iterator.unfilteredDomOffset(), steps = 0, watch = new core.LoopWatchDog(1E4), comparison; - iterator.setUnfilteredPosition(targetNode, targetOffset); - while(filter.acceptPosition(iterator) !== FILTER_ACCEPT && iterator.previousPosition()) { - watch.check() - } - targetNode = iterator.container(); - runtime.assert(Boolean(targetNode), "SelectionMover.countStepsToPosition: positionIterator.container() returned null"); - targetOffset = iterator.unfilteredDomOffset(); - iterator.setUnfilteredPosition(c, o); - while(filter.acceptPosition(iterator) !== FILTER_ACCEPT && iterator.previousPosition()) { - watch.check() + function validateGroup(elementdef, walker, element) { + if(elementdef.e.length !== 2) { + throw"Group with wrong # of members: " + elementdef.e.length; } - comparison = domUtils.comparePoints(targetNode, targetOffset, iterator.container(), iterator.unfilteredDomOffset()); - if(comparison < 0) { - while(iterator.nextPosition()) { - watch.check(); - if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { - steps += 1 - } - if(iterator.container() === targetNode && iterator.unfilteredDomOffset() === targetOffset) { - return steps - } + return validateNonEmptyPattern(elementdef.e[0], walker, element) || validateNonEmptyPattern(elementdef.e[1], walker, element) + } + function validateText(elementdef, walker, element) { + var node = walker.currentNode, type = node ? node.nodeType : 0; + while(node !== element && type !== 3) { + if(type === 1) { + return[new RelaxNGParseError("Element not allowed here.", node)] } + node = walker.nextSibling(); + type = node ? node.nodeType : 0 + } + walker.nextSibling(); + return null + } + validateNonEmptyPattern = function validateNonEmptyPattern(elementdef, walker, element, data) { + var name = elementdef.name, err = null; + if(name === "text") { + err = validateText(elementdef, walker, element) }else { - if(comparison > 0) { - while(iterator.previousPosition()) { - watch.check(); - if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { - steps -= 1; - if(iterator.container() === targetNode && iterator.unfilteredDomOffset() === targetOffset) { - break + if(name === "data") { + err = null + }else { + if(name === "value") { + if(data !== elementdef.text) { + err = [new RelaxNGParseError("Wrong value, should be '" + elementdef.text + "', not '" + data + "'", element)] + } + }else { + if(name === "list") { + err = null + }else { + if(name === "attribute") { + err = validateAttribute(elementdef, walker, element) + }else { + if(name === "element") { + err = validateElement(elementdef, walker) + }else { + if(name === "oneOrMore") { + err = validateOneOrMore(elementdef, walker, element) + }else { + if(name === "choice") { + err = validateChoice(elementdef, walker, element, data) + }else { + if(name === "group") { + err = validateGroup(elementdef, walker, element) + }else { + if(name === "interleave") { + err = validateInterleave(elementdef, walker, element) + }else { + throw name + " not allowed in nonEmptyPattern."; + } + } + } + } + } } } } } } - return steps - } - this.getStepCounter = function() { - return{countSteps:countStepsPublic, convertForwardStepsBetweenFilters:convertForwardStepsBetweenFilters, convertBackwardStepsBetweenFilters:convertBackwardStepsBetweenFilters, countLinesSteps:countLinesSteps, countStepsToLineBoundary:countStepsToLineBoundary, countStepsToPosition:countStepsToPosition, isPositionWalkable:isPositionWalkable, countPositionsToNearestStep:countPositionsToClosestStep} + return err }; - function init() { - odfUtils = new odf.OdfUtils; - domUtils = new core.DomUtils; - positionIterator = gui.SelectionMover.createPositionIterator(rootNode); - var range = rootNode.ownerDocument.createRange(); - range.setStart(positionIterator.container(), positionIterator.unfilteredDomOffset()); - range.collapse(true); - cursor.setSelectedRange(range) - } - init() -}; -gui.SelectionMover.createPositionIterator = function(rootNode) { - function CursorFilter() { - this.acceptNode = function(node) { - if(!node || (node.namespaceURI === "urn:webodf:names:cursor" || node.namespaceURI === "urn:webodf:names:editinfo")) { - return NodeFilter.FILTER_REJECT - } - return NodeFilter.FILTER_ACCEPT - } + this.validate = function validate(walker, callback) { + walker.currentNode = walker.root; + var errors = validatePattern(start.e[0], walker, (walker.root)); + callback(errors) + }; + this.init = function init(start1, nsmap1) { + start = start1; + nsmap = nsmap1 } - var filter = new CursorFilter; - return new core.PositionIterator(rootNode, 5, filter, false) }; -(function() { - return gui.SelectionMover -})(); -/* - - Copyright (C) 2012-2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ runtime.loadClass("core.DomUtils"); -runtime.loadClass("core.PositionFilter"); -runtime.loadClass("odf.OdfUtils"); -(function() { - var nextNodeId = 0; - function StepsCache(rootNode, filter, bucketSize) { - var coordinatens = "urn:webodf:names:steps", stepToDomPoint = {}, nodeToBookmark = {}, odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils, basePoint, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; - function ParagraphBookmark(steps, paragraphNode) { - this.steps = steps; - this.node = paragraphNode; - function positionInContainer(node) { - var position = 0; - while(node && node.previousSibling) { - position += 1; - node = node.previousSibling - } - return position - } - this.setIteratorPosition = function(iterator) { - iterator.setUnfilteredPosition(paragraphNode.parentNode, positionInContainer(paragraphNode)); - do { - if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { - break - } - }while(iterator.nextPosition()) - } +runtime.loadClass("gui.Avatar"); +runtime.loadClass("ops.OdtCursor"); +gui.Caret = function Caret(cursor, avatarInitiallyVisible, blinkOnRangeSelect) { + var MIN_CARET_HEIGHT_PX = 8, DEFAULT_CARET_TOP = "5%", DEFAULT_CARET_HEIGHT = "1em", span, avatar, cursorNode, isShown = true, shouldBlink = false, blinking = false, blinkTimeout, domUtils = new core.DomUtils; + function blink(reset) { + if(!shouldBlink || !cursorNode.parentNode) { + return } - function RootBookmark(steps, rootNode) { - this.steps = steps; - this.node = rootNode; - this.setIteratorPosition = function(iterator) { - iterator.setUnfilteredPosition(rootNode, 0); - do { - if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { - break - } - }while(iterator.nextPosition()) + if(!blinking || reset) { + if(reset && blinkTimeout !== undefined) { + runtime.clearTimeout(blinkTimeout) } + blinking = true; + span.style.opacity = reset || span.style.opacity === "0" ? "1" : "0"; + blinkTimeout = runtime.setTimeout(function() { + blinking = false; + blink(false) + }, 500) } - function getBucket(steps) { - return Math.floor(steps / bucketSize) * bucketSize - } - function getDestinationBucket(steps) { - return Math.ceil(steps / bucketSize) * bucketSize - } - function clearNodeId(node) { - node.removeAttributeNS(coordinatens, "nodeId") - } - function getNodeId(node) { - return node.nodeType === Node.ELEMENT_NODE && node.getAttributeNS(coordinatens, "nodeId") - } - function setNodeId(node) { - var nodeId = nextNodeId; - node.setAttributeNS(coordinatens, "nodeId", nodeId.toString()); - nextNodeId += 1; - return nodeId + } + function getCaretClientRectWithMargin(caretElement, margin) { + var caretRect = caretElement.getBoundingClientRect(); + return{left:caretRect.left - margin.left, top:caretRect.top - margin.top, right:caretRect.right + margin.right, bottom:caretRect.bottom + margin.bottom} + } + function length(node) { + return node.nodeType === Node.TEXT_NODE ? node.textContent.length : node.childNodes.length + } + function verticalOverlap(cursorNode, rangeRect) { + var cursorRect = cursorNode.getBoundingClientRect(), intersectTop = 0, intersectBottom = 0; + if(cursorRect && rangeRect) { + intersectTop = Math.max(cursorRect.top, rangeRect.top); + intersectBottom = Math.min(cursorRect.bottom, rangeRect.bottom) } - function isValidBookmarkForNode(node, bookmark) { - return bookmark.node === node + return intersectBottom - intersectTop + } + function getSelectionRect() { + var range = cursor.getSelectedRange().cloneRange(), node = cursor.getNode(), nextRectangle, selectionRectangle = null, nodeLength; + if(node.previousSibling) { + nodeLength = length(node.previousSibling); + range.setStart(node.previousSibling, nodeLength > 0 ? nodeLength - 1 : 0); + range.setEnd(node.previousSibling, nodeLength); + nextRectangle = range.getBoundingClientRect(); + if(nextRectangle && nextRectangle.height) { + selectionRectangle = nextRectangle + } } - function getNodeBookmark(node, steps) { - var nodeId = getNodeId(node) || setNodeId(node), existingBookmark; - existingBookmark = nodeToBookmark[nodeId]; - if(!existingBookmark) { - existingBookmark = nodeToBookmark[nodeId] = new ParagraphBookmark(steps, node) - }else { - if(!isValidBookmarkForNode(node, existingBookmark)) { - runtime.log("Cloned node detected. Creating new bookmark"); - nodeId = setNodeId(node); - existingBookmark = nodeToBookmark[nodeId] = new ParagraphBookmark(steps, node) - }else { - existingBookmark.steps = steps + if(node.nextSibling) { + range.setStart(node.nextSibling, 0); + range.setEnd(node.nextSibling, length(node.nextSibling) > 0 ? 1 : 0); + nextRectangle = range.getBoundingClientRect(); + if(nextRectangle && nextRectangle.height) { + if(!selectionRectangle || verticalOverlap(node, nextRectangle) > verticalOverlap(node, selectionRectangle)) { + selectionRectangle = nextRectangle } } - return existingBookmark } - function isFirstPositionInParagraph(node, offset) { - return offset === 0 && odfUtils.isParagraph(node) + return selectionRectangle + } + function handleUpdate() { + var selectionRect = getSelectionRect(), zoomLevel = cursor.getOdtDocument().getOdfCanvas().getZoomLevel(), caretRect; + if(isShown && cursor.getSelectionType() === ops.OdtCursor.RangeSelection) { + span.style.visibility = "visible" + }else { + span.style.visibility = "hidden" } - this.updateCache = function(steps, node, offset, isWalkable) { - var stablePoint, cacheBucket, existingCachePoint, bookmark; - if(isFirstPositionInParagraph(node, offset)) { - stablePoint = true; - if(!isWalkable) { - steps += 1 - } - }else { - if(node.hasChildNodes() && node.childNodes[offset]) { - node = node.childNodes[offset]; - offset = 0; - stablePoint = isFirstPositionInParagraph(node, offset); - if(stablePoint) { - steps += 1 - } - } - } - if(stablePoint) { - bookmark = getNodeBookmark(node, steps); - cacheBucket = getDestinationBucket(bookmark.steps); - existingCachePoint = stepToDomPoint[cacheBucket]; - if(!existingCachePoint || bookmark.steps > existingCachePoint.steps) { - stepToDomPoint[cacheBucket] = bookmark - } + if(selectionRect) { + span.style.top = "0"; + caretRect = domUtils.getBoundingClientRect(span); + if(selectionRect.height < MIN_CARET_HEIGHT_PX) { + selectionRect = {top:selectionRect.top - (MIN_CARET_HEIGHT_PX - selectionRect.height) / 2, height:MIN_CARET_HEIGHT_PX} } - }; - this.setToClosestStep = function(steps, iterator) { - var cacheBucket = getBucket(steps), cachePoint; - while(!cachePoint && cacheBucket !== 0) { - cachePoint = stepToDomPoint[cacheBucket]; - cacheBucket -= bucketSize + span.style.height = domUtils.adaptRangeDifferenceToZoomLevel(selectionRect.height, zoomLevel) + "px"; + span.style.top = domUtils.adaptRangeDifferenceToZoomLevel(selectionRect.top - caretRect.top, zoomLevel) + "px" + }else { + span.style.height = DEFAULT_CARET_HEIGHT; + span.style.top = DEFAULT_CARET_TOP + } + } + this.handleUpdate = handleUpdate; + this.refreshCursorBlinking = function() { + if(blinkOnRangeSelect || cursor.getSelectedRange().collapsed) { + shouldBlink = true; + blink(true) + }else { + shouldBlink = false; + span.style.opacity = "0" + } + }; + this.setFocus = function() { + shouldBlink = true; + avatar.markAsFocussed(true); + blink(true) + }; + this.removeFocus = function() { + shouldBlink = false; + avatar.markAsFocussed(false); + span.style.opacity = "1" + }; + this.show = function() { + isShown = true; + handleUpdate(); + avatar.markAsFocussed(true) + }; + this.hide = function() { + isShown = false; + handleUpdate(); + avatar.markAsFocussed(false) + }; + this.setAvatarImageUrl = function(url) { + avatar.setImageUrl(url) + }; + this.setColor = function(newColor) { + span.style.borderColor = newColor; + avatar.setColor(newColor) + }; + this.getCursor = function() { + return cursor + }; + this.getFocusElement = function() { + return span + }; + this.toggleHandleVisibility = function() { + if(avatar.isVisible()) { + avatar.hide() + }else { + avatar.show() + } + }; + this.showHandle = function() { + avatar.show() + }; + this.hideHandle = function() { + avatar.hide() + }; + this.ensureVisible = function() { + var canvasElement = cursor.getOdtDocument().getOdfCanvas().getElement(), canvasContainerElement = canvasElement.parentNode, caretRect, canvasContainerRect, horizontalMargin = canvasContainerElement.offsetWidth - canvasContainerElement.clientWidth + 5, verticalMargin = canvasContainerElement.offsetHeight - canvasContainerElement.clientHeight + 5; + caretRect = getCaretClientRectWithMargin(span, {top:verticalMargin, left:horizontalMargin, bottom:verticalMargin, right:horizontalMargin}); + canvasContainerRect = canvasContainerElement.getBoundingClientRect(); + if(caretRect.top < canvasContainerRect.top) { + canvasContainerElement.scrollTop -= canvasContainerRect.top - caretRect.top + }else { + if(caretRect.bottom > canvasContainerRect.bottom) { + canvasContainerElement.scrollTop += caretRect.bottom - canvasContainerRect.bottom } - cachePoint = cachePoint || basePoint; - cachePoint.setIteratorPosition(iterator); - return cachePoint.steps - }; - function findBookmarkedAncestor(node, offset) { - var nodeId, bookmark = null; - node = node.childNodes[offset] || node; - while(!bookmark && (node && node !== rootNode)) { - nodeId = getNodeId(node); - if(nodeId) { - bookmark = nodeToBookmark[nodeId]; - if(bookmark && !isValidBookmarkForNode(node, bookmark)) { - runtime.log("Cloned node detected. Creating new bookmark"); - bookmark = null; - clearNodeId(node) - } - } - node = node.parentNode + } + if(caretRect.left < canvasContainerRect.left) { + canvasContainerElement.scrollLeft -= canvasContainerRect.left - caretRect.left + }else { + if(caretRect.right > canvasContainerRect.right) { + canvasContainerElement.scrollLeft += caretRect.right - canvasContainerRect.right } - return bookmark } - this.setToClosestDomPoint = function(node, offset, iterator) { - var bookmark; - if(node === rootNode && offset === 0) { - bookmark = basePoint + handleUpdate() + }; + this.destroy = function(callback) { + avatar.destroy(function(err) { + if(err) { + callback(err) }else { - if(node === rootNode && offset === rootNode.childNodes.length) { - bookmark = Object.keys(stepToDomPoint).map(function(cacheBucket) { - return stepToDomPoint[cacheBucket] - }).reduce(function(largestBookmark, bookmark) { - return bookmark.steps > largestBookmark.steps ? bookmark : largestBookmark - }, basePoint) - }else { - bookmark = findBookmarkedAncestor(node, offset); - if(!bookmark) { - iterator.setUnfilteredPosition(node, offset); - while(!bookmark && iterator.previousNode()) { - bookmark = findBookmarkedAncestor(iterator.container(), iterator.unfilteredDomOffset()) - } - } - } + cursorNode.removeChild(span); + callback() + } + }) + }; + function init() { + var dom = cursor.getOdtDocument().getDOM(), htmlns = dom.documentElement.namespaceURI; + span = (dom.createElementNS(htmlns, "span")); + span.style.top = DEFAULT_CARET_TOP; + cursorNode = cursor.getNode(); + cursorNode.appendChild(span); + avatar = new gui.Avatar(cursorNode, avatarInitiallyVisible); + handleUpdate() + } + init() +}; +gui.EventManager = function EventManager(odtDocument) { + var canvasElement = odtDocument.getOdfCanvas().getElement(), window = runtime.getWindow(), bindToDirectHandler = {"beforecut":true, "beforepaste":true}, bindToWindow; + function EventDelegate() { + var self = this, recentEvents = []; + this.handlers = []; + this.isSubscribed = false; + this.handleEvent = function(e) { + if(recentEvents.indexOf(e) === -1) { + recentEvents.push(e); + self.handlers.forEach(function(handler) { + handler(e) + }); + runtime.setTimeout(function() { + recentEvents.splice(recentEvents.indexOf(e), 1) + }, 0) } - bookmark = bookmark || basePoint; - bookmark.setIteratorPosition(iterator); - return bookmark.steps - }; - this.updateCacheAtPoint = function(inflectionStep, doUpdate) { - var affectedBookmarks, updatedBuckets = {}; - affectedBookmarks = Object.keys(nodeToBookmark).map(function(nodeId) { - return nodeToBookmark[nodeId] - }).filter(function(bookmark) { - return bookmark.steps > inflectionStep - }); - affectedBookmarks.forEach(function(bookmark) { - var originalCacheBucket = getDestinationBucket(bookmark.steps), newCacheBucket, existingBookmark; - if(domUtils.containsNode(rootNode, bookmark.node)) { - doUpdate(bookmark); - newCacheBucket = getDestinationBucket(bookmark.steps); - existingBookmark = updatedBuckets[newCacheBucket]; - if(!existingBookmark || bookmark.steps > existingBookmark.steps) { - updatedBuckets[newCacheBucket] = bookmark - } - }else { - delete nodeToBookmark[getNodeId(bookmark.node)] - } - if(stepToDomPoint[originalCacheBucket] === bookmark) { - delete stepToDomPoint[originalCacheBucket] - } - }); - Object.keys(updatedBuckets).forEach(function(cacheBucket) { - stepToDomPoint[cacheBucket] = updatedBuckets[cacheBucket] - }) - }; - function init() { - basePoint = new RootBookmark(0, rootNode) } - init() } - ops.StepsTranslator = function StepsTranslator(getRootNode, newIterator, filter, bucketSize) { - var rootNode = getRootNode(), stepsCache = new StepsCache(rootNode, filter, bucketSize), domUtils = new core.DomUtils, iterator = newIterator(getRootNode()), FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; - function verifyRootNode() { - var currentRootNode = getRootNode(); - if(currentRootNode !== rootNode) { - runtime.log("Undo detected. Resetting steps cache"); - rootNode = currentRootNode; - stepsCache = new StepsCache(rootNode, filter, bucketSize); - iterator = newIterator(rootNode) + function WindowScrollState(window) { + var x = window.scrollX, y = window.scrollY; + this.restore = function() { + if(window.scrollX !== x || window.scrollY !== y) { + window.scrollTo(x, y) } } - this.convertStepsToDomPoint = function(steps) { - var stepsFromRoot, isWalkable; - if(steps < 0) { - runtime.log("warn", "Requested steps were negative (" + steps + ")"); - steps = 0 - } - verifyRootNode(); - stepsFromRoot = stepsCache.setToClosestStep(steps, iterator); - while(stepsFromRoot < steps && iterator.nextPosition()) { - isWalkable = filter.acceptPosition(iterator) === FILTER_ACCEPT; - if(isWalkable) { - stepsFromRoot += 1 - } - stepsCache.updateCache(stepsFromRoot, iterator.container(), iterator.unfilteredDomOffset(), isWalkable) - } - if(stepsFromRoot !== steps) { - runtime.log("warn", "Requested " + steps + " steps but only " + stepsFromRoot + " are available") - } - return{node:iterator.container(), offset:iterator.unfilteredDomOffset()} - }; - this.convertDomPointToSteps = function(node, offset, roundUp) { - var stepsFromRoot, beforeRoot, destinationNode, destinationOffset, rounding = 0, isWalkable; - verifyRootNode(); - if(!domUtils.containsNode(rootNode, node)) { - beforeRoot = domUtils.comparePoints(rootNode, 0, node, offset) < 0; - node = rootNode; - offset = beforeRoot ? 0 : rootNode.childNodes.length - } - iterator.setUnfilteredPosition(node, offset); - destinationNode = iterator.container(); - destinationOffset = iterator.unfilteredDomOffset(); - if(roundUp && filter.acceptPosition(iterator) !== FILTER_ACCEPT) { - rounding = 1 + } + function ElementScrollState(element) { + var top = element.scrollTop, left = element.scrollLeft; + this.restore = function() { + if(element.scrollTop !== top || element.scrollLeft !== left) { + element.scrollTop = top; + element.scrollLeft = left } - stepsFromRoot = stepsCache.setToClosestDomPoint(node, offset, iterator); - if(domUtils.comparePoints(iterator.container(), iterator.unfilteredDomOffset(), destinationNode, destinationOffset) < 0) { - return stepsFromRoot > 0 && !roundUp ? stepsFromRoot - 1 : stepsFromRoot + } + } + function listenEvent(eventTarget, eventType, eventHandler) { + var onVariant = "on" + eventType, bound = false; + if(eventTarget.attachEvent) { + bound = eventTarget.attachEvent(onVariant, eventHandler) + } + if(!bound && eventTarget.addEventListener) { + eventTarget.addEventListener(eventType, eventHandler, false); + bound = true + } + if((!bound || bindToDirectHandler[eventType]) && eventTarget.hasOwnProperty(onVariant)) { + eventTarget[onVariant] = eventHandler + } + } + function removeEvent(eventTarget, eventType, eventHandler) { + var onVariant = "on" + eventType; + if(eventTarget.detachEvent) { + eventTarget.detachEvent(onVariant, eventHandler) + } + if(eventTarget.removeEventListener) { + eventTarget.removeEventListener(eventType, eventHandler, false) + } + if(eventTarget[onVariant] === eventHandler) { + eventTarget[onVariant] = null + } + } + this.subscribe = function(eventName, handler) { + var delegate = window && bindToWindow[eventName]; + if(delegate) { + delegate.handlers.push(handler); + if(!delegate.isSubscribed) { + delegate.isSubscribed = true; + listenEvent((window), eventName, delegate.handleEvent); + listenEvent(canvasElement, eventName, delegate.handleEvent) } - while(!(iterator.container() === destinationNode && iterator.unfilteredDomOffset() === destinationOffset) && iterator.nextPosition()) { - isWalkable = filter.acceptPosition(iterator) === FILTER_ACCEPT; - if(isWalkable) { - stepsFromRoot += 1 - } - stepsCache.updateCache(stepsFromRoot, iterator.container(), iterator.unfilteredDomOffset(), isWalkable) + }else { + listenEvent(canvasElement, eventName, handler) + } + }; + this.unsubscribe = function(eventName, handler) { + var delegate = window && bindToWindow[eventName], handlerIndex = delegate && delegate.handlers.indexOf(handler); + if(delegate) { + if(handlerIndex !== -1) { + delegate.handlers.splice(handlerIndex, 1) } - return stepsFromRoot + rounding - }; - this.prime = function() { - var stepsFromRoot, isWalkable; - verifyRootNode(); - stepsFromRoot = stepsCache.setToClosestStep(0, iterator); - while(iterator.nextPosition()) { - isWalkable = filter.acceptPosition(iterator) === FILTER_ACCEPT; - if(isWalkable) { - stepsFromRoot += 1 - } - stepsCache.updateCache(stepsFromRoot, iterator.container(), iterator.unfilteredDomOffset(), isWalkable) + }else { + removeEvent(canvasElement, eventName, handler) + } + }; + function hasFocus() { + var activeElement = odtDocument.getDOM().activeElement; + return activeElement === canvasElement + } + this.hasFocus = hasFocus; + function findScrollableParent(element) { + while(element && (!element.scrollTop && !element.scrollLeft)) { + element = (element.parentNode) + } + if(element) { + return new ElementScrollState(element) + } + if(window) { + return new WindowScrollState(window) + } + return null + } + this.focus = function() { + var scrollParent; + if(!hasFocus()) { + scrollParent = findScrollableParent(canvasElement); + canvasElement.focus(); + if(scrollParent) { + scrollParent.restore() } - }; - this.handleStepsInserted = function(eventArgs) { - verifyRootNode(); - stepsCache.updateCacheAtPoint(eventArgs.position, function(bucket) { - bucket.steps += eventArgs.length - }) - }; - this.handleStepsRemoved = function(eventArgs) { - verifyRootNode(); - stepsCache.updateCacheAtPoint(eventArgs.position, function(bucket) { - bucket.steps -= eventArgs.length; - if(bucket.steps < 0) { - bucket.steps = 0 - } - }) } }; - return ops.StepsTranslator + function init() { + bindToWindow = {"mousedown":new EventDelegate, "mouseup":new EventDelegate} + } + init() +}; +runtime.loadClass("gui.SelectionMover"); +gui.ShadowCursor = function ShadowCursor(odtDocument) { + var selectedRange = (odtDocument.getDOM().createRange()), forwardSelection = true; + this.removeFromOdtDocument = function() { + }; + this.getMemberId = function() { + return gui.ShadowCursor.ShadowCursorMemberId + }; + this.getSelectedRange = function() { + return selectedRange + }; + this.setSelectedRange = function(range, isForwardSelection) { + selectedRange = range; + forwardSelection = isForwardSelection !== false + }; + this.hasForwardSelection = function() { + return forwardSelection + }; + this.getOdtDocument = function() { + return odtDocument + }; + this.getSelectionType = function() { + return ops.OdtCursor.RangeSelection + }; + function init() { + selectedRange.setStart(odtDocument.getRootNode(), 0) + } + init() +}; +gui.ShadowCursor.ShadowCursorMemberId = ""; +(function() { + return gui.ShadowCursor +})(); +/* + + Copyright (C) 2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +gui.UndoManager = function UndoManager() { +}; +gui.UndoManager.prototype.subscribe = function(signal, callback) { +}; +gui.UndoManager.prototype.unsubscribe = function(signal, callback) { +}; +gui.UndoManager.prototype.setOdtDocument = function(newDocument) { +}; +gui.UndoManager.prototype.saveInitialState = function() { +}; +gui.UndoManager.prototype.resetInitialState = function() { +}; +gui.UndoManager.prototype.setPlaybackFunction = function(playback_func) { +}; +gui.UndoManager.prototype.hasUndoStates = function() { +}; +gui.UndoManager.prototype.hasRedoStates = function() { +}; +gui.UndoManager.prototype.moveForward = function(states) { +}; +gui.UndoManager.prototype.moveBackward = function(states) { +}; +gui.UndoManager.prototype.onOperationExecuted = function(op) { +}; +gui.UndoManager.signalUndoStackChanged = "undoStackChanged"; +gui.UndoManager.signalUndoStateCreated = "undoStateCreated"; +gui.UndoManager.signalUndoStateModified = "undoStateModified"; +(function() { + return gui.UndoManager })(); /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -13077,691 +12706,1101 @@ runtime.loadClass("odf.OdfUtils"); @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.PositionFilter"); -runtime.loadClass("odf.OdfUtils"); -ops.TextPositionFilter = function TextPositionFilter(getRootNode) { - var odfUtils = new odf.OdfUtils, ELEMENT_NODE = Node.ELEMENT_NODE, TEXT_NODE = Node.TEXT_NODE, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, FILTER_REJECT = core.PositionFilter.FilterResult.FILTER_REJECT; - function checkLeftRight(container, leftNode, rightNode) { - var r, firstPos, rightOfChar; - if(leftNode) { - r = odfUtils.lookLeftForCharacter(leftNode); - if(r === 1) { - return FILTER_ACCEPT - } - if(r === 2 && (odfUtils.scanRightForAnyCharacter(rightNode) || odfUtils.scanRightForAnyCharacter(odfUtils.nextNode(container)))) { - return FILTER_ACCEPT - } - } - firstPos = leftNode === null && odfUtils.isParagraph(container); - rightOfChar = odfUtils.lookRightForCharacter(rightNode); - if(firstPos) { - if(rightOfChar) { - return FILTER_ACCEPT - } - return odfUtils.scanRightForAnyCharacter(rightNode) ? FILTER_REJECT : FILTER_ACCEPT - } - if(!rightOfChar) { - return FILTER_REJECT - } - leftNode = leftNode || odfUtils.previousNode(container); - return odfUtils.scanLeftForAnyCharacter(leftNode) ? FILTER_REJECT : FILTER_ACCEPT +gui.UndoStateRules = function UndoStateRules() { + function getOpType(op) { + return op.spec().optype } - this.acceptPosition = function(iterator) { - var container = iterator.container(), nodeType = container.nodeType, offset, text, leftChar, rightChar, leftNode, rightNode, r; - if(nodeType !== ELEMENT_NODE && nodeType !== TEXT_NODE) { - return FILTER_REJECT + this.getOpType = getOpType; + function getOpPosition(op) { + return op.spec().position + } + function isEditOperation(op) { + return op.isEdit + } + this.isEditOperation = isEditOperation; + function canAggregateOperation(optype) { + switch(optype) { + case "RemoveText": + ; + case "InsertText": + return true; + default: + return false } - if(nodeType === TEXT_NODE) { - if(!odfUtils.isGroupingElement(container.parentNode) || odfUtils.isWithinTrackedChanges(container.parentNode, getRootNode())) { - return FILTER_REJECT + } + function isSameDirectionOfTravel(recentEditOps, thisOp) { + var existing1 = getOpPosition(recentEditOps[recentEditOps.length - 2]), existing2 = getOpPosition(recentEditOps[recentEditOps.length - 1]), thisPos = getOpPosition(thisOp), direction = existing2 - existing1; + return existing2 === thisPos - direction + } + function isContinuousOperation(recentEditOps, thisOp) { + var optype = getOpType(thisOp); + if(canAggregateOperation(optype) && optype === getOpType(recentEditOps[0])) { + if(recentEditOps.length === 1) { + return true } - offset = iterator.unfilteredDomOffset(); - text = container.data; - runtime.assert(offset !== text.length, "Unexpected offset."); - if(offset > 0) { - leftChar = text[offset - 1]; - if(!odfUtils.isODFWhitespace(leftChar)) { - return FILTER_ACCEPT - } - if(offset > 1) { - leftChar = text[offset - 2]; - if(!odfUtils.isODFWhitespace(leftChar)) { - r = FILTER_ACCEPT - }else { - if(!odfUtils.isODFWhitespace(text.substr(0, offset))) { - return FILTER_REJECT - } - } - }else { - leftNode = odfUtils.previousNode(container); - if(odfUtils.scanLeftForNonSpace(leftNode)) { - r = FILTER_ACCEPT - } - } - if(r === FILTER_ACCEPT) { - return odfUtils.isTrailingWhitespace(container, offset) ? FILTER_REJECT : FILTER_ACCEPT - } - rightChar = text[offset]; - if(odfUtils.isODFWhitespace(rightChar)) { - return FILTER_REJECT - } - return odfUtils.scanLeftForAnyCharacter(odfUtils.previousNode(container)) ? FILTER_REJECT : FILTER_ACCEPT + if(isSameDirectionOfTravel(recentEditOps, thisOp)) { + return true } - leftNode = iterator.leftNode(); - rightNode = container; - container = (container.parentNode); - r = checkLeftRight(container, leftNode, rightNode) - }else { - if(!odfUtils.isGroupingElement(container) || odfUtils.isWithinTrackedChanges(container, getRootNode())) { - r = FILTER_REJECT - }else { - leftNode = iterator.leftNode(); - rightNode = iterator.rightNode(); - r = checkLeftRight(container, leftNode, rightNode) + } + return false + } + function isPartOfOperationSet(operation, lastOperations) { + if(isEditOperation(operation)) { + if(lastOperations.length === 0) { + return true } + return isEditOperation(lastOperations[lastOperations.length - 1]) && isContinuousOperation(lastOperations.filter(isEditOperation), operation) } - return r + return true } + this.isPartOfOperationSet = isPartOfOperationSet }; /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012 KO GmbH @licstart - This file is part of WebODF. + 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. - WebODF 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. + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License 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. - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend + 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: https://github.com/kogmbh/WebODF/ */ -ops.OperationTransformMatrix = function OperationTransformMatrix() { - function invertMoveCursorSpecRange(moveCursorSpec) { - moveCursorSpec.position = moveCursorSpec.position + moveCursorSpec.length; - moveCursorSpec.length *= -1 - } - function invertMoveCursorSpecRangeOnNegativeLength(moveCursorSpec) { - var isBackwards = moveCursorSpec.length < 0; - if(isBackwards) { - invertMoveCursorSpecRange(moveCursorSpec) +ops.EditInfo = function EditInfo(container, odtDocument) { + var editInfoNode, editHistory = {}; + function sortEdits() { + var arr = [], memberid; + for(memberid in editHistory) { + if(editHistory.hasOwnProperty(memberid)) { + arr.push({"memberid":memberid, "time":editHistory[memberid].time}) + } } - return isBackwards + arr.sort(function(a, b) { + return a.time - b.time + }); + return arr } - function getStyleReferencingAttributes(setProperties, styleName) { - var attributes = []; - if(setProperties) { - ["style:parent-style-name", "style:next-style-name"].forEach(function(attributeName) { - if(setProperties[attributeName] === styleName) { - attributes.push(attributeName) - } - }) + this.getNode = function() { + return editInfoNode + }; + this.getOdtDocument = function() { + return odtDocument + }; + this.getEdits = function() { + return editHistory + }; + this.getSortedEdits = function() { + return sortEdits() + }; + this.addEdit = function(memberid, timestamp) { + editHistory[memberid] = {time:timestamp} + }; + this.clearEdits = function() { + editHistory = {} + }; + this.destroy = function(callback) { + if(container.parentNode) { + container.removeChild(editInfoNode) } - return attributes + callback() + }; + function init() { + var editInfons = "urn:webodf:names:editinfo", dom = odtDocument.getDOM(); + editInfoNode = dom.createElementNS(editInfons, "editinfo"); + container.insertBefore(editInfoNode, container.firstChild) } - function dropStyleReferencingAttributes(setProperties, deletedStyleName) { - if(setProperties) { - ["style:parent-style-name", "style:next-style-name"].forEach(function(attributeName) { - if(setProperties[attributeName] === deletedStyleName) { - delete setProperties[attributeName] - } - }) - } + init() +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("core.DomUtils"); +ops.OpAddAnnotation = function OpAddAnnotation() { + var memberid, timestamp, position, length, name, doc; + this.init = function(data) { + memberid = data.memberid; + timestamp = parseInt(data.timestamp, 10); + position = parseInt(data.position, 10); + length = parseInt(data.length, 10) || 0; + name = data.name + }; + this.isEdit = true; + function createAnnotationNode(odtDocument, date) { + var annotationNode, creatorNode, dateNode, listNode, listItemNode, paragraphNode; + annotationNode = doc.createElementNS(odf.Namespaces.officens, "office:annotation"); + annotationNode.setAttributeNS(odf.Namespaces.officens, "office:name", name); + creatorNode = doc.createElementNS(odf.Namespaces.dcns, "dc:creator"); + creatorNode.setAttributeNS("urn:webodf:names:editinfo", "editinfo:memberid", memberid); + creatorNode.textContent = odtDocument.getMember(memberid).getProperties().fullName; + dateNode = doc.createElementNS(odf.Namespaces.dcns, "dc:date"); + dateNode.appendChild(doc.createTextNode(date.toISOString())); + listNode = doc.createElementNS(odf.Namespaces.textns, "text:list"); + listItemNode = doc.createElementNS(odf.Namespaces.textns, "text:list-item"); + paragraphNode = doc.createElementNS(odf.Namespaces.textns, "text:p"); + listItemNode.appendChild(paragraphNode); + listNode.appendChild(listItemNode); + annotationNode.appendChild(creatorNode); + annotationNode.appendChild(dateNode); + annotationNode.appendChild(listNode); + return annotationNode } - function cloneOpspec(opspec) { - var result = {}; - Object.keys(opspec).forEach(function(key) { - if(typeof opspec[key] === "object") { - result[key] = cloneOpspec(opspec[key]) - }else { - result[key] = opspec[key] - } - }); - return result + function createAnnotationEnd() { + var annotationEnd; + annotationEnd = doc.createElementNS(odf.Namespaces.officens, "office:annotation-end"); + annotationEnd.setAttributeNS(odf.Namespaces.officens, "office:name", name); + return annotationEnd } - function dropOverruledAndUnneededAttributes(minorSetProperties, minorRemovedProperties, majorSetProperties, majorRemovedProperties) { - var value, i, name, majorChanged = false, minorChanged = false, overrulingPropertyValue, removedPropertyNames, majorRemovedPropertyNames = majorRemovedProperties && majorRemovedProperties.attributes ? majorRemovedProperties.attributes.split(",") : []; - if(minorSetProperties && (majorSetProperties || majorRemovedPropertyNames.length > 0)) { - Object.keys(minorSetProperties).forEach(function(key) { - value = minorSetProperties[key]; - if(typeof value !== "object") { - overrulingPropertyValue = majorSetProperties && majorSetProperties[key]; - if(overrulingPropertyValue !== undefined) { - delete minorSetProperties[key]; - minorChanged = true; - if(overrulingPropertyValue === value) { - delete majorSetProperties[key]; - majorChanged = true - } - }else { - if(majorRemovedPropertyNames && majorRemovedPropertyNames.indexOf(key) !== -1) { - delete minorSetProperties[key]; - minorChanged = true - } - } - } - }) - } - if(minorRemovedProperties && (minorRemovedProperties.attributes && (majorSetProperties || majorRemovedPropertyNames.length > 0))) { - removedPropertyNames = minorRemovedProperties.attributes.split(","); - for(i = 0;i < removedPropertyNames.length;i += 1) { - name = removedPropertyNames[i]; - if(majorSetProperties && majorSetProperties[name] !== undefined || majorRemovedPropertyNames && majorRemovedPropertyNames.indexOf(name) !== -1) { - removedPropertyNames.splice(i, 1); - i -= 1; - minorChanged = true - } + function insertNodeAtPosition(odtDocument, node, insertPosition) { + var previousNode, parentNode, domPosition = odtDocument.getTextNodeAtStep(insertPosition, memberid); + if(domPosition) { + previousNode = domPosition.textNode; + parentNode = previousNode.parentNode; + if(domPosition.offset !== previousNode.length) { + previousNode.splitText(domPosition.offset) } - if(removedPropertyNames.length > 0) { - minorRemovedProperties.attributes = removedPropertyNames.join(",") - }else { - delete minorRemovedProperties.attributes + parentNode.insertBefore(node, previousNode.nextSibling); + if(previousNode.length === 0) { + parentNode.removeChild(previousNode) } } - return{majorChanged:majorChanged, minorChanged:minorChanged} } - function hasProperties(properties) { - var key; - for(key in properties) { - if(properties.hasOwnProperty(key)) { - return true - } + this.execute = function(odtDocument) { + var annotation = {}, cursor = odtDocument.getCursor(memberid), selectedRange, paragraphElement, domUtils = new core.DomUtils; + doc = odtDocument.getDOM(); + annotation.node = createAnnotationNode(odtDocument, new Date(timestamp)); + if(!annotation.node) { + return false } - return false - } - function hasRemovedProperties(properties) { - var key; - for(key in properties) { - if(properties.hasOwnProperty(key)) { - if(key !== "attributes" || properties.attributes.length > 0) { - return true - } + if(length) { + annotation.end = createAnnotationEnd(); + if(!annotation.end) { + return false } + insertNodeAtPosition(odtDocument, annotation.end, position + length) } - return false - } - function dropOverruledAndUnneededProperties(minorOpspec, majorOpspec, propertiesName) { - var minorSP = minorOpspec.setProperties ? minorOpspec.setProperties[propertiesName] : null, minorRP = minorOpspec.removedProperties ? minorOpspec.removedProperties[propertiesName] : null, majorSP = majorOpspec.setProperties ? majorOpspec.setProperties[propertiesName] : null, majorRP = majorOpspec.removedProperties ? majorOpspec.removedProperties[propertiesName] : null, result; - result = dropOverruledAndUnneededAttributes(minorSP, minorRP, majorSP, majorRP); - if(minorSP && !hasProperties(minorSP)) { - delete minorOpspec.setProperties[propertiesName] - } - if(minorRP && !hasRemovedProperties(minorRP)) { - delete minorOpspec.removedProperties[propertiesName] - } - if(majorSP && !hasProperties(majorSP)) { - delete majorOpspec.setProperties[propertiesName] - } - if(majorRP && !hasRemovedProperties(majorRP)) { - delete majorOpspec.removedProperties[propertiesName] - } - return result - } - function transformAddStyleRemoveStyle(addStyleSpec, removeStyleSpec) { - var setAttributes, helperOpspec, addStyleSpecResult = [addStyleSpec], removeStyleSpecResult = [removeStyleSpec]; - if(addStyleSpec.styleFamily === removeStyleSpec.styleFamily) { - setAttributes = getStyleReferencingAttributes(addStyleSpec.setProperties, removeStyleSpec.styleName); - if(setAttributes.length > 0) { - helperOpspec = {optype:"UpdateParagraphStyle", memberid:removeStyleSpec.memberid, timestamp:removeStyleSpec.timestamp, styleName:addStyleSpec.styleName, removedProperties:{attributes:setAttributes.join(",")}}; - removeStyleSpecResult.unshift(helperOpspec) - } - dropStyleReferencingAttributes(addStyleSpec.setProperties, removeStyleSpec.styleName) + insertNodeAtPosition(odtDocument, annotation.node, position); + odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:length}); + if(cursor) { + selectedRange = doc.createRange(); + paragraphElement = domUtils.getElementsByTagNameNS(annotation.node, odf.Namespaces.textns, "p")[0]; + selectedRange.selectNodeContents(paragraphElement); + cursor.setSelectedRange(selectedRange); + odtDocument.emit(ops.OdtDocument.signalCursorMoved, cursor) } - return{opSpecsA:addStyleSpecResult, opSpecsB:removeStyleSpecResult} + odtDocument.getOdfCanvas().addAnnotation(annotation); + odtDocument.fixCursorPositions(); + return true + }; + this.spec = function() { + return{optype:"AddAnnotation", memberid:memberid, timestamp:timestamp, position:position, length:length, name:name} } - function transformApplyDirectStylingApplyDirectStyling(applyDirectStylingSpecA, applyDirectStylingSpecB, hasAPriority) { - var majorSpec, minorSpec, majorSpecResult, minorSpecResult, majorSpecEnd, minorSpecEnd, dropResult, originalMajorSpec, originalMinorSpec, helperOpspecBefore, helperOpspecAfter, applyDirectStylingSpecAResult = [applyDirectStylingSpecA], applyDirectStylingSpecBResult = [applyDirectStylingSpecB]; - if(!(applyDirectStylingSpecA.position + applyDirectStylingSpecA.length <= applyDirectStylingSpecB.position || applyDirectStylingSpecA.position >= applyDirectStylingSpecB.position + applyDirectStylingSpecB.length)) { - majorSpec = hasAPriority ? applyDirectStylingSpecA : applyDirectStylingSpecB; - minorSpec = hasAPriority ? applyDirectStylingSpecB : applyDirectStylingSpecA; - if(applyDirectStylingSpecA.position !== applyDirectStylingSpecB.position || applyDirectStylingSpecA.length !== applyDirectStylingSpecB.length) { - originalMajorSpec = cloneOpspec(majorSpec); - originalMinorSpec = cloneOpspec(minorSpec) - } - dropResult = dropOverruledAndUnneededProperties(minorSpec, majorSpec, "style:text-properties"); - if(dropResult.majorChanged || dropResult.minorChanged) { - majorSpecResult = []; - minorSpecResult = []; - majorSpecEnd = majorSpec.position + majorSpec.length; - minorSpecEnd = minorSpec.position + minorSpec.length; - if(minorSpec.position < majorSpec.position) { - if(dropResult.minorChanged) { - helperOpspecBefore = cloneOpspec((originalMinorSpec)); - helperOpspecBefore.length = majorSpec.position - minorSpec.position; - minorSpecResult.push(helperOpspecBefore); - minorSpec.position = majorSpec.position; - minorSpec.length = minorSpecEnd - minorSpec.position - } - }else { - if(majorSpec.position < minorSpec.position) { - if(dropResult.majorChanged) { - helperOpspecBefore = cloneOpspec((originalMajorSpec)); - helperOpspecBefore.length = minorSpec.position - majorSpec.position; - majorSpecResult.push(helperOpspecBefore); - majorSpec.position = minorSpec.position; - majorSpec.length = majorSpecEnd - majorSpec.position - } - } - } - if(minorSpecEnd > majorSpecEnd) { - if(dropResult.minorChanged) { - helperOpspecAfter = originalMinorSpec; - helperOpspecAfter.position = majorSpecEnd; - helperOpspecAfter.length = minorSpecEnd - majorSpecEnd; - minorSpecResult.push(helperOpspecAfter); - minorSpec.length = majorSpecEnd - minorSpec.position - } - }else { - if(majorSpecEnd > minorSpecEnd) { - if(dropResult.majorChanged) { - helperOpspecAfter = originalMajorSpec; - helperOpspecAfter.position = minorSpecEnd; - helperOpspecAfter.length = majorSpecEnd - minorSpecEnd; - majorSpecResult.push(helperOpspecAfter); - majorSpec.length = minorSpecEnd - majorSpec.position - } - } - } - if(majorSpec.setProperties && hasProperties(majorSpec.setProperties)) { - majorSpecResult.push(majorSpec) - } - if(minorSpec.setProperties && hasProperties(minorSpec.setProperties)) { - minorSpecResult.push(minorSpec) - } - if(hasAPriority) { - applyDirectStylingSpecAResult = majorSpecResult; - applyDirectStylingSpecBResult = minorSpecResult - }else { - applyDirectStylingSpecAResult = minorSpecResult; - applyDirectStylingSpecBResult = majorSpecResult - } - } +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +ops.OpAddCursor = function OpAddCursor() { + var memberid, timestamp; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp + }; + this.isEdit = false; + this.execute = function(odtDocument) { + var cursor = odtDocument.getCursor(memberid); + if(cursor) { + return false } - return{opSpecsA:applyDirectStylingSpecAResult, opSpecsB:applyDirectStylingSpecBResult} + cursor = new ops.OdtCursor(memberid, odtDocument); + odtDocument.addCursor(cursor); + odtDocument.emit(ops.OdtDocument.signalCursorAdded, cursor); + return true + }; + this.spec = function() { + return{optype:"AddCursor", memberid:memberid, timestamp:timestamp} } - function transformApplyDirectStylingInsertText(applyDirectStylingSpec, insertTextSpec) { - if(insertTextSpec.position <= applyDirectStylingSpec.position) { - applyDirectStylingSpec.position += insertTextSpec.text.length - }else { - if(insertTextSpec.position <= applyDirectStylingSpec.position + applyDirectStylingSpec.length) { - applyDirectStylingSpec.length += insertTextSpec.text.length - } +}; +/* + + Copyright (C) 2013 KO GmbH + + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("ops.Member"); +ops.OpAddMember = function OpAddMember() { + var memberid, timestamp, setProperties; + this.init = function(data) { + memberid = data.memberid; + timestamp = parseInt(data.timestamp, 10); + setProperties = data.setProperties + }; + this.isEdit = false; + this.execute = function(odtDocument) { + if(odtDocument.getMember(memberid)) { + return false } - return{opSpecsA:[applyDirectStylingSpec], opSpecsB:[insertTextSpec]} + var member = new ops.Member(memberid, setProperties); + odtDocument.addMember(member); + odtDocument.emit(ops.OdtDocument.signalMemberAdded, member); + return true + }; + this.spec = function() { + return{optype:"AddMember", memberid:memberid, timestamp:timestamp, setProperties:setProperties} } - function transformApplyDirectStylingRemoveText(applyDirectStylingSpec, removeTextSpec) { - var applyDirectStylingSpecEnd = applyDirectStylingSpec.position + applyDirectStylingSpec.length, removeTextSpecEnd = removeTextSpec.position + removeTextSpec.length, applyDirectStylingSpecResult = [applyDirectStylingSpec], removeTextSpecResult = [removeTextSpec]; - if(removeTextSpecEnd <= applyDirectStylingSpec.position) { - applyDirectStylingSpec.position -= removeTextSpec.length - }else { - if(removeTextSpec.position < applyDirectStylingSpecEnd) { - if(applyDirectStylingSpec.position < removeTextSpec.position) { - if(removeTextSpecEnd < applyDirectStylingSpecEnd) { - applyDirectStylingSpec.length -= removeTextSpec.length - }else { - applyDirectStylingSpec.length = removeTextSpec.position - applyDirectStylingSpec.position - } - }else { - applyDirectStylingSpec.position = removeTextSpec.position; - if(removeTextSpecEnd < applyDirectStylingSpecEnd) { - applyDirectStylingSpec.length = applyDirectStylingSpecEnd - removeTextSpecEnd - }else { - applyDirectStylingSpecResult = [] - } - } - } +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("odf.Namespaces"); +ops.OpAddStyle = function OpAddStyle() { + var memberid, timestamp, styleName, styleFamily, isAutomaticStyle, setProperties, stylens = odf.Namespaces.stylens; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + styleName = data.styleName; + styleFamily = data.styleFamily; + isAutomaticStyle = data.isAutomaticStyle === "true" || data.isAutomaticStyle === true; + setProperties = data.setProperties + }; + this.isEdit = true; + this.execute = function(odtDocument) { + var odfContainer = odtDocument.getOdfCanvas().odfContainer(), formatting = odtDocument.getFormatting(), dom = odtDocument.getDOM(), styleNode = dom.createElementNS(stylens, "style:style"); + if(!styleNode) { + return false } - return{opSpecsA:applyDirectStylingSpecResult, opSpecsB:removeTextSpecResult} - } - function transformApplyDirectStylingSplitParagraph(applyDirectStylingSpec, splitParagraphSpec) { - if(splitParagraphSpec.position < applyDirectStylingSpec.position) { - applyDirectStylingSpec.position += 1 + if(setProperties) { + formatting.updateStyle(styleNode, setProperties) + } + styleNode.setAttributeNS(stylens, "style:family", styleFamily); + styleNode.setAttributeNS(stylens, "style:name", styleName); + if(isAutomaticStyle) { + odfContainer.rootElement.automaticStyles.appendChild(styleNode) }else { - if(splitParagraphSpec.position < applyDirectStylingSpec.position + applyDirectStylingSpec.length) { - applyDirectStylingSpec.length += 1 - } + odfContainer.rootElement.styles.appendChild(styleNode) } - return{opSpecsA:[applyDirectStylingSpec], opSpecsB:[splitParagraphSpec]} + odtDocument.getOdfCanvas().refreshCSS(); + if(!isAutomaticStyle) { + odtDocument.emit(ops.OdtDocument.signalCommonStyleCreated, {name:styleName, family:styleFamily}) + } + return true + }; + this.spec = function() { + return{optype:"AddStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, styleFamily:styleFamily, isAutomaticStyle:isAutomaticStyle, setProperties:setProperties} + } +}; +ops.OpAddStyle.Spec; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("gui.StyleHelper"); +runtime.loadClass("odf.OdfUtils"); +ops.OpApplyDirectStyling = function OpApplyDirectStyling() { + var memberid, timestamp, position, length, setProperties, odfUtils = new odf.OdfUtils; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = parseInt(data.position, 10); + length = parseInt(data.length, 10); + setProperties = data.setProperties + }; + this.isEdit = true; + function getRange(odtDocument) { + var point1 = length >= 0 ? position : position + length, point2 = length >= 0 ? position + length : position, p1 = odtDocument.getIteratorAtPosition(point1), p2 = length ? odtDocument.getIteratorAtPosition(point2) : p1, range = odtDocument.getDOM().createRange(); + range.setStart(p1.container(), p1.unfilteredDomOffset()); + range.setEnd(p2.container(), p2.unfilteredDomOffset()); + return range + } + this.execute = function(odtDocument) { + var range = getRange(odtDocument), impactedParagraphs = odfUtils.getImpactedParagraphs(range), styleHelper = new gui.StyleHelper(odtDocument.getFormatting()); + styleHelper.applyStyle(memberid, range, setProperties); + range.detach(); + odtDocument.getOdfCanvas().refreshCSS(); + odtDocument.fixCursorPositions(); + impactedParagraphs.forEach(function(n) { + odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:n, memberId:memberid, timeStamp:timestamp}) + }); + odtDocument.getOdfCanvas().rerenderAnnotations(); + return true + }; + this.spec = function() { + return{optype:"ApplyDirectStyling", memberid:memberid, timestamp:timestamp, position:position, length:length, setProperties:setProperties} } - function transformInsertTextInsertText(insertTextSpecA, insertTextSpecB, hasAPriority) { - if(insertTextSpecA.position < insertTextSpecB.position) { - insertTextSpecB.position += insertTextSpecA.text.length - }else { - if(insertTextSpecA.position > insertTextSpecB.position) { - insertTextSpecA.position += insertTextSpecB.text.length - }else { - if(hasAPriority) { - insertTextSpecB.position += insertTextSpecA.text.length - }else { - insertTextSpecA.position += insertTextSpecB.text.length - } - return null - } - } - return{opSpecsA:[insertTextSpecA], opSpecsB:[insertTextSpecB]} +}; +ops.OpApplyDirectStyling.Spec; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +ops.OpInsertImage = function OpInsertImage() { + var memberid, timestamp, position, filename, frameWidth, frameHeight, frameStyleName, frameName, drawns = odf.Namespaces.drawns, svgns = odf.Namespaces.svgns, textns = odf.Namespaces.textns, xlinkns = odf.Namespaces.xlinkns; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = data.position; + filename = data.filename; + frameWidth = data.frameWidth; + frameHeight = data.frameHeight; + frameStyleName = data.frameStyleName; + frameName = data.frameName + }; + this.isEdit = true; + function createFrameElement(document) { + var imageNode = document.createElementNS(drawns, "draw:image"), frameNode = document.createElementNS(drawns, "draw:frame"); + imageNode.setAttributeNS(xlinkns, "xlink:href", filename); + imageNode.setAttributeNS(xlinkns, "xlink:type", "simple"); + imageNode.setAttributeNS(xlinkns, "xlink:show", "embed"); + imageNode.setAttributeNS(xlinkns, "xlink:actuate", "onLoad"); + frameNode.setAttributeNS(drawns, "draw:style-name", frameStyleName); + frameNode.setAttributeNS(drawns, "draw:name", frameName); + frameNode.setAttributeNS(textns, "text:anchor-type", "as-char"); + frameNode.setAttributeNS(svgns, "svg:width", frameWidth); + frameNode.setAttributeNS(svgns, "svg:height", frameHeight); + frameNode.appendChild(imageNode); + return frameNode } - function transformInsertTextMoveCursor(insertTextSpec, moveCursorSpec) { - var isMoveCursorSpecRangeInverted = invertMoveCursorSpecRangeOnNegativeLength(moveCursorSpec); - if(insertTextSpec.position < moveCursorSpec.position) { - moveCursorSpec.position += insertTextSpec.text.length - }else { - if(insertTextSpec.position < moveCursorSpec.position + moveCursorSpec.length) { - moveCursorSpec.length += insertTextSpec.text.length - } - } - if(isMoveCursorSpecRangeInverted) { - invertMoveCursorSpecRange(moveCursorSpec) + this.execute = function(odtDocument) { + var odfCanvas = odtDocument.getOdfCanvas(), domPosition = odtDocument.getTextNodeAtStep(position, memberid), textNode, refNode, paragraphElement, frameElement; + if(!domPosition) { + return false } - return{opSpecsA:[insertTextSpec], opSpecsB:[moveCursorSpec]} - } - function transformInsertTextRemoveText(insertTextSpec, removeTextSpec) { - var helperOpspec, removeTextSpecEnd = removeTextSpec.position + removeTextSpec.length, insertTextSpecResult = [insertTextSpec], removeTextSpecResult = [removeTextSpec]; - if(removeTextSpecEnd <= insertTextSpec.position) { - insertTextSpec.position -= removeTextSpec.length - }else { - if(insertTextSpec.position <= removeTextSpec.position) { - removeTextSpec.position += insertTextSpec.text.length - }else { - removeTextSpec.length = insertTextSpec.position - removeTextSpec.position; - helperOpspec = {optype:"RemoveText", memberid:removeTextSpec.memberid, timestamp:removeTextSpec.timestamp, position:insertTextSpec.position + insertTextSpec.text.length, length:removeTextSpecEnd - insertTextSpec.position}; - removeTextSpecResult.unshift(helperOpspec); - insertTextSpec.position = removeTextSpec.position - } + textNode = domPosition.textNode; + paragraphElement = odtDocument.getParagraphElement(textNode); + refNode = domPosition.offset !== textNode.length ? textNode.splitText(domPosition.offset) : textNode.nextSibling; + frameElement = createFrameElement(odtDocument.getDOM()); + textNode.parentNode.insertBefore(frameElement, refNode); + odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:1}); + if(textNode.length === 0) { + textNode.parentNode.removeChild(textNode) } - return{opSpecsA:insertTextSpecResult, opSpecsB:removeTextSpecResult} + odfCanvas.addCssForFrameWithImage(frameElement); + odfCanvas.refreshCSS(); + odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:paragraphElement, memberId:memberid, timeStamp:timestamp}); + odfCanvas.rerenderAnnotations(); + return true + }; + this.spec = function() { + return{optype:"InsertImage", memberid:memberid, timestamp:timestamp, filename:filename, position:position, frameWidth:frameWidth, frameHeight:frameHeight, frameStyleName:frameStyleName, frameName:frameName} } - function transformInsertTextSplitParagraph(insertTextSpec, splitParagraphSpec, hasAPriority) { - if(insertTextSpec.position < splitParagraphSpec.position) { - splitParagraphSpec.position += insertTextSpec.text.length +}; +/* + + Copyright (C) 2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +ops.OpInsertTable = function OpInsertTable() { + var memberid, timestamp, initialRows, initialColumns, position, tableName, tableStyleName, tableColumnStyleName, tableCellStyleMatrix, tablens = "urn:oasis:names:tc:opendocument:xmlns:table:1.0", textns = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = data.position; + initialRows = data.initialRows; + initialColumns = data.initialColumns; + tableName = data.tableName; + tableStyleName = data.tableStyleName; + tableColumnStyleName = data.tableColumnStyleName; + tableCellStyleMatrix = data.tableCellStyleMatrix + }; + this.isEdit = true; + function getCellStyleName(row, column) { + var rowStyles; + if(tableCellStyleMatrix.length === 1) { + rowStyles = tableCellStyleMatrix[0] }else { - if(insertTextSpec.position > splitParagraphSpec.position) { - insertTextSpec.position += 1 - }else { - if(hasAPriority) { - splitParagraphSpec.position += insertTextSpec.text.length - }else { - insertTextSpec.position += 1 - } - return null - } - } - return{opSpecsA:[insertTextSpec], opSpecsB:[splitParagraphSpec]} - } - function transformUpdateParagraphStyleUpdateParagraphStyle(updateParagraphStyleSpecA, updateParagraphStyleSpecB, hasAPriority) { - var majorSpec, minorSpec, updateParagraphStyleSpecAResult = [updateParagraphStyleSpecA], updateParagraphStyleSpecBResult = [updateParagraphStyleSpecB]; - if(updateParagraphStyleSpecA.styleName === updateParagraphStyleSpecB.styleName) { - majorSpec = hasAPriority ? updateParagraphStyleSpecA : updateParagraphStyleSpecB; - minorSpec = hasAPriority ? updateParagraphStyleSpecB : updateParagraphStyleSpecA; - dropOverruledAndUnneededProperties(minorSpec, majorSpec, "style:paragraph-properties"); - dropOverruledAndUnneededProperties(minorSpec, majorSpec, "style:text-properties"); - dropOverruledAndUnneededAttributes(minorSpec.setProperties || null, minorSpec.removedProperties || null, majorSpec.setProperties || null, majorSpec.removedProperties || null); - if(!(majorSpec.setProperties && hasProperties(majorSpec.setProperties)) && !(majorSpec.removedProperties && hasRemovedProperties(majorSpec.removedProperties))) { - if(hasAPriority) { - updateParagraphStyleSpecAResult = [] - }else { - updateParagraphStyleSpecBResult = [] - } - } - if(!(minorSpec.setProperties && hasProperties(minorSpec.setProperties)) && !(minorSpec.removedProperties && hasRemovedProperties(minorSpec.removedProperties))) { - if(hasAPriority) { - updateParagraphStyleSpecBResult = [] - }else { - updateParagraphStyleSpecAResult = [] + if(tableCellStyleMatrix.length === 3) { + switch(row) { + case 0: + rowStyles = tableCellStyleMatrix[0]; + break; + case initialRows - 1: + rowStyles = tableCellStyleMatrix[2]; + break; + default: + rowStyles = tableCellStyleMatrix[1]; + break } - } - } - return{opSpecsA:updateParagraphStyleSpecAResult, opSpecsB:updateParagraphStyleSpecBResult} - } - function transformUpdateMetadataUpdateMetadata(updateMetadataSpecA, updateMetadataSpecB, hasAPriority) { - var majorSpec, minorSpec, updateMetadataSpecAResult = [updateMetadataSpecA], updateMetadataSpecBResult = [updateMetadataSpecB]; - majorSpec = hasAPriority ? updateMetadataSpecA : updateMetadataSpecB; - minorSpec = hasAPriority ? updateMetadataSpecB : updateMetadataSpecA; - dropOverruledAndUnneededAttributes(minorSpec.setProperties || null, minorSpec.removedProperties || null, majorSpec.setProperties || null, majorSpec.removedProperties || null); - if(!(majorSpec.setProperties && hasProperties(majorSpec.setProperties)) && !(majorSpec.removedProperties && hasRemovedProperties(majorSpec.removedProperties))) { - if(hasAPriority) { - updateMetadataSpecAResult = [] - }else { - updateMetadataSpecBResult = [] - } - } - if(!(minorSpec.setProperties && hasProperties(minorSpec.setProperties)) && !(minorSpec.removedProperties && hasRemovedProperties(minorSpec.removedProperties))) { - if(hasAPriority) { - updateMetadataSpecBResult = [] }else { - updateMetadataSpecAResult = [] + rowStyles = tableCellStyleMatrix[row] } } - return{opSpecsA:updateMetadataSpecAResult, opSpecsB:updateMetadataSpecBResult} + if(rowStyles.length === 1) { + return rowStyles[0] + } + if(rowStyles.length === 3) { + switch(column) { + case 0: + return rowStyles[0]; + case initialColumns - 1: + return rowStyles[2]; + default: + return rowStyles[1] + } + } + return rowStyles[column] } - function transformSplitParagraphSplitParagraph(splitParagraphSpecA, splitParagraphSpecB, hasAPriority) { - if(splitParagraphSpecA.position < splitParagraphSpecB.position) { - splitParagraphSpecB.position += 1 - }else { - if(splitParagraphSpecA.position > splitParagraphSpecB.position) { - splitParagraphSpecA.position += 1 - }else { - if(splitParagraphSpecA.position === splitParagraphSpecB.position) { - if(hasAPriority) { - splitParagraphSpecB.position += 1 - }else { - splitParagraphSpecA.position += 1 - } - return null + function createTableNode(document) { + var tableNode = document.createElementNS(tablens, "table:table"), columns = document.createElementNS(tablens, "table:table-column"), row, cell, paragraph, rowCounter, columnCounter, cellStyleName; + if(tableStyleName) { + tableNode.setAttributeNS(tablens, "table:style-name", tableStyleName) + } + if(tableName) { + tableNode.setAttributeNS(tablens, "table:name", tableName) + } + columns.setAttributeNS(tablens, "table:number-columns-repeated", initialColumns); + if(tableColumnStyleName) { + columns.setAttributeNS(tablens, "table:style-name", tableColumnStyleName) + } + tableNode.appendChild(columns); + for(rowCounter = 0;rowCounter < initialRows;rowCounter += 1) { + row = document.createElementNS(tablens, "table:table-row"); + for(columnCounter = 0;columnCounter < initialColumns;columnCounter += 1) { + cell = document.createElementNS(tablens, "table:table-cell"); + cellStyleName = getCellStyleName(rowCounter, columnCounter); + if(cellStyleName) { + cell.setAttributeNS(tablens, "table:style-name", cellStyleName) } + paragraph = document.createElementNS(textns, "text:p"); + cell.appendChild(paragraph); + row.appendChild(cell) } + tableNode.appendChild(row) } - return{opSpecsA:[splitParagraphSpecA], opSpecsB:[splitParagraphSpecB]} + return tableNode } - function transformMoveCursorRemoveCursor(moveCursorSpec, removeCursorSpec) { - var isSameCursorRemoved = moveCursorSpec.memberid === removeCursorSpec.memberid; - return{opSpecsA:isSameCursorRemoved ? [] : [moveCursorSpec], opSpecsB:[removeCursorSpec]} + this.execute = function(odtDocument) { + var domPosition = odtDocument.getTextNodeAtStep(position), rootNode = odtDocument.getRootNode(), previousSibling, tableNode; + if(domPosition) { + tableNode = createTableNode(odtDocument.getDOM()); + previousSibling = odtDocument.getParagraphElement(domPosition.textNode); + rootNode.insertBefore(tableNode, previousSibling.nextSibling); + odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:initialColumns * initialRows + 1}); + odtDocument.getOdfCanvas().refreshSize(); + odtDocument.emit(ops.OdtDocument.signalTableAdded, {tableElement:tableNode, memberId:memberid, timeStamp:timestamp}); + odtDocument.getOdfCanvas().rerenderAnnotations(); + return true + } + return false + }; + this.spec = function() { + return{optype:"InsertTable", memberid:memberid, timestamp:timestamp, position:position, initialRows:initialRows, initialColumns:initialColumns, tableName:tableName, tableStyleName:tableStyleName, tableColumnStyleName:tableColumnStyleName, tableCellStyleMatrix:tableCellStyleMatrix} } - function transformMoveCursorRemoveText(moveCursorSpec, removeTextSpec) { - var isMoveCursorSpecRangeInverted = invertMoveCursorSpecRangeOnNegativeLength(moveCursorSpec), moveCursorSpecEnd = moveCursorSpec.position + moveCursorSpec.length, removeTextSpecEnd = removeTextSpec.position + removeTextSpec.length; - if(removeTextSpecEnd <= moveCursorSpec.position) { - moveCursorSpec.position -= removeTextSpec.length - }else { - if(removeTextSpec.position < moveCursorSpecEnd) { - if(moveCursorSpec.position < removeTextSpec.position) { - if(removeTextSpecEnd < moveCursorSpecEnd) { - moveCursorSpec.length -= removeTextSpec.length - }else { - moveCursorSpec.length = removeTextSpec.position - moveCursorSpec.position - } - }else { - moveCursorSpec.position = removeTextSpec.position; - if(removeTextSpecEnd < moveCursorSpecEnd) { - moveCursorSpec.length = moveCursorSpecEnd - removeTextSpecEnd +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +ops.OpInsertText = function OpInsertText() { + var space = " ", tab = "\t", memberid, timestamp, position, text; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = data.position; + text = data.text + }; + this.isEdit = true; + function triggerLayoutInWebkit(textNode) { + var parent = textNode.parentNode, next = textNode.nextSibling; + parent.removeChild(textNode); + parent.insertBefore(textNode, next) + } + function requiresSpaceElement(text, index) { + return text[index] === space && (index === 0 || (index === text.length - 1 || text[index - 1] === space)) + } + this.execute = function(odtDocument) { + var domPosition, previousNode, parentElement, nextNode = null, ownerDocument = odtDocument.getDOM(), paragraphElement, textns = "urn:oasis:names:tc:opendocument:xmlns:text:1.0", toInsertIndex = 0, spaceTag, spaceElement, i; + function insertTextNode(toInsertText) { + parentElement.insertBefore(ownerDocument.createTextNode(toInsertText), nextNode) + } + odtDocument.upgradeWhitespacesAtPosition(position); + domPosition = odtDocument.getTextNodeAtStep(position, memberid); + if(domPosition) { + previousNode = domPosition.textNode; + nextNode = previousNode.nextSibling; + parentElement = previousNode.parentNode; + paragraphElement = odtDocument.getParagraphElement(previousNode); + for(i = 0;i < text.length;i += 1) { + if(requiresSpaceElement(text, i) || text[i] === tab) { + if(toInsertIndex === 0) { + if(domPosition.offset !== previousNode.length) { + nextNode = previousNode.splitText(domPosition.offset) + } + if(0 < i) { + previousNode.appendData(text.substring(0, i)) + } }else { - moveCursorSpec.length = 0 + if(toInsertIndex < i) { + insertTextNode(text.substring(toInsertIndex, i)) + } } + toInsertIndex = i + 1; + spaceTag = text[i] === space ? "text:s" : "text:tab"; + spaceElement = ownerDocument.createElementNS(textns, spaceTag); + spaceElement.appendChild(ownerDocument.createTextNode(text[i])); + parentElement.insertBefore(spaceElement, nextNode) } } - } - if(isMoveCursorSpecRangeInverted) { - invertMoveCursorSpecRange(moveCursorSpec) - } - return{opSpecsA:[moveCursorSpec], opSpecsB:[removeTextSpec]} - } - function transformMoveCursorSplitParagraph(moveCursorSpec, splitParagraphSpec) { - var isMoveCursorSpecRangeInverted = invertMoveCursorSpecRangeOnNegativeLength(moveCursorSpec); - if(splitParagraphSpec.position < moveCursorSpec.position) { - moveCursorSpec.position += 1 - }else { - if(splitParagraphSpec.position < moveCursorSpec.position + moveCursorSpec.length) { - moveCursorSpec.length += 1 + if(toInsertIndex === 0) { + previousNode.insertData(domPosition.offset, text) + }else { + if(toInsertIndex < text.length) { + insertTextNode(text.substring(toInsertIndex)) + } + } + triggerLayoutInWebkit(previousNode); + if(previousNode.length === 0) { + previousNode.parentNode.removeChild(previousNode) } + odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:text.length}); + if(position > 0) { + if(position > 1) { + odtDocument.downgradeWhitespacesAtPosition(position - 2) + } + odtDocument.downgradeWhitespacesAtPosition(position - 1) + } + odtDocument.downgradeWhitespacesAtPosition(position); + odtDocument.downgradeWhitespacesAtPosition(position + text.length - 1); + odtDocument.downgradeWhitespacesAtPosition(position + text.length); + odtDocument.getOdfCanvas().refreshSize(); + odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:paragraphElement, memberId:memberid, timeStamp:timestamp}); + odtDocument.getOdfCanvas().rerenderAnnotations(); + return true } - if(isMoveCursorSpecRangeInverted) { - invertMoveCursorSpecRange(moveCursorSpec) + return false + }; + this.spec = function() { + return{optype:"InsertText", memberid:memberid, timestamp:timestamp, position:position, text:text} + } +}; +ops.OpInsertText.Spec; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +ops.OpMoveCursor = function OpMoveCursor() { + var memberid, timestamp, position, length, selectionType; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = data.position; + length = data.length || 0; + selectionType = data.selectionType || ops.OdtCursor.RangeSelection + }; + this.isEdit = false; + this.execute = function(odtDocument) { + var cursor = odtDocument.getCursor(memberid), selectedRange; + if(!cursor) { + return false } - return{opSpecsA:[moveCursorSpec], opSpecsB:[splitParagraphSpec]} - } - function transformRemoveCursorRemoveCursor(removeCursorSpecA, removeCursorSpecB) { - var isSameMemberid = removeCursorSpecA.memberid === removeCursorSpecB.memberid; - return{opSpecsA:isSameMemberid ? [] : [removeCursorSpecA], opSpecsB:isSameMemberid ? [] : [removeCursorSpecB]} - } - function transformRemoveStyleRemoveStyle(removeStyleSpecA, removeStyleSpecB) { - var isSameStyle = removeStyleSpecA.styleName === removeStyleSpecB.styleName && removeStyleSpecA.styleFamily === removeStyleSpecB.styleFamily; - return{opSpecsA:isSameStyle ? [] : [removeStyleSpecA], opSpecsB:isSameStyle ? [] : [removeStyleSpecB]} + selectedRange = odtDocument.convertCursorToDomRange(position, length); + cursor.setSelectedRange(selectedRange, length >= 0); + cursor.setSelectionType(selectionType); + odtDocument.emit(ops.OdtDocument.signalCursorMoved, cursor); + return true + }; + this.spec = function() { + return{optype:"MoveCursor", memberid:memberid, timestamp:timestamp, position:position, length:length, selectionType:selectionType} } - function transformRemoveStyleSetParagraphStyle(removeStyleSpec, setParagraphStyleSpec) { - var helperOpspec, removeStyleSpecResult = [removeStyleSpec], setParagraphStyleSpecResult = [setParagraphStyleSpec]; - if(removeStyleSpec.styleFamily === "paragraph" && removeStyleSpec.styleName === setParagraphStyleSpec.styleName) { - helperOpspec = {optype:"SetParagraphStyle", memberid:removeStyleSpec.memberid, timestamp:removeStyleSpec.timestamp, position:setParagraphStyleSpec.position, styleName:""}; - removeStyleSpecResult.unshift(helperOpspec); - setParagraphStyleSpec.styleName = "" +}; +ops.OpMoveCursor.Spec; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("odf.Namespaces"); +runtime.loadClass("core.DomUtils"); +ops.OpRemoveAnnotation = function OpRemoveAnnotation() { + var memberid, timestamp, position, length, domUtils; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = parseInt(data.position, 10); + length = parseInt(data.length, 10); + domUtils = new core.DomUtils + }; + this.isEdit = true; + this.execute = function(odtDocument) { + var iterator = odtDocument.getIteratorAtPosition(position), container = iterator.container(), annotationName, annotationNode, annotationEnd, cursors; + while(!(container.namespaceURI === odf.Namespaces.officens && container.localName === "annotation")) { + container = container.parentNode } - return{opSpecsA:removeStyleSpecResult, opSpecsB:setParagraphStyleSpecResult} - } - function transformRemoveStyleUpdateParagraphStyle(removeStyleSpec, updateParagraphStyleSpec) { - var setAttributes, helperOpspec, removeStyleSpecResult = [removeStyleSpec], updateParagraphStyleSpecResult = [updateParagraphStyleSpec]; - if(removeStyleSpec.styleFamily === "paragraph") { - setAttributes = getStyleReferencingAttributes(updateParagraphStyleSpec.setProperties, removeStyleSpec.styleName); - if(setAttributes.length > 0) { - helperOpspec = {optype:"UpdateParagraphStyle", memberid:removeStyleSpec.memberid, timestamp:removeStyleSpec.timestamp, styleName:updateParagraphStyleSpec.styleName, removedProperties:{attributes:setAttributes.join(",")}}; - removeStyleSpecResult.unshift(helperOpspec) - } - if(removeStyleSpec.styleName === updateParagraphStyleSpec.styleName) { - updateParagraphStyleSpecResult = [] - }else { - dropStyleReferencingAttributes(updateParagraphStyleSpec.setProperties, removeStyleSpec.styleName) - } + if(container === null) { + return false } - return{opSpecsA:removeStyleSpecResult, opSpecsB:updateParagraphStyleSpecResult} - } - function transformRemoveTextRemoveText(removeTextSpecA, removeTextSpecB) { - var removeTextSpecAEnd = removeTextSpecA.position + removeTextSpecA.length, removeTextSpecBEnd = removeTextSpecB.position + removeTextSpecB.length, removeTextSpecAResult = [removeTextSpecA], removeTextSpecBResult = [removeTextSpecB]; - if(removeTextSpecBEnd <= removeTextSpecA.position) { - removeTextSpecA.position -= removeTextSpecB.length - }else { - if(removeTextSpecAEnd <= removeTextSpecB.position) { - removeTextSpecB.position -= removeTextSpecA.length - }else { - if(removeTextSpecB.position < removeTextSpecAEnd) { - if(removeTextSpecA.position < removeTextSpecB.position) { - if(removeTextSpecBEnd < removeTextSpecAEnd) { - removeTextSpecA.length = removeTextSpecA.length - removeTextSpecB.length - }else { - removeTextSpecA.length = removeTextSpecB.position - removeTextSpecA.position - } - if(removeTextSpecAEnd < removeTextSpecBEnd) { - removeTextSpecB.position = removeTextSpecA.position; - removeTextSpecB.length = removeTextSpecBEnd - removeTextSpecAEnd - }else { - removeTextSpecBResult = [] - } - }else { - if(removeTextSpecAEnd < removeTextSpecBEnd) { - removeTextSpecB.length = removeTextSpecB.length - removeTextSpecA.length - }else { - if(removeTextSpecB.position < removeTextSpecA.position) { - removeTextSpecB.length = removeTextSpecA.position - removeTextSpecB.position - }else { - removeTextSpecBResult = [] - } - } - if(removeTextSpecBEnd < removeTextSpecAEnd) { - removeTextSpecA.position = removeTextSpecB.position; - removeTextSpecA.length = removeTextSpecAEnd - removeTextSpecBEnd - }else { - removeTextSpecAResult = [] - } - } - } - } + annotationNode = container; + annotationName = annotationNode.getAttributeNS(odf.Namespaces.officens, "name"); + if(annotationName) { + annotationEnd = domUtils.getElementsByTagNameNS(odtDocument.getRootNode(), odf.Namespaces.officens, "annotation-end").filter(function(element) { + return annotationName === element.getAttributeNS(odf.Namespaces.officens, "name") + })[0] || null } - return{opSpecsA:removeTextSpecAResult, opSpecsB:removeTextSpecBResult} - } - function transformRemoveTextSplitParagraph(removeTextSpec, splitParagraphSpec) { - var removeTextSpecEnd = removeTextSpec.position + removeTextSpec.length, helperOpspec, removeTextSpecResult = [removeTextSpec], splitParagraphSpecResult = [splitParagraphSpec]; - if(splitParagraphSpec.position <= removeTextSpec.position) { - removeTextSpec.position += 1 - }else { - if(splitParagraphSpec.position < removeTextSpecEnd) { - removeTextSpec.length = splitParagraphSpec.position - removeTextSpec.position; - helperOpspec = {optype:"RemoveText", memberid:removeTextSpec.memberid, timestamp:removeTextSpec.timestamp, position:splitParagraphSpec.position + 1, length:removeTextSpecEnd - splitParagraphSpec.position}; - removeTextSpecResult.unshift(helperOpspec) - } + odtDocument.getOdfCanvas().forgetAnnotations(); + cursors = domUtils.getElementsByTagNameNS(annotationNode, "urn:webodf:names:cursor", "cursor"); + while(cursors.length) { + annotationNode.parentNode.insertBefore(cursors.pop(), annotationNode) } - if(removeTextSpec.position + removeTextSpec.length <= splitParagraphSpec.position) { - splitParagraphSpec.position -= removeTextSpec.length - }else { - if(removeTextSpec.position < splitParagraphSpec.position) { - splitParagraphSpec.position = removeTextSpec.position - } + annotationNode.parentNode.removeChild(annotationNode); + if(annotationEnd) { + annotationEnd.parentNode.removeChild(annotationEnd) } - return{opSpecsA:removeTextSpecResult, opSpecsB:splitParagraphSpecResult} - } - function passUnchanged(opSpecA, opSpecB) { - return{opSpecsA:[opSpecA], opSpecsB:[opSpecB]} + odtDocument.emit(ops.OdtDocument.signalStepsRemoved, {position:position > 0 ? position - 1 : position, length:length}); + odtDocument.fixCursorPositions(); + odtDocument.getOdfCanvas().refreshAnnotations(); + return true + }; + this.spec = function() { + return{optype:"RemoveAnnotation", memberid:memberid, timestamp:timestamp, position:position, length:length} } - var transformations = {"AddCursor":{"AddCursor":passUnchanged, "AddMember":passUnchanged, "AddStyle":passUnchanged, "ApplyDirectStyling":passUnchanged, "InsertText":passUnchanged, "MoveCursor":passUnchanged, "RemoveCursor":passUnchanged, "RemoveMember":passUnchanged, "RemoveStyle":passUnchanged, "RemoveText":passUnchanged, "SetParagraphStyle":passUnchanged, "SplitParagraph":passUnchanged, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "AddMember":{"AddStyle":passUnchanged, - "InsertText":passUnchanged, "MoveCursor":passUnchanged, "RemoveCursor":passUnchanged, "RemoveStyle":passUnchanged, "RemoveText":passUnchanged, "SetParagraphStyle":passUnchanged, "SplitParagraph":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "AddStyle":{"AddStyle":passUnchanged, "ApplyDirectStyling":passUnchanged, "InsertText":passUnchanged, "MoveCursor":passUnchanged, "RemoveCursor":passUnchanged, "RemoveMember":passUnchanged, "RemoveStyle":transformAddStyleRemoveStyle, - "RemoveText":passUnchanged, "SetParagraphStyle":passUnchanged, "SplitParagraph":passUnchanged, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "ApplyDirectStyling":{"ApplyDirectStyling":transformApplyDirectStylingApplyDirectStyling, "InsertText":transformApplyDirectStylingInsertText, "MoveCursor":passUnchanged, "RemoveCursor":passUnchanged, "RemoveStyle":passUnchanged, "RemoveText":transformApplyDirectStylingRemoveText, "SetParagraphStyle":passUnchanged, - "SplitParagraph":transformApplyDirectStylingSplitParagraph, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "InsertText":{"InsertText":transformInsertTextInsertText, "MoveCursor":transformInsertTextMoveCursor, "RemoveCursor":passUnchanged, "RemoveMember":passUnchanged, "RemoveStyle":passUnchanged, "RemoveText":transformInsertTextRemoveText, "SplitParagraph":transformInsertTextSplitParagraph, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, - "MoveCursor":{"MoveCursor":passUnchanged, "RemoveCursor":transformMoveCursorRemoveCursor, "RemoveMember":passUnchanged, "RemoveStyle":passUnchanged, "RemoveText":transformMoveCursorRemoveText, "SetParagraphStyle":passUnchanged, "SplitParagraph":transformMoveCursorSplitParagraph, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "RemoveCursor":{"RemoveCursor":transformRemoveCursorRemoveCursor, "RemoveMember":passUnchanged, "RemoveStyle":passUnchanged, - "RemoveText":passUnchanged, "SetParagraphStyle":passUnchanged, "SplitParagraph":passUnchanged, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "RemoveMember":{"RemoveStyle":passUnchanged, "RemoveText":passUnchanged, "SetParagraphStyle":passUnchanged, "SplitParagraph":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "RemoveStyle":{"RemoveStyle":transformRemoveStyleRemoveStyle, "RemoveText":passUnchanged, "SetParagraphStyle":transformRemoveStyleSetParagraphStyle, - "SplitParagraph":passUnchanged, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":transformRemoveStyleUpdateParagraphStyle}, "RemoveText":{"RemoveText":transformRemoveTextRemoveText, "SplitParagraph":transformRemoveTextSplitParagraph, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "SetParagraphStyle":{"UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "SplitParagraph":{"SplitParagraph":transformSplitParagraphSplitParagraph, - "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "UpdateMember":{"UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "UpdateMetadata":{"UpdateMetadata":transformUpdateMetadataUpdateMetadata, "UpdateParagraphStyle":passUnchanged}, "UpdateParagraphStyle":{"UpdateParagraphStyle":transformUpdateParagraphStyleUpdateParagraphStyle}}; - this.passUnchanged = passUnchanged; - this.extendTransformations = function(moreTransformations) { - Object.keys(moreTransformations).forEach(function(optypeA) { - var moreTransformationsOptypeAMap = moreTransformations[optypeA], optypeAMap, isExtendingOptypeAMap = transformations.hasOwnProperty(optypeA); - runtime.log((isExtendingOptypeAMap ? "Extending" : "Adding") + " map for optypeA: " + optypeA); - if(!isExtendingOptypeAMap) { - transformations[optypeA] = {} - } - optypeAMap = transformations[optypeA]; - Object.keys(moreTransformationsOptypeAMap).forEach(function(optypeB) { - var isOverwritingOptypeBEntry = optypeAMap.hasOwnProperty(optypeB); - runtime.assert(optypeA <= optypeB, "Wrong order:" + optypeA + ", " + optypeB); - runtime.log(" " + (isOverwritingOptypeBEntry ? "Overwriting" : "Adding") + " entry for optypeB: " + optypeB); - optypeAMap[optypeB] = moreTransformationsOptypeAMap[optypeB] - }) - }) +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +ops.OpRemoveBlob = function OpRemoveBlob() { + var memberid, timestamp, filename; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + filename = data.filename }; - this.transformOpspecVsOpspec = function(opSpecA, opSpecB) { - var isOptypeAAlphaNumericSmaller = opSpecA.optype <= opSpecB.optype, helper, transformationFunctionMap, transformationFunction, result; - runtime.log("Crosstransforming:"); - runtime.log(runtime.toJson(opSpecA)); - runtime.log(runtime.toJson(opSpecB)); - if(!isOptypeAAlphaNumericSmaller) { - helper = opSpecA; - opSpecA = opSpecB; - opSpecB = helper - } - transformationFunctionMap = transformations[opSpecA.optype]; - transformationFunction = transformationFunctionMap && transformationFunctionMap[opSpecB.optype]; - if(transformationFunction) { - result = transformationFunction(opSpecA, opSpecB, !isOptypeAAlphaNumericSmaller); - if(!isOptypeAAlphaNumericSmaller && result !== null) { - result = {opSpecsA:result.opSpecsB, opSpecsB:result.opSpecsA} - } - }else { - result = null - } - runtime.log("result:"); - if(result) { - runtime.log(runtime.toJson(result.opSpecsA)); - runtime.log(runtime.toJson(result.opSpecsB)) - }else { - runtime.log("null") + this.isEdit = true; + this.execute = function(odtDocument) { + odtDocument.getOdfCanvas().odfContainer().removeBlob(filename); + return true + }; + this.spec = function() { + return{optype:"RemoveBlob", memberid:memberid, timestamp:timestamp, filename:filename} + } +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +ops.OpRemoveCursor = function OpRemoveCursor() { + var memberid, timestamp; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp + }; + this.isEdit = false; + this.execute = function(odtDocument) { + if(!odtDocument.removeCursor(memberid)) { + return false } - return result + return true + }; + this.spec = function() { + return{optype:"RemoveCursor", memberid:memberid, timestamp:timestamp} } }; +ops.OpRemoveCursor.Spec; /* Copyright (C) 2013 KO GmbH @@ -13786,143 +13825,90 @@ ops.OperationTransformMatrix = function OperationTransformMatrix() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("ops.OperationFactory"); -runtime.loadClass("ops.OperationTransformMatrix"); -ops.OperationTransformer = function OperationTransformer() { - var operationFactory, operationTransformMatrix = new ops.OperationTransformMatrix; - function operations(opspecs) { - var ops = []; - opspecs.forEach(function(opspec) { - ops.push(operationFactory.create(opspec)) - }); - return ops - } - function transformOpVsOp(opSpecA, opSpecB) { - return operationTransformMatrix.transformOpspecVsOpspec(opSpecA, opSpecB) - } - function transformOpListVsOp(opSpecsA, opSpecB) { - var transformResult, transformListResult, transformedOpspecsA = [], transformedOpspecsB = []; - while(opSpecsA.length > 0 && opSpecB) { - transformResult = transformOpVsOp(opSpecsA.shift(), (opSpecB)); - if(!transformResult) { - return null - } - transformedOpspecsA = transformedOpspecsA.concat(transformResult.opSpecsA); - if(transformResult.opSpecsB.length === 0) { - transformedOpspecsA = transformedOpspecsA.concat(opSpecsA); - opSpecB = null; - break - } - while(transformResult.opSpecsB.length > 1) { - transformListResult = transformOpListVsOp(opSpecsA, transformResult.opSpecsB.shift()); - if(!transformListResult) { - return null - } - transformedOpspecsB = transformedOpspecsB.concat(transformListResult.opSpecsB); - opSpecsA = transformListResult.opSpecsA - } - opSpecB = transformResult.opSpecsB.pop() - } - if(opSpecB) { - transformedOpspecsB.push(opSpecB) - } - return{opSpecsA:transformedOpspecsA, opSpecsB:transformedOpspecsB} - } - this.setOperationFactory = function(f) { - operationFactory = f - }; - this.getOperationTransformMatrix = function() { - return operationTransformMatrix +runtime.loadClass("ops.Member"); +ops.OpRemoveMember = function OpRemoveMember() { + var memberid, timestamp; + this.init = function(data) { + memberid = data.memberid; + timestamp = parseInt(data.timestamp, 10) }; - this.transform = function(opSpecsA, opSpecsB) { - var transformResult, transformedOpspecsB = []; - while(opSpecsB.length > 0) { - transformResult = transformOpListVsOp(opSpecsA, opSpecsB.shift()); - if(!transformResult) { - return null - } - opSpecsA = transformResult.opSpecsA; - transformedOpspecsB = transformedOpspecsB.concat(transformResult.opSpecsB) + this.isEdit = false; + this.execute = function(odtDocument) { + if(!odtDocument.getMember(memberid)) { + return false } - return{opsA:operations(opSpecsA), opsB:operations(transformedOpspecsB)} + odtDocument.removeMember(memberid); + odtDocument.emit(ops.OdtDocument.signalMemberRemoved, memberid); + return true + }; + this.spec = function() { + return{optype:"RemoveMember", memberid:memberid, timestamp:timestamp} } }; -runtime.loadClass("core.Cursor"); -runtime.loadClass("gui.SelectionMover"); -ops.OdtCursor = function OdtCursor(memberId, odtDocument) { - var self = this, validSelectionTypes = {}, selectionType, selectionMover, cursor; - this.removeFromOdtDocument = function() { - cursor.remove() - }; - this.move = function(number, extend) { - var moved = 0; - if(number > 0) { - moved = selectionMover.movePointForward(number, extend) - }else { - if(number <= 0) { - moved = -selectionMover.movePointBackward(-number, extend) - } - } - self.handleUpdate(); - return moved - }; - this.handleUpdate = function() { - }; - this.getStepCounter = function() { - return selectionMover.getStepCounter() - }; - this.getMemberId = function() { - return memberId - }; - this.getNode = function() { - return cursor.getNode() - }; - this.getAnchorNode = function() { - return cursor.getAnchorNode() - }; - this.getSelectedRange = function() { - return cursor.getSelectedRange() - }; - this.setSelectedRange = function(range, isForwardSelection) { - cursor.setSelectedRange(range, isForwardSelection); - self.handleUpdate() - }; - this.hasForwardSelection = function() { - return cursor.hasForwardSelection() - }; - this.getOdtDocument = function() { - return odtDocument - }; - this.getSelectionType = function() { - return selectionType +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +ops.OpRemoveStyle = function OpRemoveStyle() { + var memberid, timestamp, styleName, styleFamily; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + styleName = data.styleName; + styleFamily = data.styleFamily }; - this.setSelectionType = function(value) { - if(validSelectionTypes.hasOwnProperty(value)) { - selectionType = value - }else { - runtime.log("Invalid selection type: " + value) + this.isEdit = true; + this.execute = function(odtDocument) { + var styleNode = odtDocument.getStyleElement(styleName, styleFamily); + if(!styleNode) { + return false } + styleNode.parentNode.removeChild(styleNode); + odtDocument.getOdfCanvas().refreshCSS(); + odtDocument.emit(ops.OdtDocument.signalCommonStyleDeleted, {name:styleName, family:styleFamily}); + return true }; - this.resetSelectionType = function() { - self.setSelectionType(ops.OdtCursor.RangeSelection) - }; - function init() { - cursor = new core.Cursor(odtDocument.getDOM(), memberId); - selectionMover = new gui.SelectionMover(cursor, odtDocument.getRootNode()); - validSelectionTypes[ops.OdtCursor.RangeSelection] = true; - validSelectionTypes[ops.OdtCursor.RegionSelection] = true; - self.resetSelectionType() + this.spec = function() { + return{optype:"RemoveStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, styleFamily:styleFamily} } - init() }; -ops.OdtCursor.RangeSelection = "Range"; -ops.OdtCursor.RegionSelection = "Region"; -(function() { - return ops.OdtCursor -})(); +ops.OpRemoveStyle.Spec; /* - Copyright (C) 2012 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -13957,312 +13943,138 @@ ops.OdtCursor.RegionSelection = "Region"; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.EditInfo = function EditInfo(container, odtDocument) { - var editInfoNode, editHistory = {}; - function sortEdits() { - var arr = [], memberid; - for(memberid in editHistory) { - if(editHistory.hasOwnProperty(memberid)) { - arr.push({"memberid":memberid, "time":editHistory[memberid].time}) - } - } - arr.sort(function(a, b) { - return a.time - b.time - }); - return arr - } - this.getNode = function() { - return editInfoNode - }; - this.getOdtDocument = function() { - return odtDocument - }; - this.getEdits = function() { - return editHistory - }; - this.getSortedEdits = function() { - return sortEdits() - }; - this.addEdit = function(memberid, timestamp) { - editHistory[memberid] = {time:timestamp} - }; - this.clearEdits = function() { - editHistory = {} - }; - this.destroy = function(callback) { - if(container.parentNode) { - container.removeChild(editInfoNode) - } - callback() - }; - function init() { - var editInfons = "urn:webodf:names:editinfo", dom = odtDocument.getDOM(); - editInfoNode = dom.createElementNS(editInfons, "editinfo"); - container.insertBefore(editInfoNode, container.firstChild) - } - init() -}; -runtime.loadClass("gui.SelectionMover"); -gui.ShadowCursor = function ShadowCursor(odtDocument) { - var selectedRange = odtDocument.getDOM().createRange(), forwardSelection = true; - this.removeFromOdtDocument = function() { - }; - this.getMemberId = function() { - return gui.ShadowCursor.ShadowCursorMemberId - }; - this.getSelectedRange = function() { - return selectedRange - }; - this.setSelectedRange = function(range, isForwardSelection) { - selectedRange = range; - forwardSelection = isForwardSelection !== false - }; - this.hasForwardSelection = function() { - return forwardSelection - }; - this.getOdtDocument = function() { - return odtDocument - }; - this.getSelectionType = function() { - return ops.OdtCursor.RangeSelection - }; - function init() { - selectedRange.setStart(odtDocument.getRootNode(), 0) - } - init() -}; -gui.ShadowCursor.ShadowCursorMemberId = ""; -(function() { - return gui.ShadowCursor -})(); -gui.Avatar = function Avatar(parentElement, avatarInitiallyVisible) { - var self = this, handle, image, pendingImageUrl, displayShown = "block", displayHidden = "none"; - this.setColor = function(color) { - image.style.borderColor = color - }; - this.setImageUrl = function(url) { - if(self.isVisible()) { - image.src = url - }else { - pendingImageUrl = url - } - }; - this.isVisible = function() { - return handle.style.display === displayShown +runtime.loadClass("odf.Namespaces"); +runtime.loadClass("odf.OdfUtils"); +runtime.loadClass("core.DomUtils"); +ops.OpRemoveText = function OpRemoveText() { + var memberid, timestamp, position, length, odfUtils, domUtils, editinfons = "urn:webodf:names:editinfo", odfNodeNamespaceMap = {}; + this.init = function(data) { + runtime.assert(data.length >= 0, "OpRemoveText only supports positive lengths"); + memberid = data.memberid; + timestamp = data.timestamp; + position = parseInt(data.position, 10); + length = parseInt(data.length, 10); + odfUtils = new odf.OdfUtils; + domUtils = new core.DomUtils; + odfNodeNamespaceMap[odf.Namespaces.dbns] = true; + odfNodeNamespaceMap[odf.Namespaces.dcns] = true; + odfNodeNamespaceMap[odf.Namespaces.dr3dns] = true; + odfNodeNamespaceMap[odf.Namespaces.drawns] = true; + odfNodeNamespaceMap[odf.Namespaces.chartns] = true; + odfNodeNamespaceMap[odf.Namespaces.formns] = true; + odfNodeNamespaceMap[odf.Namespaces.numberns] = true; + odfNodeNamespaceMap[odf.Namespaces.officens] = true; + odfNodeNamespaceMap[odf.Namespaces.presentationns] = true; + odfNodeNamespaceMap[odf.Namespaces.stylens] = true; + odfNodeNamespaceMap[odf.Namespaces.svgns] = true; + odfNodeNamespaceMap[odf.Namespaces.tablens] = true; + odfNodeNamespaceMap[odf.Namespaces.textns] = true }; - this.show = function() { - if(pendingImageUrl) { - image.src = pendingImageUrl; - pendingImageUrl = undefined + this.isEdit = true; + function CollapsingRules(rootNode) { + function isOdfNode(node) { + return odfNodeNamespaceMap.hasOwnProperty(node.namespaceURI) } - handle.style.display = displayShown - }; - this.hide = function() { - handle.style.display = displayHidden - }; - this.markAsFocussed = function(isFocussed) { - handle.className = isFocussed ? "active" : "" - }; - this.destroy = function(callback) { - parentElement.removeChild(handle); - callback() - }; - function init() { - var document = (parentElement.ownerDocument), htmlns = document.documentElement.namespaceURI; - handle = document.createElementNS(htmlns, "div"); - image = document.createElementNS(htmlns, "img"); - image.width = 64; - image.height = 64; - handle.appendChild(image); - handle.style.width = "64px"; - handle.style.height = "70px"; - handle.style.position = "absolute"; - handle.style.top = "-80px"; - handle.style.left = "-34px"; - handle.style.display = avatarInitiallyVisible ? displayShown : displayHidden; - parentElement.appendChild(handle) - } - init() -}; -runtime.loadClass("core.DomUtils"); -runtime.loadClass("gui.Avatar"); -runtime.loadClass("ops.OdtCursor"); -gui.Caret = function Caret(cursor, avatarInitiallyVisible, blinkOnRangeSelect) { - var MIN_CARET_HEIGHT_PX = 8, DEFAULT_CARET_TOP = "5%", DEFAULT_CARET_HEIGHT = "1em", span, avatar, cursorNode, isShown = true, shouldBlink = false, blinking = false, blinkTimeout, domUtils = new core.DomUtils; - function blink(reset) { - if(!shouldBlink || !cursorNode.parentNode) { - return + function shouldRemove(node) { + return isOdfNode(node) || (node.localName === "br" && odfUtils.isLineBreak(node.parentNode) || node.nodeType === Node.TEXT_NODE && isOdfNode((node.parentNode))) } - if(!blinking || reset) { - if(reset && blinkTimeout !== undefined) { - runtime.clearTimeout(blinkTimeout) + function isEmpty(node) { + var childNode; + if(odfUtils.isCharacterElement(node)) { + return false } - blinking = true; - span.style.opacity = reset || span.style.opacity === "0" ? "1" : "0"; - blinkTimeout = runtime.setTimeout(function() { - blinking = false; - blink(false) - }, 500) + if(node.nodeType === Node.TEXT_NODE) { + return node.textContent.length === 0 + } + childNode = node.firstChild; + while(childNode) { + if(isOdfNode(childNode) || !isEmpty(childNode)) { + return false + } + childNode = childNode.nextSibling + } + return true } - } - function getCaretClientRectWithMargin(caretElement, margin) { - var caretRect = caretElement.getBoundingClientRect(); - return{left:caretRect.left - margin.left, top:caretRect.top - margin.top, right:caretRect.right + margin.right, bottom:caretRect.bottom + margin.bottom} - } - function length(node) { - return node.nodeType === Node.TEXT_NODE ? node.textContent.length : node.childNodes.length - } - function verticalOverlap(cursorNode, rangeRect) { - var cursorRect = cursorNode.getBoundingClientRect(), intersectTop = 0, intersectBottom = 0; - if(cursorRect && rangeRect) { - intersectTop = Math.max(cursorRect.top, rangeRect.top); - intersectBottom = Math.min(cursorRect.bottom, rangeRect.bottom) + this.isEmpty = isEmpty; + function isCollapsibleContainer(node) { + return!odfUtils.isParagraph(node) && (node !== rootNode && isEmpty(node)) } - return intersectBottom - intersectTop - } - function getSelectionRect() { - var range = cursor.getSelectedRange().cloneRange(), node = cursor.getNode(), nextRectangle, selectionRectangle = null, nodeLength; - if(node.previousSibling) { - nodeLength = length(node.previousSibling); - range.setStart(node.previousSibling, nodeLength > 0 ? nodeLength - 1 : 0); - range.setEnd(node.previousSibling, nodeLength); - nextRectangle = range.getBoundingClientRect(); - if(nextRectangle && nextRectangle.height) { - selectionRectangle = nextRectangle + function mergeChildrenIntoParent(targetNode) { + var parent; + if(targetNode.nodeType === Node.TEXT_NODE) { + parent = targetNode.parentNode; + parent.removeChild(targetNode) + }else { + parent = domUtils.removeUnwantedNodes(targetNode, shouldRemove) } - } - if(node.nextSibling) { - range.setStart(node.nextSibling, 0); - range.setEnd(node.nextSibling, length(node.nextSibling) > 0 ? 1 : 0); - nextRectangle = range.getBoundingClientRect(); - if(nextRectangle && nextRectangle.height) { - if(!selectionRectangle || verticalOverlap(node, nextRectangle) > verticalOverlap(node, selectionRectangle)) { - selectionRectangle = nextRectangle - } + if(isCollapsibleContainer(parent)) { + return mergeChildrenIntoParent(parent) } + return parent } - return selectionRectangle + this.mergeChildrenIntoParent = mergeChildrenIntoParent } - function handleUpdate() { - var selectionRect = getSelectionRect(), zoomLevel = cursor.getOdtDocument().getOdfCanvas().getZoomLevel(), caretRect; - if(isShown && cursor.getSelectionType() === ops.OdtCursor.RangeSelection) { - span.style.visibility = "visible" - }else { - span.style.visibility = "hidden" - } - if(selectionRect) { - span.style.top = "0"; - caretRect = domUtils.getBoundingClientRect(span); - if(selectionRect.height < MIN_CARET_HEIGHT_PX) { - selectionRect = {top:selectionRect.top - (MIN_CARET_HEIGHT_PX - selectionRect.height) / 2, height:MIN_CARET_HEIGHT_PX} + function mergeParagraphs(first, second, collapseRules) { + var child, mergeForward = false, destination = first, source = second, secondParent, insertionPoint = null; + if(collapseRules.isEmpty(first)) { + mergeForward = true; + if(second.parentNode !== first.parentNode) { + secondParent = second.parentNode; + first.parentNode.insertBefore(second, first.nextSibling) } - span.style.height = domUtils.adaptRangeDifferenceToZoomLevel(selectionRect.height, zoomLevel) + "px"; - span.style.top = domUtils.adaptRangeDifferenceToZoomLevel(selectionRect.top - caretRect.top, zoomLevel) + "px" - }else { - span.style.height = DEFAULT_CARET_HEIGHT; - span.style.top = DEFAULT_CARET_TOP - } - } - this.handleUpdate = handleUpdate; - this.refreshCursorBlinking = function() { - if(blinkOnRangeSelect || cursor.getSelectedRange().collapsed) { - shouldBlink = true; - blink(true) - }else { - shouldBlink = false; - span.style.opacity = "0" - } - }; - this.setFocus = function() { - shouldBlink = true; - avatar.markAsFocussed(true); - blink(true) - }; - this.removeFocus = function() { - shouldBlink = false; - avatar.markAsFocussed(false); - span.style.opacity = "1" - }; - this.show = function() { - isShown = true; - handleUpdate(); - avatar.markAsFocussed(true) - }; - this.hide = function() { - isShown = false; - handleUpdate(); - avatar.markAsFocussed(false) - }; - this.setAvatarImageUrl = function(url) { - avatar.setImageUrl(url) - }; - this.setColor = function(newColor) { - span.style.borderColor = newColor; - avatar.setColor(newColor) - }; - this.getCursor = function() { - return cursor - }; - this.getFocusElement = function() { - return span - }; - this.toggleHandleVisibility = function() { - if(avatar.isVisible()) { - avatar.hide() - }else { - avatar.show() + source = first; + destination = second; + insertionPoint = destination.getElementsByTagNameNS(editinfons, "editinfo")[0] || destination.firstChild } - }; - this.showHandle = function() { - avatar.show() - }; - this.hideHandle = function() { - avatar.hide() - }; - this.ensureVisible = function() { - var canvasElement = cursor.getOdtDocument().getOdfCanvas().getElement(), canvasContainerElement = canvasElement.parentNode, caretRect, canvasContainerRect, horizontalMargin = canvasContainerElement.offsetWidth - canvasContainerElement.clientWidth + 5, verticalMargin = canvasContainerElement.offsetHeight - canvasContainerElement.clientHeight + 5; - caretRect = getCaretClientRectWithMargin(span, {top:verticalMargin, left:horizontalMargin, bottom:verticalMargin, right:horizontalMargin}); - canvasContainerRect = canvasContainerElement.getBoundingClientRect(); - if(caretRect.top < canvasContainerRect.top) { - canvasContainerElement.scrollTop -= canvasContainerRect.top - caretRect.top - }else { - if(caretRect.bottom > canvasContainerRect.bottom) { - canvasContainerElement.scrollTop += caretRect.bottom - canvasContainerRect.bottom + while(source.hasChildNodes()) { + child = mergeForward ? source.lastChild : source.firstChild; + source.removeChild(child); + if(child.localName !== "editinfo") { + destination.insertBefore(child, insertionPoint) } } - if(caretRect.left < canvasContainerRect.left) { - canvasContainerElement.scrollLeft -= canvasContainerRect.left - caretRect.left - }else { - if(caretRect.right > canvasContainerRect.right) { - canvasContainerElement.scrollLeft += caretRect.right - canvasContainerRect.right - } + if(secondParent && collapseRules.isEmpty(secondParent)) { + collapseRules.mergeChildrenIntoParent(secondParent) } - handleUpdate() - }; - this.destroy = function(callback) { - avatar.destroy(function(err) { - if(err) { - callback(err) - }else { - cursorNode.removeChild(span); - callback() - } - }) + collapseRules.mergeChildrenIntoParent(source); + return destination + } + this.execute = function(odtDocument) { + var paragraphElement, destinationParagraph, range, textNodes, paragraphs, cursor = odtDocument.getCursor(memberid), collapseRules = new CollapsingRules(odtDocument.getRootNode()); + odtDocument.upgradeWhitespacesAtPosition(position); + odtDocument.upgradeWhitespacesAtPosition(position + length); + range = odtDocument.convertCursorToDomRange(position, length); + domUtils.splitBoundaries(range); + paragraphElement = odtDocument.getParagraphElement(range.startContainer); + textNodes = odfUtils.getTextElements(range, false, true); + paragraphs = odfUtils.getParagraphElements(range); + range.detach(); + textNodes.forEach(function(element) { + collapseRules.mergeChildrenIntoParent(element) + }); + destinationParagraph = paragraphs.reduce(function(destination, paragraph) { + return mergeParagraphs(destination, paragraph, collapseRules) + }); + odtDocument.emit(ops.OdtDocument.signalStepsRemoved, {position:position, length:length}); + odtDocument.downgradeWhitespacesAtPosition(position); + odtDocument.fixCursorPositions(); + odtDocument.getOdfCanvas().refreshSize(); + odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:destinationParagraph || paragraphElement, memberId:memberid, timeStamp:timestamp}); + if(cursor) { + cursor.resetSelectionType(); + odtDocument.emit(ops.OdtDocument.signalCursorMoved, cursor) + } + odtDocument.getOdfCanvas().rerenderAnnotations(); + return true }; - function init() { - var dom = cursor.getOdtDocument().getDOM(), htmlns = dom.documentElement.namespaceURI; - span = dom.createElementNS(htmlns, "span"); - span.style.top = DEFAULT_CARET_TOP; - cursorNode = cursor.getNode(); - cursorNode.appendChild(span); - avatar = new gui.Avatar(cursorNode, avatarInitiallyVisible); - handleUpdate() + this.spec = function() { + return{optype:"RemoveText", memberid:memberid, timestamp:timestamp, position:position, length:length} } - init() }; +ops.OpRemoveText.Spec; /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -14297,27 +14109,27 @@ gui.Caret = function Caret(cursor, avatarInitiallyVisible, blinkOnRangeSelect) { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -gui.PlainTextPasteboard = function PlainTextPasteboard(odtDocument, inputMemberId) { - function createOp(op, data) { - op.init(data); - return op - } - this.createPasteOps = function(data) { - var originalCursorPosition = odtDocument.getCursorPosition(inputMemberId), cursorPosition = originalCursorPosition, operations = [], paragraphs; - paragraphs = data.replace(/\r/g, "").split("\n"); - paragraphs.forEach(function(text) { - operations.push(createOp(new ops.OpSplitParagraph, {memberid:inputMemberId, position:cursorPosition})); - cursorPosition += 1; - operations.push(createOp(new ops.OpInsertText, {memberid:inputMemberId, position:cursorPosition, text:text})); - cursorPosition += text.length - }); - operations.push(createOp(new ops.OpRemoveText, {memberid:inputMemberId, position:originalCursorPosition, length:1})); - return operations +ops.OpSetBlob = function OpSetBlob() { + var memberid, timestamp, filename, mimetype, content; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + filename = data.filename; + mimetype = data.mimetype; + content = data.content + }; + this.isEdit = true; + this.execute = function(odtDocument) { + odtDocument.getOdfCanvas().odfContainer().setBlob(filename, mimetype, content); + return true + }; + this.spec = function() { + return{optype:"SetBlob", memberid:memberid, timestamp:timestamp, filename:filename, mimetype:mimetype, content:content} } }; /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -14352,42 +14164,40 @@ gui.PlainTextPasteboard = function PlainTextPasteboard(odtDocument, inputMemberI @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.Namespaces"); -runtime.loadClass("xmldom.LSSerializer"); -runtime.loadClass("odf.OdfNodeFilter"); -runtime.loadClass("odf.TextSerializer"); -gui.Clipboard = function Clipboard() { - var xmlSerializer, textSerializer, filter; - this.setDataFromRange = function(e, range) { - var result = true, setDataResult, clipboard = e.clipboardData, window = runtime.getWindow(), document = range.startContainer.ownerDocument, fragmentContainer; - if(!clipboard && window) { - clipboard = window.clipboardData - } - if(clipboard) { - fragmentContainer = document.createElement("span"); - fragmentContainer.appendChild(range.cloneContents()); - setDataResult = clipboard.setData("text/plain", textSerializer.writeToString(fragmentContainer)); - result = result && setDataResult; - setDataResult = clipboard.setData("text/html", xmlSerializer.writeToString(fragmentContainer, odf.Namespaces.namespaceMap)); - result = result && setDataResult; - e.preventDefault() - }else { - result = false - } - return result +ops.OpSetParagraphStyle = function OpSetParagraphStyle() { + var memberid, timestamp, position, styleName, textns = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = data.position; + styleName = data.styleName }; - function init() { - xmlSerializer = new xmldom.LSSerializer; - textSerializer = new odf.TextSerializer; - filter = new odf.OdfNodeFilter; - xmlSerializer.filter = filter; - textSerializer.filter = filter + this.isEdit = true; + this.execute = function(odtDocument) { + var iterator, paragraphNode; + iterator = odtDocument.getIteratorAtPosition(position); + paragraphNode = odtDocument.getParagraphElement(iterator.container()); + if(paragraphNode) { + if(styleName !== "") { + paragraphNode.setAttributeNS(textns, "text:style-name", styleName) + }else { + paragraphNode.removeAttributeNS(textns, "style-name") + } + odtDocument.getOdfCanvas().refreshSize(); + odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:paragraphNode, timeStamp:timestamp, memberId:memberid}); + odtDocument.getOdfCanvas().rerenderAnnotations(); + return true + } + return false + }; + this.spec = function() { + return{optype:"SetParagraphStyle", memberid:memberid, timestamp:timestamp, position:position, styleName:styleName} } - init() }; +ops.OpSetParagraphStyle.Spec; /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -14422,212 +14232,209 @@ gui.Clipboard = function Clipboard() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.EventNotifier"); -runtime.loadClass("core.Utils"); -runtime.loadClass("ops.OpApplyDirectStyling"); -runtime.loadClass("gui.StyleHelper"); -gui.DirectTextStyler = function DirectTextStyler(session, inputMemberId) { - var self = this, utils = new core.Utils, odtDocument = session.getOdtDocument(), styleHelper = new gui.StyleHelper(odtDocument.getFormatting()), eventNotifier = new core.EventNotifier([gui.DirectTextStyler.textStylingChanged]), directCursorStyleProperties, currentSelectionStyles = [], isBoldValue = false, isItalicValue = false, hasUnderlineValue = false, hasStrikeThroughValue = false, fontSizeValue, fontNameValue; - function get(obj, keys) { - var i = 0, key = keys[i]; - while(key && obj) { - obj = obj[key]; - i += 1; - key = keys[i] - } - return keys.length === i ? obj : undefined - } - function getCommonValue(objArray, keys) { - var value = get(objArray[0], keys); - return objArray.every(function(obj) { - return value === get(obj, keys) - }) ? value : undefined - } - function getAppliedStyles() { - var cursor = odtDocument.getCursor(inputMemberId), range = cursor && cursor.getSelectedRange(), selectionStyles = range && styleHelper.getAppliedStyles(range) || []; - if(selectionStyles[0] && directCursorStyleProperties) { - selectionStyles[0] = utils.mergeObjects(selectionStyles[0], (directCursorStyleProperties)) - } - return selectionStyles - } - function updatedCachedValues() { - var fontSize, diffMap; - currentSelectionStyles = getAppliedStyles(); - function noteChange(oldValue, newValue, id) { - if(oldValue !== newValue) { - if(diffMap === undefined) { - diffMap = {} - } - diffMap[id] = newValue - } - return newValue +ops.OpSplitParagraph = function OpSplitParagraph() { + var memberid, timestamp, position, odfUtils; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = data.position; + odfUtils = new odf.OdfUtils + }; + this.isEdit = true; + this.execute = function(odtDocument) { + var domPosition, paragraphNode, targetNode, node, splitNode, splitChildNode, keptChildNode; + odtDocument.upgradeWhitespacesAtPosition(position); + domPosition = odtDocument.getTextNodeAtStep(position, memberid); + if(!domPosition) { + return false } - isBoldValue = noteChange(isBoldValue, currentSelectionStyles ? styleHelper.isBold(currentSelectionStyles) : false, "isBold"); - isItalicValue = noteChange(isItalicValue, currentSelectionStyles ? styleHelper.isItalic(currentSelectionStyles) : false, "isItalic"); - hasUnderlineValue = noteChange(hasUnderlineValue, currentSelectionStyles ? styleHelper.hasUnderline(currentSelectionStyles) : false, "hasUnderline"); - hasStrikeThroughValue = noteChange(hasStrikeThroughValue, currentSelectionStyles ? styleHelper.hasStrikeThrough(currentSelectionStyles) : false, "hasStrikeThrough"); - fontSize = currentSelectionStyles && getCommonValue(currentSelectionStyles, ["style:text-properties", "fo:font-size"]); - fontSizeValue = noteChange(fontSizeValue, fontSize && parseFloat(fontSize), "fontSize"); - fontNameValue = noteChange(fontNameValue, currentSelectionStyles && getCommonValue(currentSelectionStyles, ["style:text-properties", "style:font-name"]), "fontName"); - if(diffMap) { - eventNotifier.emit(gui.DirectTextStyler.textStylingChanged, diffMap) + paragraphNode = odtDocument.getParagraphElement(domPosition.textNode); + if(!paragraphNode) { + return false } - } - function onCursorAdded(cursor) { - if(cursor.getMemberId() === inputMemberId) { - updatedCachedValues() + if(odfUtils.isListItem(paragraphNode.parentNode)) { + targetNode = paragraphNode.parentNode + }else { + targetNode = paragraphNode } - } - function onCursorRemoved(memberId) { - if(memberId === inputMemberId) { - updatedCachedValues() + if(domPosition.offset === 0) { + keptChildNode = domPosition.textNode.previousSibling; + splitChildNode = null + }else { + keptChildNode = domPosition.textNode; + if(domPosition.offset >= domPosition.textNode.length) { + splitChildNode = null + }else { + splitChildNode = (domPosition.textNode.splitText(domPosition.offset)) + } } - } - function onCursorMoved(cursor) { - if(cursor.getMemberId() === inputMemberId) { - updatedCachedValues() + node = domPosition.textNode; + while(node !== targetNode) { + node = node.parentNode; + splitNode = node.cloneNode(false); + if(splitChildNode) { + splitNode.appendChild(splitChildNode) + } + if(keptChildNode) { + while(keptChildNode && keptChildNode.nextSibling) { + splitNode.appendChild(keptChildNode.nextSibling) + } + }else { + while(node.firstChild) { + splitNode.appendChild(node.firstChild) + } + } + node.parentNode.insertBefore(splitNode, node.nextSibling); + keptChildNode = node; + splitChildNode = splitNode } - } - function onParagraphStyleModified() { - updatedCachedValues() - } - function onParagraphChanged(args) { - var cursor = odtDocument.getCursor(inputMemberId); - if(cursor && odtDocument.getParagraphElement(cursor.getNode()) === args.paragraphElement) { - updatedCachedValues() + if(odfUtils.isListItem(splitChildNode)) { + splitChildNode = splitChildNode.childNodes[0] } - } - function toggle(predicate, toggleMethod) { - var cursor = odtDocument.getCursor(inputMemberId), appliedStyles; - if(!cursor) { - return false + if(domPosition.textNode.length === 0) { + domPosition.textNode.parentNode.removeChild(domPosition.textNode) } - appliedStyles = styleHelper.getAppliedStyles(cursor.getSelectedRange()); - toggleMethod(!predicate(appliedStyles)); + odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:1}); + odtDocument.fixCursorPositions(); + odtDocument.getOdfCanvas().refreshSize(); + odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:paragraphNode, memberId:memberid, timeStamp:timestamp}); + odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:splitChildNode, memberId:memberid, timeStamp:timestamp}); + odtDocument.getOdfCanvas().rerenderAnnotations(); return true - } - function formatTextSelection(textProperties) { - var selection = odtDocument.getCursorSelection(inputMemberId), op, properties = {"style:text-properties":textProperties}; - if(selection.length !== 0) { - op = new ops.OpApplyDirectStyling; - op.init({memberid:inputMemberId, position:selection.position, length:selection.length, setProperties:properties}); - session.enqueue([op]) - }else { - directCursorStyleProperties = utils.mergeObjects(directCursorStyleProperties || {}, properties); - updatedCachedValues() - } - } - this.formatTextSelection = formatTextSelection; - function applyTextPropertyToSelection(propertyName, propertyValue) { - var textProperties = {}; - textProperties[propertyName] = propertyValue; - formatTextSelection(textProperties) - } - this.createCursorStyleOp = function(position, length) { - var styleOp = null; - if(directCursorStyleProperties) { - styleOp = new ops.OpApplyDirectStyling; - styleOp.init({memberid:inputMemberId, position:position, length:length, setProperties:directCursorStyleProperties}); - directCursorStyleProperties = null; - updatedCachedValues() - } - return styleOp }; - function clearCursorStyle(op) { - var spec = op.spec(); - if(directCursorStyleProperties && spec.memberid === inputMemberId) { - if(spec.optype !== "SplitParagraph") { - directCursorStyleProperties = null; - updatedCachedValues() - } - } - } - function setBold(checked) { - var value = checked ? "bold" : "normal"; - applyTextPropertyToSelection("fo:font-weight", value) - } - this.setBold = setBold; - function setItalic(checked) { - var value = checked ? "italic" : "normal"; - applyTextPropertyToSelection("fo:font-style", value) - } - this.setItalic = setItalic; - function setHasUnderline(checked) { - var value = checked ? "solid" : "none"; - applyTextPropertyToSelection("style:text-underline-style", value) - } - this.setHasUnderline = setHasUnderline; - function setHasStrikethrough(checked) { - var value = checked ? "solid" : "none"; - applyTextPropertyToSelection("style:text-line-through-style", value) - } - this.setHasStrikethrough = setHasStrikethrough; - function setFontSize(value) { - applyTextPropertyToSelection("fo:font-size", value + "pt") - } - this.setFontSize = setFontSize; - function setFontName(value) { - applyTextPropertyToSelection("style:font-name", value) + this.spec = function() { + return{optype:"SplitParagraph", memberid:memberid, timestamp:timestamp, position:position} } - this.setFontName = setFontName; - this.getAppliedStyles = function() { - return currentSelectionStyles - }; - this.toggleBold = toggle.bind(self, styleHelper.isBold, setBold); - this.toggleItalic = toggle.bind(self, styleHelper.isItalic, setItalic); - this.toggleUnderline = toggle.bind(self, styleHelper.hasUnderline, setHasUnderline); - this.toggleStrikethrough = toggle.bind(self, styleHelper.hasStrikeThrough, setHasStrikethrough); - this.isBold = function() { - return isBoldValue - }; - this.isItalic = function() { - return isItalicValue - }; - this.hasUnderline = function() { - return hasUnderlineValue - }; - this.hasStrikeThrough = function() { - return hasStrikeThroughValue - }; - this.fontSize = function() { - return fontSizeValue - }; - this.fontName = function() { - return fontNameValue +}; +ops.OpSplitParagraph.Spec; +/* + + Copyright (C) 2013 KO GmbH + + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("ops.Member"); +runtime.loadClass("xmldom.XPath"); +ops.OpUpdateMember = function OpUpdateMember() { + var memberid, timestamp, setProperties, removedProperties, doc; + this.init = function(data) { + memberid = data.memberid; + timestamp = parseInt(data.timestamp, 10); + setProperties = data.setProperties; + removedProperties = data.removedProperties }; - this.subscribe = function(eventid, cb) { - eventNotifier.subscribe(eventid, cb) + this.isEdit = false; + function updateCreators() { + var xpath = xmldom.XPath, xp = "//dc:creator[@editinfo:memberid='" + memberid + "']", creators = xpath.getODFElementsWithXPath(doc.getRootNode(), xp, function(prefix) { + if(prefix === "editinfo") { + return"urn:webodf:names:editinfo" + } + return odf.Namespaces.lookupNamespaceURI(prefix) + }), i; + for(i = 0;i < creators.length;i += 1) { + creators[i].textContent = setProperties.fullName + } + } + this.execute = function(odtDocument) { + doc = odtDocument; + var member = odtDocument.getMember(memberid); + if(!member) { + return false + } + if(removedProperties) { + member.removeProperties(removedProperties) + } + if(setProperties) { + member.setProperties(setProperties); + if(setProperties.fullName) { + updateCreators() + } + } + odtDocument.emit(ops.OdtDocument.signalMemberUpdated, member); + return true }; - this.unsubscribe = function(eventid, cb) { - eventNotifier.unsubscribe(eventid, cb) + this.spec = function() { + return{optype:"UpdateMember", memberid:memberid, timestamp:timestamp, setProperties:setProperties, removedProperties:removedProperties} + } +}; +/* + + Copyright (C) 2013 KO GmbH + + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +ops.OpUpdateMetadata = function OpUpdateMetadata() { + var memberid, timestamp, setProperties, removedProperties; + this.init = function(data) { + memberid = data.memberid; + timestamp = parseInt(data.timestamp, 10); + setProperties = data.setProperties; + removedProperties = data.removedProperties }; - this.destroy = function(callback) { - odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); - odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); - odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); - odtDocument.unsubscribe(ops.OdtDocument.signalOperationExecuted, clearCursorStyle); - callback() + this.isEdit = true; + this.execute = function(odtDocument) { + var metadataManager = odtDocument.getOdfCanvas().odfContainer().getMetadataManager(), removedPropertiesArray = [], blockedProperties = ["dc:date", "dc:creator", "meta:editing-cycles"]; + if(setProperties) { + blockedProperties.forEach(function(el) { + if(setProperties[el]) { + return false + } + }) + } + if(removedProperties) { + blockedProperties.forEach(function(el) { + if(removedPropertiesArray.indexOf(el) !== -1) { + return false + } + }); + removedPropertiesArray = removedProperties.attributes.split(",") + } + metadataManager.setMetadata(setProperties, removedPropertiesArray); + return true }; - function init() { - odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); - odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); - odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); - odtDocument.subscribe(ops.OdtDocument.signalOperationExecuted, clearCursorStyle); - updatedCachedValues() + this.spec = function() { + return{optype:"UpdateMetadata", memberid:memberid, timestamp:timestamp, setProperties:setProperties, removedProperties:removedProperties} } - init() }; -gui.DirectTextStyler.textStylingChanged = "textStyling/changed"; -(function() { - return gui.DirectTextStyler -})(); /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -14662,154 +14469,64 @@ gui.DirectTextStyler.textStylingChanged = "textStyling/changed"; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.EventNotifier"); -runtime.loadClass("core.Utils"); -runtime.loadClass("odf.OdfUtils"); -runtime.loadClass("ops.OpAddStyle"); -runtime.loadClass("ops.OpSetParagraphStyle"); -runtime.loadClass("gui.StyleHelper"); -gui.DirectParagraphStyler = function DirectParagraphStyler(session, inputMemberId, objectNameGenerator) { - var odtDocument = session.getOdtDocument(), utils = new core.Utils, odfUtils = new odf.OdfUtils, styleHelper = new gui.StyleHelper(odtDocument.getFormatting()), eventNotifier = new core.EventNotifier([gui.DirectParagraphStyler.paragraphStylingChanged]), isAlignedLeftValue, isAlignedCenterValue, isAlignedRightValue, isAlignedJustifiedValue; - function updatedCachedValues() { - var cursor = odtDocument.getCursor(inputMemberId), range = cursor && cursor.getSelectedRange(), diffMap; - function noteChange(oldValue, newValue, id) { - if(oldValue !== newValue) { - if(diffMap === undefined) { - diffMap = {} - } - diffMap[id] = newValue - } - return newValue - } - isAlignedLeftValue = noteChange(isAlignedLeftValue, range ? styleHelper.isAlignedLeft(range) : false, "isAlignedLeft"); - isAlignedCenterValue = noteChange(isAlignedCenterValue, range ? styleHelper.isAlignedCenter(range) : false, "isAlignedCenter"); - isAlignedRightValue = noteChange(isAlignedRightValue, range ? styleHelper.isAlignedRight(range) : false, "isAlignedRight"); - isAlignedJustifiedValue = noteChange(isAlignedJustifiedValue, range ? styleHelper.isAlignedJustified(range) : false, "isAlignedJustified"); - if(diffMap) { - eventNotifier.emit(gui.DirectParagraphStyler.paragraphStylingChanged, diffMap) - } - } - function onCursorAdded(cursor) { - if(cursor.getMemberId() === inputMemberId) { - updatedCachedValues() - } - } - function onCursorRemoved(memberId) { - if(memberId === inputMemberId) { - updatedCachedValues() - } - } - function onCursorMoved(cursor) { - if(cursor.getMemberId() === inputMemberId) { - updatedCachedValues() - } - } - function onParagraphStyleModified() { - updatedCachedValues() - } - function onParagraphChanged(args) { - var cursor = odtDocument.getCursor(inputMemberId); - if(cursor && odtDocument.getParagraphElement(cursor.getNode()) === args.paragraphElement) { - updatedCachedValues() +runtime.loadClass("odf.Namespaces"); +ops.OpUpdateParagraphStyle = function OpUpdateParagraphStyle() { + var memberid, timestamp, styleName, setProperties, removedProperties, paragraphPropertiesName = "style:paragraph-properties", textPropertiesName = "style:text-properties", stylens = odf.Namespaces.stylens; + function removedAttributesFromStyleNode(node, removedAttributeNames) { + var i, attributeNameParts, attributeNameList = removedAttributeNames ? removedAttributeNames.split(",") : []; + for(i = 0;i < attributeNameList.length;i += 1) { + attributeNameParts = attributeNameList[i].split(":"); + node.removeAttributeNS(odf.Namespaces.lookupNamespaceURI(attributeNameParts[0]), attributeNameParts[1]) } } - this.isAlignedLeft = function() { - return isAlignedLeftValue - }; - this.isAlignedCenter = function() { - return isAlignedCenterValue - }; - this.isAlignedRight = function() { - return isAlignedRightValue - }; - this.isAlignedJustified = function() { - return isAlignedJustifiedValue - }; - function applyParagraphDirectStyling(applyDirectStyling) { - var range = odtDocument.getCursor(inputMemberId).getSelectedRange(), position = odtDocument.getCursorPosition(inputMemberId), paragraphs = odfUtils.getParagraphElements(range), formatting = odtDocument.getFormatting(); - paragraphs.forEach(function(paragraph) { - var paragraphStartPoint = position + odtDocument.getDistanceFromCursor(inputMemberId, paragraph, 0), paragraphStyleName = paragraph.getAttributeNS(odf.Namespaces.textns, "style-name"), newParagraphStyleName = objectNameGenerator.generateStyleName(), opAddStyle, opSetParagraphStyle, paragraphProperties; - paragraphStartPoint += 1; - if(paragraphStyleName) { - paragraphProperties = formatting.createDerivedStyleObject(paragraphStyleName, "paragraph", {}) - } - paragraphProperties = applyDirectStyling(paragraphProperties || {}); - opAddStyle = new ops.OpAddStyle; - opAddStyle.init({memberid:inputMemberId, styleName:newParagraphStyleName, styleFamily:"paragraph", isAutomaticStyle:true, setProperties:paragraphProperties}); - opSetParagraphStyle = new ops.OpSetParagraphStyle; - opSetParagraphStyle.init({memberid:inputMemberId, styleName:newParagraphStyleName, position:paragraphStartPoint}); - session.enqueue([opAddStyle, opSetParagraphStyle]) - }) - } - function applySimpleParagraphDirectStyling(styleOverrides) { - applyParagraphDirectStyling(function(paragraphStyle) { - return utils.mergeObjects(paragraphStyle, styleOverrides) - }) - } - function alignParagraph(alignment) { - applySimpleParagraphDirectStyling({"style:paragraph-properties":{"fo:text-align":alignment}}) - } - this.alignParagraphLeft = function() { - alignParagraph("left"); - return true - }; - this.alignParagraphCenter = function() { - alignParagraph("center"); - return true - }; - this.alignParagraphRight = function() { - alignParagraph("right"); - return true - }; - this.alignParagraphJustified = function() { - alignParagraph("justify"); - return true + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + styleName = data.styleName; + setProperties = data.setProperties; + removedProperties = data.removedProperties }; - function modifyParagraphIndent(direction, paragraphStyle) { - var tabStopDistance = odtDocument.getFormatting().getDefaultTabStopDistance(), paragraphProperties = paragraphStyle["style:paragraph-properties"], indentValue = paragraphProperties && paragraphProperties["fo:margin-left"], indent = indentValue && odfUtils.parseLength(indentValue), newIndent; - if(indent && indent.unit === tabStopDistance.unit) { - newIndent = indent.value + direction * tabStopDistance.value + indent.unit + this.isEdit = true; + this.execute = function(odtDocument) { + var formatting = odtDocument.getFormatting(), styleNode, paragraphPropertiesNode, textPropertiesNode; + if(styleName !== "") { + styleNode = odtDocument.getParagraphStyleElement(styleName) }else { - newIndent = direction * tabStopDistance.value + tabStopDistance.unit + styleNode = formatting.getDefaultStyleElement("paragraph") + } + if(styleNode) { + paragraphPropertiesNode = styleNode.getElementsByTagNameNS(stylens, "paragraph-properties")[0]; + textPropertiesNode = styleNode.getElementsByTagNameNS(stylens, "text-properties")[0]; + if(setProperties) { + formatting.updateStyle(styleNode, setProperties) + } + if(removedProperties) { + if(removedProperties[paragraphPropertiesName]) { + removedAttributesFromStyleNode(paragraphPropertiesNode, removedProperties[paragraphPropertiesName].attributes); + if(paragraphPropertiesNode.attributes.length === 0) { + styleNode.removeChild(paragraphPropertiesNode) + } + } + if(removedProperties[textPropertiesName]) { + removedAttributesFromStyleNode(textPropertiesNode, removedProperties[textPropertiesName].attributes); + if(textPropertiesNode.attributes.length === 0) { + styleNode.removeChild(textPropertiesNode) + } + } + removedAttributesFromStyleNode(styleNode, removedProperties.attributes) + } + odtDocument.getOdfCanvas().refreshCSS(); + odtDocument.emit(ops.OdtDocument.signalParagraphStyleModified, styleName); + odtDocument.getOdfCanvas().rerenderAnnotations(); + return true } - return utils.mergeObjects(paragraphStyle, {"style:paragraph-properties":{"fo:margin-left":newIndent}}) - } - this.indent = function() { - applyParagraphDirectStyling(modifyParagraphIndent.bind(null, 1)); - return true - }; - this.outdent = function() { - applyParagraphDirectStyling(modifyParagraphIndent.bind(null, -1)); - return true - }; - this.subscribe = function(eventid, cb) { - eventNotifier.subscribe(eventid, cb) - }; - this.unsubscribe = function(eventid, cb) { - eventNotifier.unsubscribe(eventid, cb) - }; - this.destroy = function(callback) { - odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); - odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); - odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); - callback() + return false }; - function init() { - odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); - odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); - odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); - updatedCachedValues() + this.spec = function() { + return{optype:"UpdateParagraphStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, setProperties:setProperties, removedProperties:removedProperties} } - init() }; -gui.DirectParagraphStyler.paragraphStylingChanged = "paragraphStyling/changed"; -(function() { - return gui.DirectParagraphStyler -})(); +ops.OpUpdateParagraphStyle.Spec; /* Copyright (C) 2012-2013 KO GmbH @@ -14847,183 +14564,50 @@ gui.DirectParagraphStyler.paragraphStylingChanged = "paragraphStyling/changed"; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -gui.KeyboardHandler = function KeyboardHandler() { - var modifier = gui.KeyboardHandler.Modifier, defaultBinding = null, bindings = {}; - function getModifiers(e) { - var modifiers = modifier.None; - if(e.metaKey) { - modifiers |= modifier.Meta - } - if(e.ctrlKey) { - modifiers |= modifier.Ctrl - } - if(e.altKey) { - modifiers |= modifier.Alt - } - if(e.shiftKey) { - modifiers |= modifier.Shift - } - return modifiers - } - function getKeyCombo(keyCode, modifiers) { - if(!modifiers) { - modifiers = modifier.None - } - return keyCode + ":" + modifiers - } - this.setDefault = function(callback) { - defaultBinding = callback - }; - this.bind = function(keyCode, modifiers, callback) { - var keyCombo = getKeyCombo(keyCode, modifiers); - runtime.assert(bindings.hasOwnProperty(keyCombo) === false, "tried to overwrite the callback handler of key combo: " + keyCombo); - bindings[keyCombo] = callback - }; - this.unbind = function(keyCode, modifiers) { - var keyCombo = getKeyCombo(keyCode, modifiers); - delete bindings[keyCombo] - }; - this.reset = function() { - defaultBinding = null; - bindings = {} +runtime.loadClass("ops.OpAddMember"); +runtime.loadClass("ops.OpUpdateMember"); +runtime.loadClass("ops.OpRemoveMember"); +runtime.loadClass("ops.OpAddCursor"); +runtime.loadClass("ops.OpApplyDirectStyling"); +runtime.loadClass("ops.OpRemoveCursor"); +runtime.loadClass("ops.OpMoveCursor"); +runtime.loadClass("ops.OpSetBlob"); +runtime.loadClass("ops.OpRemoveBlob"); +runtime.loadClass("ops.OpInsertImage"); +runtime.loadClass("ops.OpInsertTable"); +runtime.loadClass("ops.OpInsertText"); +runtime.loadClass("ops.OpRemoveText"); +runtime.loadClass("ops.OpSplitParagraph"); +runtime.loadClass("ops.OpSetParagraphStyle"); +runtime.loadClass("ops.OpUpdateParagraphStyle"); +runtime.loadClass("ops.OpAddStyle"); +runtime.loadClass("ops.OpRemoveStyle"); +runtime.loadClass("ops.OpAddAnnotation"); +runtime.loadClass("ops.OpRemoveAnnotation"); +runtime.loadClass("ops.OpUpdateMetadata"); +ops.OperationFactory = function OperationFactory() { + var specs; + this.register = function(specName, specConstructor) { + specs[specName] = specConstructor }; - this.handleEvent = function(e) { - var keyCombo = getKeyCombo(e.keyCode, getModifiers(e)), callback = bindings[keyCombo], handled = false; - if(callback) { - handled = callback() - }else { - if(defaultBinding !== null) { - handled = defaultBinding(e) - } - } - if(handled) { - if(e.preventDefault) { - e.preventDefault() - }else { - e.returnValue = false - } + this.create = function(spec) { + var op = null, specConstructor = specs[spec.optype]; + if(specConstructor) { + op = specConstructor(spec); + op.init(spec) } - } -}; -gui.KeyboardHandler.Modifier = {None:0, Meta:1, Ctrl:2, Alt:4, CtrlAlt:6, Shift:8, MetaShift:9, CtrlShift:10, AltShift:12}; -gui.KeyboardHandler.KeyCode = {Backspace:8, Tab:9, Clear:12, Enter:13, End:35, Home:36, Left:37, Up:38, Right:39, Down:40, Delete:46, A:65, B:66, C:67, D:68, E:69, F:70, G:71, H:72, I:73, J:74, K:75, L:76, M:77, N:78, O:79, P:80, Q:81, R:82, S:83, T:84, U:85, V:86, W:87, X:88, Y:89, Z:90}; -(function() { - return gui.KeyboardHandler -})(); -runtime.loadClass("odf.Namespaces"); -runtime.loadClass("odf.ObjectNameGenerator"); -gui.ImageManager = function ImageManager(session, inputMemberId, objectNameGenerator) { - var cmPerPixel = 0.0264583333333334, fileExtensionByMimetype = {"image/gif":".gif", "image/jpeg":".jpg", "image/png":".png"}, textns = odf.Namespaces.textns, odtDocument = session.getOdtDocument(), formatting = odtDocument.getFormatting(), paragraphStyleToPageContentSizeMap = {}; - function createAddGraphicsStyleOp(name) { - var op = new ops.OpAddStyle; - op.init({memberid:inputMemberId, styleName:name, styleFamily:"graphic", isAutomaticStyle:false, setProperties:{"style:graphic-properties":{"text:anchor-type":"paragraph", "svg:x":"0cm", "svg:y":"0cm", "style:wrap":"dynamic", "style:number-wrapped-paragraphs":"no-limit", "style:wrap-contour":"false", "style:vertical-pos":"top", "style:vertical-rel":"paragraph", "style:horizontal-pos":"center", "style:horizontal-rel":"paragraph"}}}); - return op - } - function createAddFrameStyleOp(styleName, parentStyleName) { - var op = new ops.OpAddStyle; - op.init({memberid:inputMemberId, styleName:styleName, styleFamily:"graphic", isAutomaticStyle:true, setProperties:{"style:parent-style-name":parentStyleName, "style:graphic-properties":{"style:vertical-pos":"top", "style:vertical-rel":"baseline", "style:horizontal-pos":"center", "style:horizontal-rel":"paragraph", "fo:background-color":"transparent", "style:background-transparency":"100%", "style:shadow":"none", "style:mirror":"none", "fo:clip":"rect(0cm, 0cm, 0cm, 0cm)", "draw:luminance":"0%", - "draw:contrast":"0%", "draw:red":"0%", "draw:green":"0%", "draw:blue":"0%", "draw:gamma":"100%", "draw:color-inversion":"false", "draw:image-opacity":"100%", "draw:color-mode":"standard"}}}); return op - } - function getFileExtension(mimetype) { - mimetype = mimetype.toLowerCase(); - return fileExtensionByMimetype.hasOwnProperty(mimetype) ? fileExtensionByMimetype[mimetype] : null - } - function insertImageInternal(mimetype, content, widthInCm, heightInCm) { - var graphicsStyleName = "Graphics", stylesElement = odtDocument.getOdfCanvas().odfContainer().rootElement.styles, fileExtension = getFileExtension(mimetype), fileName, graphicsStyleElement, frameStyleName, op, operations = []; - runtime.assert(fileExtension !== null, "Image type is not supported: " + mimetype); - fileName = "Pictures/" + objectNameGenerator.generateImageName() + fileExtension; - op = new ops.OpSetBlob; - op.init({memberid:inputMemberId, filename:fileName, mimetype:mimetype, content:content}); - operations.push(op); - graphicsStyleElement = formatting.getStyleElement(graphicsStyleName, "graphic", [stylesElement]); - if(!graphicsStyleElement) { - op = createAddGraphicsStyleOp(graphicsStyleName); - operations.push(op) - } - frameStyleName = objectNameGenerator.generateStyleName(); - op = createAddFrameStyleOp(frameStyleName, graphicsStyleName); - operations.push(op); - op = new ops.OpInsertImage; - op.init({memberid:inputMemberId, position:odtDocument.getCursorPosition(inputMemberId), filename:fileName, frameWidth:widthInCm + "cm", frameHeight:heightInCm + "cm", frameStyleName:frameStyleName, frameName:objectNameGenerator.generateFrameName()}); - operations.push(op); - session.enqueue(operations) - } - function trimmedSize(originalSize, pageContentSize) { - var widthRatio = 1, heightRatio = 1, ratio; - if(originalSize.width > pageContentSize.width) { - widthRatio = pageContentSize.width / originalSize.width - } - if(originalSize.height > pageContentSize.height) { - heightRatio = pageContentSize.height / originalSize.height - } - ratio = Math.min(widthRatio, heightRatio); - return{width:originalSize.width * ratio, height:originalSize.height * ratio} - } - this.insertImage = function(mimetype, content, widthInPx, heightInPx) { - var paragraphElement, styleName, pageContentSize, originalSize, newSize; - runtime.assert(widthInPx > 0 && heightInPx > 0, "Both width and height of the image should be greater than 0px."); - paragraphElement = odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode()); - styleName = paragraphElement.getAttributeNS(textns, "style-name"); - if(!paragraphStyleToPageContentSizeMap.hasOwnProperty(styleName)) { - paragraphStyleToPageContentSizeMap[styleName] = formatting.getContentSize(styleName, "paragraph") - } - pageContentSize = paragraphStyleToPageContentSizeMap[styleName]; - originalSize = {width:widthInPx * cmPerPixel, height:heightInPx * cmPerPixel}; - newSize = trimmedSize(originalSize, pageContentSize); - insertImageInternal(mimetype, content, newSize.width, newSize.height) - } -}; -runtime.loadClass("odf.Namespaces"); -gui.ImageSelector = function ImageSelector(odfCanvas) { - var svgns = odf.Namespaces.svgns, imageSelectorId = "imageSelector", selectorBorderWidth = 1, squareClassNames = ["topLeft", "topRight", "bottomRight", "bottomLeft", "topMiddle", "rightMiddle", "bottomMiddle", "leftMiddle"], document = odfCanvas.getElement().ownerDocument, hasSelection = false; - function createSelectorElement() { - var sizerElement = odfCanvas.getSizer(), selectorElement, squareElement; - selectorElement = document.createElement("div"); - selectorElement.id = "imageSelector"; - selectorElement.style.borderWidth = selectorBorderWidth + "px"; - sizerElement.appendChild(selectorElement); - squareClassNames.forEach(function(className) { - squareElement = document.createElement("div"); - squareElement.className = className; - selectorElement.appendChild(squareElement) - }); - return selectorElement - } - function getPosition(element, referenceElement) { - var rect = element.getBoundingClientRect(), refRect = referenceElement.getBoundingClientRect(), zoomLevel = odfCanvas.getZoomLevel(); - return{left:(rect.left - refRect.left) / zoomLevel - selectorBorderWidth, top:(rect.top - refRect.top) / zoomLevel - selectorBorderWidth} - } - this.select = function(frameElement) { - var selectorElement = document.getElementById(imageSelectorId), position; - if(!selectorElement) { - selectorElement = createSelectorElement() - } - hasSelection = true; - position = getPosition(frameElement, (selectorElement.parentNode)); - selectorElement.style.display = "block"; - selectorElement.style.left = position.left + "px"; - selectorElement.style.top = position.top + "px"; - selectorElement.style.width = frameElement.getAttributeNS(svgns, "width"); - selectorElement.style.height = frameElement.getAttributeNS(svgns, "height") - }; - this.clearSelection = function() { - var selectorElement; - if(hasSelection) { - selectorElement = document.getElementById(imageSelectorId); - if(selectorElement) { - selectorElement.style.display = "none" - } - } - hasSelection = false }; - this.isSelectorElement = function(node) { - var selectorElement = document.getElementById(imageSelectorId); - if(!selectorElement) { - return false + function constructor(OperationType) { + return function() { + return new OperationType } - return node === selectorElement || node.parentNode === selectorElement } + function init() { + specs = {AddMember:constructor(ops.OpAddMember), UpdateMember:constructor(ops.OpUpdateMember), RemoveMember:constructor(ops.OpRemoveMember), AddCursor:constructor(ops.OpAddCursor), ApplyDirectStyling:constructor(ops.OpApplyDirectStyling), SetBlob:constructor(ops.OpSetBlob), RemoveBlob:constructor(ops.OpRemoveBlob), InsertImage:constructor(ops.OpInsertImage), InsertTable:constructor(ops.OpInsertTable), InsertText:constructor(ops.OpInsertText), RemoveText:constructor(ops.OpRemoveText), SplitParagraph:constructor(ops.OpSplitParagraph), + SetParagraphStyle:constructor(ops.OpSetParagraphStyle), UpdateParagraphStyle:constructor(ops.OpUpdateParagraphStyle), AddStyle:constructor(ops.OpAddStyle), RemoveStyle:constructor(ops.OpRemoveStyle), MoveCursor:constructor(ops.OpMoveCursor), RemoveCursor:constructor(ops.OpRemoveCursor), AddAnnotation:constructor(ops.OpAddAnnotation), RemoveAnnotation:constructor(ops.OpRemoveAnnotation), UpdateMetadata:constructor(ops.OpUpdateMetadata)} + } + init() }; /* @@ -15062,103 +14646,24 @@ gui.ImageSelector = function ImageSelector(odfCanvas) { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.PositionFilter"); -gui.TextManipulator = function TextManipulator(session, inputMemberId, directStyleOp) { - var odtDocument = session.getOdtDocument(), FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; - function createOpRemoveSelection(selection) { - var op = new ops.OpRemoveText; - op.init({memberid:inputMemberId, position:selection.position, length:selection.length}); - return op - } - function toForwardSelection(selection) { - if(selection.length < 0) { - selection.position += selection.length; - selection.length = -selection.length - } - return selection - } - this.enqueueParagraphSplittingOps = function() { - var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op, operations = []; - if(selection.length > 0) { - op = createOpRemoveSelection(selection); - operations.push(op) - } - op = new ops.OpSplitParagraph; - op.init({memberid:inputMemberId, position:selection.position}); - operations.push(op); - session.enqueue(operations); - return true - }; - function hasPositionInDirection(cursorNode, forward) { - var rootConstrainedFilter = new core.PositionFilterChain, iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootElement(cursorNode)), nextPosition = (forward ? iterator.nextPosition : iterator.previousPosition); - rootConstrainedFilter.addFilter("BaseFilter", odtDocument.getPositionFilter()); - rootConstrainedFilter.addFilter("RootFilter", odtDocument.createRootFilter(inputMemberId)); - iterator.setUnfilteredPosition(cursorNode, 0); - while(nextPosition()) { - if(rootConstrainedFilter.acceptPosition(iterator) === FILTER_ACCEPT) { - return true - } - } - return false - } - this.removeTextByBackspaceKey = function() { - var cursor = odtDocument.getCursor(inputMemberId), selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op = null; - if(selection.length === 0) { - if(hasPositionInDirection(cursor.getNode(), false)) { - op = new ops.OpRemoveText; - op.init({memberid:inputMemberId, position:selection.position - 1, length:1}); - session.enqueue([op]) - } - }else { - op = createOpRemoveSelection(selection); - session.enqueue([op]) - } - return op !== null - }; - this.removeTextByDeleteKey = function() { - var cursor = odtDocument.getCursor(inputMemberId), selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op = null; - if(selection.length === 0) { - if(hasPositionInDirection(cursor.getNode(), true)) { - op = new ops.OpRemoveText; - op.init({memberid:inputMemberId, position:selection.position, length:1}); - session.enqueue([op]) - } - }else { - op = createOpRemoveSelection(selection); - session.enqueue([op]) - } - return op !== null - }; - this.removeCurrentSelection = function() { - var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op; - if(selection.length !== 0) { - op = createOpRemoveSelection(selection); - session.enqueue([op]) - } - return true - }; - function insertText(text) { - var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op, stylingOp, operations = []; - if(selection.length > 0) { - op = createOpRemoveSelection(selection); - operations.push(op) - } - op = new ops.OpInsertText; - op.init({memberid:inputMemberId, position:selection.position, text:text}); - operations.push(op); - if(directStyleOp) { - stylingOp = directStyleOp(selection.position, text.length); - if(stylingOp) { - operations.push(stylingOp) - } - } - session.enqueue(operations) - } - this.insertText = insertText +ops.OperationRouter = function OperationRouter() { +}; +ops.OperationRouter.prototype.setOperationFactory = function(f) { +}; +ops.OperationRouter.prototype.setPlaybackFunction = function(playback_func) { +}; +ops.OperationRouter.prototype.push = function(operations) { +}; +ops.OperationRouter.prototype.close = function(callback) { +}; +ops.OperationRouter.prototype.subscribe = function(eventId, cb) { +}; +ops.OperationRouter.prototype.unsubscribe = function(eventId, cb) { +}; +ops.OperationRouter.prototype.hasLocalUnsyncedOps = function() { +}; +ops.OperationRouter.prototype.hasSessionHostConnection = function() { }; -(function() { - return gui.TextManipulator -})(); /* Copyright (C) 2013 KO GmbH @@ -15183,905 +14688,1213 @@ gui.TextManipulator = function TextManipulator(session, inputMemberId, directSty @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.EventNotifier"); -runtime.loadClass("core.PositionFilter"); -runtime.loadClass("ops.OpAddAnnotation"); -runtime.loadClass("ops.OpRemoveAnnotation"); -runtime.loadClass("gui.SelectionMover"); -gui.AnnotationController = function AnnotationController(session, inputMemberId) { - var odtDocument = session.getOdtDocument(), isAnnotatable = false, eventNotifier = new core.EventNotifier([gui.AnnotationController.annotatableChanged]), officens = odf.Namespaces.officens; - function isWithinAnnotation(node, container) { - while(node && node !== container) { - if(node.namespaceURI === officens && node.localName === "annotation") { - return true - } - node = node.parentNode - } - return false - } - function updatedCachedValues() { - var cursor = odtDocument.getCursor(inputMemberId), cursorNode = cursor && cursor.getNode(), newIsAnnotatable = cursorNode && !isWithinAnnotation(cursorNode, odtDocument.getRootNode()); - if(newIsAnnotatable !== isAnnotatable) { - isAnnotatable = newIsAnnotatable; - eventNotifier.emit(gui.AnnotationController.annotatableChanged, isAnnotatable) - } - } - function onCursorAdded(cursor) { - if(cursor.getMemberId() === inputMemberId) { - updatedCachedValues() - } - } - function onCursorRemoved(memberId) { - if(memberId === inputMemberId) { - updatedCachedValues() - } - } - function onCursorMoved(cursor) { - if(cursor.getMemberId() === inputMemberId) { - updatedCachedValues() - } - } - this.isAnnotatable = function() { - return isAnnotatable - }; - this.addAnnotation = function() { - var op = new ops.OpAddAnnotation, selection = odtDocument.getCursorSelection(inputMemberId), length = selection.length, position = selection.position; - if(!isAnnotatable) { - return - } - position = length >= 0 ? position : position + length; - length = Math.abs(length); - op.init({memberid:inputMemberId, position:position, length:length, name:inputMemberId + Date.now()}); - session.enqueue([op]) - }; - this.removeAnnotation = function(annotationNode) { - var startStep, endStep, op, moveCursor; - startStep = odtDocument.convertDomPointToCursorStep(annotationNode, 0) + 1; - endStep = odtDocument.convertDomPointToCursorStep(annotationNode, annotationNode.childNodes.length); - op = new ops.OpRemoveAnnotation; - op.init({memberid:inputMemberId, position:startStep, length:endStep - startStep}); - moveCursor = new ops.OpMoveCursor; - moveCursor.init({memberid:inputMemberId, position:startStep > 0 ? startStep - 1 : startStep, length:0}); - session.enqueue([op, moveCursor]) - }; - this.subscribe = function(eventid, cb) { - eventNotifier.subscribe(eventid, cb) - }; - this.unsubscribe = function(eventid, cb) { - eventNotifier.unsubscribe(eventid, cb) - }; - this.destroy = function(callback) { - odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); - callback() - }; - function init() { - odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); - updatedCachedValues() - } - init() -}; -gui.AnnotationController.annotatableChanged = "annotatable/changed"; -(function() { - return gui.AnnotationController -})(); -gui.EventManager = function EventManager(odtDocument) { - var canvasElement = odtDocument.getOdfCanvas().getElement(), window = runtime.getWindow(), bindToDirectHandler = {"beforecut":true, "beforepaste":true}, bindToWindow; - function EventDelegate() { - var self = this, recentEvents = []; - this.handlers = []; - this.isSubscribed = false; - this.handleEvent = function(e) { - if(recentEvents.indexOf(e) === -1) { - recentEvents.push(e); - self.handlers.forEach(function(handler) { - handler(e) - }); - runtime.setTimeout(function() { - recentEvents.splice(recentEvents.indexOf(e), 1) - }, 0) - } - } - } - function WindowScrollState(window) { - var x = window.scrollX, y = window.scrollY; - this.restore = function() { - if(window.scrollX !== x || window.scrollY !== y) { - window.scrollTo(x, y) - } - } +ops.OperationTransformMatrix = function OperationTransformMatrix() { + function invertMoveCursorSpecRange(moveCursorSpec) { + moveCursorSpec.position = moveCursorSpec.position + moveCursorSpec.length; + moveCursorSpec.length *= -1 } - function ElementScrollState(element) { - var top = element.scrollTop, left = element.scrollLeft; - this.restore = function() { - if(element.scrollTop !== top || element.scrollLeft !== left) { - element.scrollTop = top; - element.scrollLeft = left - } + function invertMoveCursorSpecRangeOnNegativeLength(moveCursorSpec) { + var isBackwards = moveCursorSpec.length < 0; + if(isBackwards) { + invertMoveCursorSpecRange(moveCursorSpec) } + return isBackwards } - function listenEvent(eventTarget, eventType, eventHandler) { - var onVariant = "on" + eventType, bound = false; - if(eventTarget.attachEvent) { - bound = eventTarget.attachEvent(onVariant, eventHandler) - } - if(!bound && eventTarget.addEventListener) { - eventTarget.addEventListener(eventType, eventHandler, false); - bound = true - } - if((!bound || bindToDirectHandler[eventType]) && eventTarget.hasOwnProperty(onVariant)) { - eventTarget[onVariant] = eventHandler + function getStyleReferencingAttributes(setProperties, styleName) { + var attributes = []; + if(setProperties) { + ["style:parent-style-name", "style:next-style-name"].forEach(function(attributeName) { + if(setProperties[attributeName] === styleName) { + attributes.push(attributeName) + } + }) } + return attributes } - function removeEvent(eventTarget, eventType, eventHandler) { - var onVariant = "on" + eventType; - if(eventTarget.detachEvent) { - eventTarget.detachEvent(onVariant, eventHandler) - } - if(eventTarget.removeEventListener) { - eventTarget.removeEventListener(eventType, eventHandler, false) - } - if(eventTarget[onVariant] === eventHandler) { - eventTarget[onVariant] = null + function dropStyleReferencingAttributes(setProperties, deletedStyleName) { + if(setProperties) { + ["style:parent-style-name", "style:next-style-name"].forEach(function(attributeName) { + if(setProperties[attributeName] === deletedStyleName) { + delete setProperties[attributeName] + } + }) } } - this.subscribe = function(eventName, handler) { - var delegate = window && bindToWindow[eventName]; - if(delegate) { - delegate.handlers.push(handler); - if(!delegate.isSubscribed) { - delegate.isSubscribed = true; - listenEvent((window), eventName, delegate.handleEvent); - listenEvent(canvasElement, eventName, delegate.handleEvent) + function cloneOpspec(opspec) { + var result = {}; + Object.keys(opspec).forEach(function(key) { + if(typeof opspec[key] === "object") { + result[key] = cloneOpspec(opspec[key]) + }else { + result[key] = opspec[key] } - }else { - listenEvent(canvasElement, eventName, handler) + }); + return result + } + function dropOverruledAndUnneededAttributes(minorSetProperties, minorRemovedProperties, majorSetProperties, majorRemovedProperties) { + var value, i, name, majorChanged = false, minorChanged = false, overrulingPropertyValue, removedPropertyNames, majorRemovedPropertyNames = majorRemovedProperties && majorRemovedProperties.attributes ? majorRemovedProperties.attributes.split(",") : []; + if(minorSetProperties && (majorSetProperties || majorRemovedPropertyNames.length > 0)) { + Object.keys(minorSetProperties).forEach(function(key) { + value = minorSetProperties[key]; + if(typeof value !== "object") { + overrulingPropertyValue = majorSetProperties && majorSetProperties[key]; + if(overrulingPropertyValue !== undefined) { + delete minorSetProperties[key]; + minorChanged = true; + if(overrulingPropertyValue === value) { + delete majorSetProperties[key]; + majorChanged = true + } + }else { + if(majorRemovedPropertyNames && majorRemovedPropertyNames.indexOf(key) !== -1) { + delete minorSetProperties[key]; + minorChanged = true + } + } + } + }) } - }; - this.unsubscribe = function(eventName, handler) { - var delegate = window && bindToWindow[eventName], handlerIndex = delegate && delegate.handlers.indexOf(handler); - if(delegate) { - if(handlerIndex !== -1) { - delegate.handlers.splice(handlerIndex, 1) + if(minorRemovedProperties && (minorRemovedProperties.attributes && (majorSetProperties || majorRemovedPropertyNames.length > 0))) { + removedPropertyNames = minorRemovedProperties.attributes.split(","); + for(i = 0;i < removedPropertyNames.length;i += 1) { + name = removedPropertyNames[i]; + if(majorSetProperties && majorSetProperties[name] !== undefined || majorRemovedPropertyNames && majorRemovedPropertyNames.indexOf(name) !== -1) { + removedPropertyNames.splice(i, 1); + i -= 1; + minorChanged = true + } + } + if(removedPropertyNames.length > 0) { + minorRemovedProperties.attributes = removedPropertyNames.join(",") + }else { + delete minorRemovedProperties.attributes } - }else { - removeEvent(canvasElement, eventName, handler) } - }; - function hasFocus() { - var activeElement = odtDocument.getDOM().activeElement; - return activeElement === canvasElement + return{majorChanged:majorChanged, minorChanged:minorChanged} } - this.hasFocus = hasFocus; - function findScrollableParent(element) { - while(element && (!element.scrollTop && !element.scrollLeft)) { - element = (element.parentNode) - } - if(element) { - return new ElementScrollState(element) - } - if(window) { - return new WindowScrollState(window) + function hasProperties(properties) { + var key; + for(key in properties) { + if(properties.hasOwnProperty(key)) { + return true + } } - return null + return false } - this.focus = function() { - var scrollParent; - if(!hasFocus()) { - scrollParent = findScrollableParent(canvasElement); - canvasElement.focus(); - if(scrollParent) { - scrollParent.restore() + function hasRemovedProperties(properties) { + var key; + for(key in properties) { + if(properties.hasOwnProperty(key)) { + if(key !== "attributes" || properties.attributes.length > 0) { + return true + } } } - }; - function init() { - bindToWindow = {"mousedown":new EventDelegate, "mouseup":new EventDelegate} + return false } - init() -}; -runtime.loadClass("core.DomUtils"); -runtime.loadClass("core.Async"); -runtime.loadClass("core.ScheduledTask"); -runtime.loadClass("odf.OdfUtils"); -runtime.loadClass("odf.ObjectNameGenerator"); -runtime.loadClass("ops.OdtCursor"); -runtime.loadClass("ops.OpAddCursor"); -runtime.loadClass("ops.OpRemoveCursor"); -runtime.loadClass("gui.Clipboard"); -runtime.loadClass("gui.DirectTextStyler"); -runtime.loadClass("gui.DirectParagraphStyler"); -runtime.loadClass("gui.KeyboardHandler"); -runtime.loadClass("gui.ImageManager"); -runtime.loadClass("gui.ImageSelector"); -runtime.loadClass("gui.TextManipulator"); -runtime.loadClass("gui.AnnotationController"); -runtime.loadClass("gui.EventManager"); -runtime.loadClass("gui.PlainTextPasteboard"); -gui.SessionController = function() { - var FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; - gui.SessionController = function SessionController(session, inputMemberId, shadowCursor, args) { - var window = (runtime.getWindow()), odtDocument = session.getOdtDocument(), async = new core.Async, domUtils = new core.DomUtils, odfUtils = new odf.OdfUtils, clipboard = new gui.Clipboard, keyDownHandler = new gui.KeyboardHandler, keyPressHandler = new gui.KeyboardHandler, keyboardMovementsFilter = new core.PositionFilterChain, baseFilter = odtDocument.getPositionFilter(), clickStartedWithinContainer = false, objectNameGenerator = new odf.ObjectNameGenerator(odtDocument.getOdfCanvas().odfContainer(), - inputMemberId), isMouseMoved = false, mouseDownRootFilter = null, undoManager = null, eventManager = new gui.EventManager(odtDocument), annotationController = new gui.AnnotationController(session, inputMemberId), directTextStyler = new gui.DirectTextStyler(session, inputMemberId), directParagraphStyler = args && args.directParagraphStylingEnabled ? new gui.DirectParagraphStyler(session, inputMemberId, objectNameGenerator) : null, createCursorStyleOp = (directTextStyler.createCursorStyleOp), textManipulator = - new gui.TextManipulator(session, inputMemberId, createCursorStyleOp), imageManager = new gui.ImageManager(session, inputMemberId, objectNameGenerator), imageSelector = new gui.ImageSelector(odtDocument.getOdfCanvas()), shadowCursorIterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), drawShadowCursorTask, suppressFocusEvent = false, pasteHandler = new gui.PlainTextPasteboard(odtDocument, inputMemberId); - 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("RootFilter", odtDocument.createRootFilter(inputMemberId)); - function getTarget(e) { - return e.target || e.srcElement + function dropOverruledAndUnneededProperties(minorOpspec, majorOpspec, propertiesName) { + var minorSP = minorOpspec.setProperties ? minorOpspec.setProperties[propertiesName] : null, minorRP = minorOpspec.removedProperties ? minorOpspec.removedProperties[propertiesName] : null, majorSP = majorOpspec.setProperties ? majorOpspec.setProperties[propertiesName] : null, majorRP = majorOpspec.removedProperties ? majorOpspec.removedProperties[propertiesName] : null, result; + result = dropOverruledAndUnneededAttributes(minorSP, minorRP, majorSP, majorRP); + if(minorSP && !hasProperties(minorSP)) { + delete minorOpspec.setProperties[propertiesName] } - function cancelEvent(event) { - if(event.preventDefault) { - event.preventDefault() - }else { - event.returnValue = false - } + if(minorRP && !hasRemovedProperties(minorRP)) { + delete minorOpspec.removedProperties[propertiesName] } - function dummyHandler(e) { - cancelEvent(e) + if(majorSP && !hasProperties(majorSP)) { + delete majorOpspec.setProperties[propertiesName] } - function createOpMoveCursor(position, length, selectionType) { - var op = new ops.OpMoveCursor; - op.init({memberid:inputMemberId, position:position, length:length || 0, selectionType:selectionType}); - return op + if(majorRP && !hasRemovedProperties(majorRP)) { + delete majorOpspec.removedProperties[propertiesName] } - function caretPositionFromPoint(x, y) { - var doc = odtDocument.getDOM(), result; - if(doc.caretRangeFromPoint) { - result = doc.caretRangeFromPoint(x, y); - return{container:result.startContainer, offset:result.startOffset} - } - if(doc.caretPositionFromPoint) { - result = doc.caretPositionFromPoint(x, y); - return{container:result.offsetNode, offset:result.offset} + return result + } + function transformAddStyleRemoveStyle(addStyleSpec, removeStyleSpec) { + var setAttributes, helperOpspec, addStyleSpecResult = [addStyleSpec], removeStyleSpecResult = [removeStyleSpec]; + if(addStyleSpec.styleFamily === removeStyleSpec.styleFamily) { + setAttributes = getStyleReferencingAttributes(addStyleSpec.setProperties, removeStyleSpec.styleName); + if(setAttributes.length > 0) { + helperOpspec = {optype:"UpdateParagraphStyle", memberid:removeStyleSpec.memberid, timestamp:removeStyleSpec.timestamp, styleName:addStyleSpec.styleName, removedProperties:{attributes:setAttributes.join(",")}}; + removeStyleSpecResult.unshift(helperOpspec) } - return null + dropStyleReferencingAttributes(addStyleSpec.setProperties, removeStyleSpec.styleName) } - function expandToWordBoundaries(selection) { - var alphaNumeric = /[A-Za-z0-9]/, iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), isForwardSelection = domUtils.comparePoints(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset) > 0, startPoint, endPoint, currentNode, c; - if(isForwardSelection) { - startPoint = {node:selection.anchorNode, offset:selection.anchorOffset}; - endPoint = {node:selection.focusNode, offset:selection.focusOffset} - }else { - startPoint = {node:selection.focusNode, offset:selection.focusOffset}; - endPoint = {node:selection.anchorNode, offset:selection.anchorOffset} + return{opSpecsA:addStyleSpecResult, opSpecsB:removeStyleSpecResult} + } + function transformApplyDirectStylingApplyDirectStyling(applyDirectStylingSpecA, applyDirectStylingSpecB, hasAPriority) { + var majorSpec, minorSpec, majorSpecResult, minorSpecResult, majorSpecEnd, minorSpecEnd, dropResult, originalMajorSpec, originalMinorSpec, helperOpspecBefore, helperOpspecAfter, applyDirectStylingSpecAResult = [applyDirectStylingSpecA], applyDirectStylingSpecBResult = [applyDirectStylingSpecB]; + if(!(applyDirectStylingSpecA.position + applyDirectStylingSpecA.length <= applyDirectStylingSpecB.position || applyDirectStylingSpecA.position >= applyDirectStylingSpecB.position + applyDirectStylingSpecB.length)) { + majorSpec = hasAPriority ? applyDirectStylingSpecA : applyDirectStylingSpecB; + minorSpec = hasAPriority ? applyDirectStylingSpecB : applyDirectStylingSpecA; + if(applyDirectStylingSpecA.position !== applyDirectStylingSpecB.position || applyDirectStylingSpecA.length !== applyDirectStylingSpecB.length) { + originalMajorSpec = cloneOpspec(majorSpec); + originalMinorSpec = cloneOpspec(minorSpec) } - iterator.setUnfilteredPosition(startPoint.node, startPoint.offset); - while(iterator.previousPosition()) { - currentNode = iterator.getCurrentNode(); - if(currentNode.nodeType === Node.TEXT_NODE) { - c = currentNode.data[iterator.unfilteredDomOffset()]; - if(!alphaNumeric.test(c)) { - break + dropResult = dropOverruledAndUnneededProperties(minorSpec, majorSpec, "style:text-properties"); + if(dropResult.majorChanged || dropResult.minorChanged) { + majorSpecResult = []; + minorSpecResult = []; + majorSpecEnd = majorSpec.position + majorSpec.length; + minorSpecEnd = minorSpec.position + minorSpec.length; + if(minorSpec.position < majorSpec.position) { + if(dropResult.minorChanged) { + helperOpspecBefore = cloneOpspec((originalMinorSpec)); + helperOpspecBefore.length = majorSpec.position - minorSpec.position; + minorSpecResult.push(helperOpspecBefore); + minorSpec.position = majorSpec.position; + minorSpec.length = minorSpecEnd - minorSpec.position } }else { - if(!odfUtils.isTextSpan(currentNode)) { - break + if(majorSpec.position < minorSpec.position) { + if(dropResult.majorChanged) { + helperOpspecBefore = cloneOpspec((originalMajorSpec)); + helperOpspecBefore.length = minorSpec.position - majorSpec.position; + majorSpecResult.push(helperOpspecBefore); + majorSpec.position = minorSpec.position; + majorSpec.length = majorSpecEnd - majorSpec.position + } } } - startPoint.node = iterator.container(); - startPoint.offset = iterator.unfilteredDomOffset() - } - iterator.setUnfilteredPosition(endPoint.node, endPoint.offset); - do { - currentNode = iterator.getCurrentNode(); - if(currentNode.nodeType === Node.TEXT_NODE) { - c = currentNode.data[iterator.unfilteredDomOffset()]; - if(!alphaNumeric.test(c)) { - break + if(minorSpecEnd > majorSpecEnd) { + if(dropResult.minorChanged) { + helperOpspecAfter = originalMinorSpec; + helperOpspecAfter.position = majorSpecEnd; + helperOpspecAfter.length = minorSpecEnd - majorSpecEnd; + minorSpecResult.push(helperOpspecAfter); + minorSpec.length = majorSpecEnd - minorSpec.position + } + }else { + if(majorSpecEnd > minorSpecEnd) { + if(dropResult.majorChanged) { + helperOpspecAfter = originalMajorSpec; + helperOpspecAfter.position = minorSpecEnd; + helperOpspecAfter.length = majorSpecEnd - minorSpecEnd; + majorSpecResult.push(helperOpspecAfter); + majorSpec.length = minorSpecEnd - majorSpec.position + } } + } + if(majorSpec.setProperties && hasProperties(majorSpec.setProperties)) { + majorSpecResult.push(majorSpec) + } + if(minorSpec.setProperties && hasProperties(minorSpec.setProperties)) { + minorSpecResult.push(minorSpec) + } + if(hasAPriority) { + applyDirectStylingSpecAResult = majorSpecResult; + applyDirectStylingSpecBResult = minorSpecResult }else { - if(!odfUtils.isTextSpan(currentNode)) { - break - } + applyDirectStylingSpecAResult = minorSpecResult; + applyDirectStylingSpecBResult = majorSpecResult } - }while(iterator.nextPosition()); - endPoint.node = iterator.container(); - endPoint.offset = iterator.unfilteredDomOffset(); - if(isForwardSelection) { - selection.anchorNode = startPoint.node; - selection.anchorOffset = startPoint.offset; - selection.focusNode = endPoint.node; - selection.focusOffset = endPoint.offset - }else { - selection.focusNode = startPoint.node; - selection.focusOffset = startPoint.offset; - selection.anchorNode = endPoint.node; - selection.anchorOffset = endPoint.offset - } - } - function expandToParagraphBoundaries(selection) { - var anchorParagraph = odtDocument.getParagraphElement(selection.anchorNode), focusParagraph = odtDocument.getParagraphElement(selection.focusNode); - if(anchorParagraph) { - selection.anchorNode = anchorParagraph; - selection.anchorOffset = 0 - } - if(focusParagraph) { - selection.focusNode = focusParagraph; - selection.focusOffset = focusParagraph.childNodes.length } } - function selectImage(frameNode) { - var stepsToAnchor = odtDocument.getDistanceFromCursor(inputMemberId, frameNode, 0), stepsToFocus = stepsToAnchor !== null ? stepsToAnchor + 1 : null, oldPosition, op; - if(stepsToFocus || stepsToAnchor) { - oldPosition = odtDocument.getCursorPosition(inputMemberId); - op = createOpMoveCursor(oldPosition + stepsToAnchor, stepsToFocus - stepsToAnchor, ops.OdtCursor.RegionSelection); - session.enqueue([op]) + return{opSpecsA:applyDirectStylingSpecAResult, opSpecsB:applyDirectStylingSpecBResult} + } + function transformApplyDirectStylingInsertText(applyDirectStylingSpec, insertTextSpec) { + if(insertTextSpec.position <= applyDirectStylingSpec.position) { + applyDirectStylingSpec.position += insertTextSpec.text.length + }else { + if(insertTextSpec.position <= applyDirectStylingSpec.position + applyDirectStylingSpec.length) { + applyDirectStylingSpec.length += insertTextSpec.text.length } - eventManager.focus() } - function selectRange(selection, capturedDetails) { - var canvasElement = odtDocument.getOdfCanvas().getElement(), validSelection, clickCount = capturedDetails.detail, caretPos, anchorNodeInsideCanvas, focusNodeInsideCanvas, existingSelection, newSelection, op; - if(!selection) { - return - } - if(!selection.anchorNode && !selection.focusNode) { - caretPos = caretPositionFromPoint(capturedDetails.clientX, capturedDetails.clientY); - if(!caretPos) { - return - } - selection.anchorNode = (caretPos.container); - selection.anchorOffset = caretPos.offset; - selection.focusNode = selection.anchorNode; - selection.focusOffset = selection.anchorOffset - } - runtime.assert(selection.anchorNode !== null && selection.focusNode !== null, "anchorNode or focusNode is null"); - validSelection = (selection); - anchorNodeInsideCanvas = domUtils.containsNode(canvasElement, validSelection.anchorNode); - focusNodeInsideCanvas = domUtils.containsNode(canvasElement, validSelection.focusNode); - if(!anchorNodeInsideCanvas && !focusNodeInsideCanvas) { - return - } - if(anchorNodeInsideCanvas && focusNodeInsideCanvas) { - if(clickCount === 2) { - expandToWordBoundaries(validSelection) + return{opSpecsA:[applyDirectStylingSpec], opSpecsB:[insertTextSpec]} + } + function transformApplyDirectStylingRemoveText(applyDirectStylingSpec, removeTextSpec) { + var applyDirectStylingSpecEnd = applyDirectStylingSpec.position + applyDirectStylingSpec.length, removeTextSpecEnd = removeTextSpec.position + removeTextSpec.length, applyDirectStylingSpecResult = [applyDirectStylingSpec], removeTextSpecResult = [removeTextSpec]; + if(removeTextSpecEnd <= applyDirectStylingSpec.position) { + applyDirectStylingSpec.position -= removeTextSpec.length + }else { + if(removeTextSpec.position < applyDirectStylingSpecEnd) { + if(applyDirectStylingSpec.position < removeTextSpec.position) { + if(removeTextSpecEnd < applyDirectStylingSpecEnd) { + applyDirectStylingSpec.length -= removeTextSpec.length + }else { + applyDirectStylingSpec.length = removeTextSpec.position - applyDirectStylingSpec.position + } }else { - if(clickCount >= 3) { - expandToParagraphBoundaries(validSelection) + applyDirectStylingSpec.position = removeTextSpec.position; + if(removeTextSpecEnd < applyDirectStylingSpecEnd) { + applyDirectStylingSpec.length = applyDirectStylingSpecEnd - removeTextSpecEnd + }else { + applyDirectStylingSpecResult = [] } } } - newSelection = odtDocument.convertDomToCursorRange(validSelection.anchorNode, validSelection.anchorOffset, validSelection.focusNode, validSelection.focusOffset); - existingSelection = odtDocument.getCursorSelection(inputMemberId); - if(newSelection.position !== existingSelection.position || newSelection.length !== existingSelection.length) { - op = createOpMoveCursor(newSelection.position, newSelection.length, ops.OdtCursor.RangeSelection); - session.enqueue([op]) - } - eventManager.focus() } - this.selectRange = selectRange; - function extendCursorByAdjustment(lengthAdjust) { - var selection = odtDocument.getCursorSelection(inputMemberId), stepCounter = odtDocument.getCursor(inputMemberId).getStepCounter(), newLength; - if(lengthAdjust !== 0) { - lengthAdjust = lengthAdjust > 0 ? stepCounter.convertForwardStepsBetweenFilters(lengthAdjust, keyboardMovementsFilter, baseFilter) : -stepCounter.convertBackwardStepsBetweenFilters(-lengthAdjust, keyboardMovementsFilter, baseFilter); - newLength = selection.length + lengthAdjust; - session.enqueue([createOpMoveCursor(selection.position, newLength)]) + return{opSpecsA:applyDirectStylingSpecResult, opSpecsB:removeTextSpecResult} + } + function transformApplyDirectStylingSplitParagraph(applyDirectStylingSpec, splitParagraphSpec) { + if(splitParagraphSpec.position < applyDirectStylingSpec.position) { + applyDirectStylingSpec.position += 1 + }else { + if(splitParagraphSpec.position < applyDirectStylingSpec.position + applyDirectStylingSpec.length) { + applyDirectStylingSpec.length += 1 } } - function moveCursorByAdjustment(positionAdjust) { - var position = odtDocument.getCursorPosition(inputMemberId), stepCounter = odtDocument.getCursor(inputMemberId).getStepCounter(); - if(positionAdjust !== 0) { - positionAdjust = positionAdjust > 0 ? stepCounter.convertForwardStepsBetweenFilters(positionAdjust, keyboardMovementsFilter, baseFilter) : -stepCounter.convertBackwardStepsBetweenFilters(-positionAdjust, keyboardMovementsFilter, baseFilter); - position = position + positionAdjust; - session.enqueue([createOpMoveCursor(position, 0)]) + return{opSpecsA:[applyDirectStylingSpec], opSpecsB:[splitParagraphSpec]} + } + function transformInsertTextInsertText(insertTextSpecA, insertTextSpecB, hasAPriority) { + if(insertTextSpecA.position < insertTextSpecB.position) { + insertTextSpecB.position += insertTextSpecA.text.length + }else { + if(insertTextSpecA.position > insertTextSpecB.position) { + insertTextSpecA.position += insertTextSpecB.text.length + }else { + if(hasAPriority) { + insertTextSpecB.position += insertTextSpecA.text.length + }else { + insertTextSpecA.position += insertTextSpecB.text.length + } + return null } } - function moveCursorToLeft() { - moveCursorByAdjustment(-1); - return true - } - this.moveCursorToLeft = moveCursorToLeft; - function moveCursorToRight() { - moveCursorByAdjustment(1); - return true - } - function extendSelectionToLeft() { - extendCursorByAdjustment(-1); - return true + return{opSpecsA:[insertTextSpecA], opSpecsB:[insertTextSpecB]} + } + function transformInsertTextMoveCursor(insertTextSpec, moveCursorSpec) { + var isMoveCursorSpecRangeInverted = invertMoveCursorSpecRangeOnNegativeLength(moveCursorSpec); + if(insertTextSpec.position < moveCursorSpec.position) { + moveCursorSpec.position += insertTextSpec.text.length + }else { + if(insertTextSpec.position < moveCursorSpec.position + moveCursorSpec.length) { + moveCursorSpec.length += insertTextSpec.text.length + } } - function extendSelectionToRight() { - extendCursorByAdjustment(1); - return true + if(isMoveCursorSpecRangeInverted) { + invertMoveCursorSpecRange(moveCursorSpec) } - function moveCursorByLine(direction, extend) { - var paragraphNode = odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode()), steps; - runtime.assert(Boolean(paragraphNode), "SessionController: Cursor outside paragraph"); - steps = odtDocument.getCursor(inputMemberId).getStepCounter().countLinesSteps(direction, keyboardMovementsFilter); - if(extend) { - extendCursorByAdjustment(steps) + return{opSpecsA:[insertTextSpec], opSpecsB:[moveCursorSpec]} + } + function transformInsertTextRemoveText(insertTextSpec, removeTextSpec) { + var helperOpspec, removeTextSpecEnd = removeTextSpec.position + removeTextSpec.length, insertTextSpecResult = [insertTextSpec], removeTextSpecResult = [removeTextSpec]; + if(removeTextSpecEnd <= insertTextSpec.position) { + insertTextSpec.position -= removeTextSpec.length + }else { + if(insertTextSpec.position <= removeTextSpec.position) { + removeTextSpec.position += insertTextSpec.text.length }else { - moveCursorByAdjustment(steps) + removeTextSpec.length = insertTextSpec.position - removeTextSpec.position; + helperOpspec = {optype:"RemoveText", memberid:removeTextSpec.memberid, timestamp:removeTextSpec.timestamp, position:insertTextSpec.position + insertTextSpec.text.length, length:removeTextSpecEnd - insertTextSpec.position}; + removeTextSpecResult.unshift(helperOpspec); + insertTextSpec.position = removeTextSpec.position } } - function moveCursorUp() { - moveCursorByLine(-1, false); - return true - } - function moveCursorDown() { - moveCursorByLine(1, false); - return true - } - function extendSelectionUp() { - moveCursorByLine(-1, true); - return true - } - function extendSelectionDown() { - moveCursorByLine(1, true); - return true - } - function moveCursorToLineBoundary(direction, extend) { - var steps = odtDocument.getCursor(inputMemberId).getStepCounter().countStepsToLineBoundary(direction, keyboardMovementsFilter); - if(extend) { - extendCursorByAdjustment(steps) + return{opSpecsA:insertTextSpecResult, opSpecsB:removeTextSpecResult} + } + function transformInsertTextSplitParagraph(insertTextSpec, splitParagraphSpec, hasAPriority) { + if(insertTextSpec.position < splitParagraphSpec.position) { + splitParagraphSpec.position += insertTextSpec.text.length + }else { + if(insertTextSpec.position > splitParagraphSpec.position) { + insertTextSpec.position += 1 }else { - moveCursorByAdjustment(steps) + if(hasAPriority) { + splitParagraphSpec.position += insertTextSpec.text.length + }else { + insertTextSpec.position += 1 + } + return null } } - function moveCursorToLineStart() { - moveCursorToLineBoundary(-1, false); - return true - } - function moveCursorToLineEnd() { - moveCursorToLineBoundary(1, false); - return true - } - function extendSelectionToLineStart() { - moveCursorToLineBoundary(-1, true); - return true - } - function extendSelectionToLineEnd() { - moveCursorToLineBoundary(1, true); - return true - } - function extendSelectionToParagraphStart() { - var paragraphNode = odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode()), iterator, node, steps; - runtime.assert(Boolean(paragraphNode), "SessionController: Cursor outside paragraph"); - steps = odtDocument.getDistanceFromCursor(inputMemberId, paragraphNode, 0); - iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()); - iterator.setUnfilteredPosition(paragraphNode, 0); - while(steps === 0 && iterator.previousPosition()) { - node = iterator.getCurrentNode(); - if(odfUtils.isParagraph(node)) { - steps = odtDocument.getDistanceFromCursor(inputMemberId, node, 0) + return{opSpecsA:[insertTextSpec], opSpecsB:[splitParagraphSpec]} + } + function transformUpdateParagraphStyleUpdateParagraphStyle(updateParagraphStyleSpecA, updateParagraphStyleSpecB, hasAPriority) { + var majorSpec, minorSpec, updateParagraphStyleSpecAResult = [updateParagraphStyleSpecA], updateParagraphStyleSpecBResult = [updateParagraphStyleSpecB]; + if(updateParagraphStyleSpecA.styleName === updateParagraphStyleSpecB.styleName) { + majorSpec = hasAPriority ? updateParagraphStyleSpecA : updateParagraphStyleSpecB; + minorSpec = hasAPriority ? updateParagraphStyleSpecB : updateParagraphStyleSpecA; + dropOverruledAndUnneededProperties(minorSpec, majorSpec, "style:paragraph-properties"); + dropOverruledAndUnneededProperties(minorSpec, majorSpec, "style:text-properties"); + dropOverruledAndUnneededAttributes(minorSpec.setProperties || null, minorSpec.removedProperties || null, majorSpec.setProperties || null, majorSpec.removedProperties || null); + if(!(majorSpec.setProperties && hasProperties(majorSpec.setProperties)) && !(majorSpec.removedProperties && hasRemovedProperties(majorSpec.removedProperties))) { + if(hasAPriority) { + updateParagraphStyleSpecAResult = [] + }else { + updateParagraphStyleSpecBResult = [] } } - extendCursorByAdjustment(steps); - return true - } - function extendSelectionToParagraphEnd() { - var paragraphNode = odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode()), iterator, node, steps; - runtime.assert(Boolean(paragraphNode), "SessionController: Cursor outside paragraph"); - iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()); - iterator.moveToEndOfNode(paragraphNode); - steps = odtDocument.getDistanceFromCursor(inputMemberId, iterator.container(), iterator.unfilteredDomOffset()); - while(steps === 0 && iterator.nextPosition()) { - node = iterator.getCurrentNode(); - if(odfUtils.isParagraph(node)) { - iterator.moveToEndOfNode(node); - steps = odtDocument.getDistanceFromCursor(inputMemberId, iterator.container(), iterator.unfilteredDomOffset()) + if(!(minorSpec.setProperties && hasProperties(minorSpec.setProperties)) && !(minorSpec.removedProperties && hasRemovedProperties(minorSpec.removedProperties))) { + if(hasAPriority) { + updateParagraphStyleSpecBResult = [] + }else { + updateParagraphStyleSpecAResult = [] } } - extendCursorByAdjustment(steps); - return true } - function moveCursorToDocumentBoundary(direction, extend) { - var iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), steps; - if(direction > 0) { - iterator.moveToEnd() + return{opSpecsA:updateParagraphStyleSpecAResult, opSpecsB:updateParagraphStyleSpecBResult} + } + function transformUpdateMetadataUpdateMetadata(updateMetadataSpecA, updateMetadataSpecB, hasAPriority) { + var majorSpec, minorSpec, updateMetadataSpecAResult = [updateMetadataSpecA], updateMetadataSpecBResult = [updateMetadataSpecB]; + majorSpec = hasAPriority ? updateMetadataSpecA : updateMetadataSpecB; + minorSpec = hasAPriority ? updateMetadataSpecB : updateMetadataSpecA; + dropOverruledAndUnneededAttributes(minorSpec.setProperties || null, minorSpec.removedProperties || null, majorSpec.setProperties || null, majorSpec.removedProperties || null); + if(!(majorSpec.setProperties && hasProperties(majorSpec.setProperties)) && !(majorSpec.removedProperties && hasRemovedProperties(majorSpec.removedProperties))) { + if(hasAPriority) { + updateMetadataSpecAResult = [] + }else { + updateMetadataSpecBResult = [] + } + } + if(!(minorSpec.setProperties && hasProperties(minorSpec.setProperties)) && !(minorSpec.removedProperties && hasRemovedProperties(minorSpec.removedProperties))) { + if(hasAPriority) { + updateMetadataSpecBResult = [] + }else { + updateMetadataSpecAResult = [] } - steps = odtDocument.getDistanceFromCursor(inputMemberId, iterator.container(), iterator.unfilteredDomOffset()); - if(extend) { - extendCursorByAdjustment(steps) + } + return{opSpecsA:updateMetadataSpecAResult, opSpecsB:updateMetadataSpecBResult} + } + function transformSplitParagraphSplitParagraph(splitParagraphSpecA, splitParagraphSpecB, hasAPriority) { + if(splitParagraphSpecA.position < splitParagraphSpecB.position) { + splitParagraphSpecB.position += 1 + }else { + if(splitParagraphSpecA.position > splitParagraphSpecB.position) { + splitParagraphSpecA.position += 1 }else { - moveCursorByAdjustment(steps) + if(splitParagraphSpecA.position === splitParagraphSpecB.position) { + if(hasAPriority) { + splitParagraphSpecB.position += 1 + }else { + splitParagraphSpecA.position += 1 + } + return null + } } } - this.moveCursorToDocumentBoundary = moveCursorToDocumentBoundary; - function moveCursorToDocumentStart() { - moveCursorToDocumentBoundary(-1, false); - return true + return{opSpecsA:[splitParagraphSpecA], opSpecsB:[splitParagraphSpecB]} + } + function transformMoveCursorRemoveCursor(moveCursorSpec, removeCursorSpec) { + var isSameCursorRemoved = moveCursorSpec.memberid === removeCursorSpec.memberid; + return{opSpecsA:isSameCursorRemoved ? [] : [moveCursorSpec], opSpecsB:[removeCursorSpec]} + } + function transformMoveCursorRemoveText(moveCursorSpec, removeTextSpec) { + var isMoveCursorSpecRangeInverted = invertMoveCursorSpecRangeOnNegativeLength(moveCursorSpec), moveCursorSpecEnd = moveCursorSpec.position + moveCursorSpec.length, removeTextSpecEnd = removeTextSpec.position + removeTextSpec.length; + if(removeTextSpecEnd <= moveCursorSpec.position) { + moveCursorSpec.position -= removeTextSpec.length + }else { + if(removeTextSpec.position < moveCursorSpecEnd) { + if(moveCursorSpec.position < removeTextSpec.position) { + if(removeTextSpecEnd < moveCursorSpecEnd) { + moveCursorSpec.length -= removeTextSpec.length + }else { + moveCursorSpec.length = removeTextSpec.position - moveCursorSpec.position + } + }else { + moveCursorSpec.position = removeTextSpec.position; + if(removeTextSpecEnd < moveCursorSpecEnd) { + moveCursorSpec.length = moveCursorSpecEnd - removeTextSpecEnd + }else { + moveCursorSpec.length = 0 + } + } + } } - function moveCursorToDocumentEnd() { - moveCursorToDocumentBoundary(1, false); - return true + if(isMoveCursorSpecRangeInverted) { + invertMoveCursorSpecRange(moveCursorSpec) } - function extendSelectionToDocumentStart() { - moveCursorToDocumentBoundary(-1, true); - return true + return{opSpecsA:[moveCursorSpec], opSpecsB:[removeTextSpec]} + } + function transformMoveCursorSplitParagraph(moveCursorSpec, splitParagraphSpec) { + var isMoveCursorSpecRangeInverted = invertMoveCursorSpecRangeOnNegativeLength(moveCursorSpec); + if(splitParagraphSpec.position < moveCursorSpec.position) { + moveCursorSpec.position += 1 + }else { + if(splitParagraphSpec.position < moveCursorSpec.position + moveCursorSpec.length) { + moveCursorSpec.length += 1 + } } - function extendSelectionToDocumentEnd() { - moveCursorToDocumentBoundary(1, true); - return true + if(isMoveCursorSpecRangeInverted) { + invertMoveCursorSpecRange(moveCursorSpec) } - function extendSelectionToEntireDocument() { - var rootNode = odtDocument.getRootNode(), lastWalkableStep = odtDocument.convertDomPointToCursorStep(rootNode, rootNode.childNodes.length); - session.enqueue([createOpMoveCursor(0, lastWalkableStep)]); - return true + return{opSpecsA:[moveCursorSpec], opSpecsB:[splitParagraphSpec]} + } + function transformRemoveCursorRemoveCursor(removeCursorSpecA, removeCursorSpecB) { + var isSameMemberid = removeCursorSpecA.memberid === removeCursorSpecB.memberid; + return{opSpecsA:isSameMemberid ? [] : [removeCursorSpecA], opSpecsB:isSameMemberid ? [] : [removeCursorSpecB]} + } + function transformRemoveStyleRemoveStyle(removeStyleSpecA, removeStyleSpecB) { + var isSameStyle = removeStyleSpecA.styleName === removeStyleSpecB.styleName && removeStyleSpecA.styleFamily === removeStyleSpecB.styleFamily; + return{opSpecsA:isSameStyle ? [] : [removeStyleSpecA], opSpecsB:isSameStyle ? [] : [removeStyleSpecB]} + } + function transformRemoveStyleSetParagraphStyle(removeStyleSpec, setParagraphStyleSpec) { + var helperOpspec, removeStyleSpecResult = [removeStyleSpec], setParagraphStyleSpecResult = [setParagraphStyleSpec]; + if(removeStyleSpec.styleFamily === "paragraph" && removeStyleSpec.styleName === setParagraphStyleSpec.styleName) { + helperOpspec = {optype:"SetParagraphStyle", memberid:removeStyleSpec.memberid, timestamp:removeStyleSpec.timestamp, position:setParagraphStyleSpec.position, styleName:""}; + removeStyleSpecResult.unshift(helperOpspec); + setParagraphStyleSpec.styleName = "" } - this.extendSelectionToEntireDocument = extendSelectionToEntireDocument; - function maintainCursorSelection() { - var cursor = odtDocument.getCursor(inputMemberId), selection = window.getSelection(), imageElement, range; - if(cursor) { - imageSelector.clearSelection(); - if(cursor.getSelectionType() === ops.OdtCursor.RegionSelection) { - imageElement = odfUtils.getImageElements(cursor.getSelectedRange())[0]; - if(imageElement) { - imageSelector.select((imageElement.parentNode)) - } - } - if(eventManager.hasFocus()) { - range = cursor.getSelectedRange(); - if(selection.extend) { - if(cursor.hasForwardSelection()) { - selection.collapse(range.startContainer, range.startOffset); - selection.extend(range.endContainer, range.endOffset) + return{opSpecsA:removeStyleSpecResult, opSpecsB:setParagraphStyleSpecResult} + } + function transformRemoveStyleUpdateParagraphStyle(removeStyleSpec, updateParagraphStyleSpec) { + var setAttributes, helperOpspec, removeStyleSpecResult = [removeStyleSpec], updateParagraphStyleSpecResult = [updateParagraphStyleSpec]; + if(removeStyleSpec.styleFamily === "paragraph") { + setAttributes = getStyleReferencingAttributes(updateParagraphStyleSpec.setProperties, removeStyleSpec.styleName); + if(setAttributes.length > 0) { + helperOpspec = {optype:"UpdateParagraphStyle", memberid:removeStyleSpec.memberid, timestamp:removeStyleSpec.timestamp, styleName:updateParagraphStyleSpec.styleName, removedProperties:{attributes:setAttributes.join(",")}}; + removeStyleSpecResult.unshift(helperOpspec) + } + if(removeStyleSpec.styleName === updateParagraphStyleSpec.styleName) { + updateParagraphStyleSpecResult = [] + }else { + dropStyleReferencingAttributes(updateParagraphStyleSpec.setProperties, removeStyleSpec.styleName) + } + } + return{opSpecsA:removeStyleSpecResult, opSpecsB:updateParagraphStyleSpecResult} + } + function transformRemoveTextRemoveText(removeTextSpecA, removeTextSpecB) { + var removeTextSpecAEnd = removeTextSpecA.position + removeTextSpecA.length, removeTextSpecBEnd = removeTextSpecB.position + removeTextSpecB.length, removeTextSpecAResult = [removeTextSpecA], removeTextSpecBResult = [removeTextSpecB]; + if(removeTextSpecBEnd <= removeTextSpecA.position) { + removeTextSpecA.position -= removeTextSpecB.length + }else { + if(removeTextSpecAEnd <= removeTextSpecB.position) { + removeTextSpecB.position -= removeTextSpecA.length + }else { + if(removeTextSpecB.position < removeTextSpecAEnd) { + if(removeTextSpecA.position < removeTextSpecB.position) { + if(removeTextSpecBEnd < removeTextSpecAEnd) { + removeTextSpecA.length = removeTextSpecA.length - removeTextSpecB.length }else { - selection.collapse(range.endContainer, range.endOffset); - selection.extend(range.startContainer, range.startOffset) + removeTextSpecA.length = removeTextSpecB.position - removeTextSpecA.position + } + if(removeTextSpecAEnd < removeTextSpecBEnd) { + removeTextSpecB.position = removeTextSpecA.position; + removeTextSpecB.length = removeTextSpecBEnd - removeTextSpecAEnd + }else { + removeTextSpecBResult = [] } }else { - suppressFocusEvent = true; - selection.removeAllRanges(); - selection.addRange(range.cloneRange()); - odtDocument.getOdfCanvas().getElement().setActive(); - runtime.setTimeout(function() { - suppressFocusEvent = false - }, 0) + if(removeTextSpecAEnd < removeTextSpecBEnd) { + removeTextSpecB.length = removeTextSpecB.length - removeTextSpecA.length + }else { + if(removeTextSpecB.position < removeTextSpecA.position) { + removeTextSpecB.length = removeTextSpecA.position - removeTextSpecB.position + }else { + removeTextSpecBResult = [] + } + } + if(removeTextSpecBEnd < removeTextSpecAEnd) { + removeTextSpecA.position = removeTextSpecB.position; + removeTextSpecA.length = removeTextSpecAEnd - removeTextSpecBEnd + }else { + removeTextSpecAResult = [] + } } } - }else { - imageSelector.clearSelection() } } - function delayedMaintainCursor() { - if(suppressFocusEvent === false) { - runtime.setTimeout(maintainCursorSelection, 0) - } + return{opSpecsA:removeTextSpecAResult, opSpecsB:removeTextSpecBResult} + } + function transformRemoveTextSplitParagraph(removeTextSpec, splitParagraphSpec) { + var removeTextSpecEnd = removeTextSpec.position + removeTextSpec.length, helperOpspec, removeTextSpecResult = [removeTextSpec], splitParagraphSpecResult = [splitParagraphSpec]; + if(splitParagraphSpec.position <= removeTextSpec.position) { + removeTextSpec.position += 1 + }else { + if(splitParagraphSpec.position < removeTextSpecEnd) { + removeTextSpec.length = splitParagraphSpec.position - removeTextSpec.position; + helperOpspec = {optype:"RemoveText", memberid:removeTextSpec.memberid, timestamp:removeTextSpec.timestamp, position:splitParagraphSpec.position + 1, length:removeTextSpecEnd - splitParagraphSpec.position}; + removeTextSpecResult.unshift(helperOpspec) + } + } + if(removeTextSpec.position + removeTextSpec.length <= splitParagraphSpec.position) { + splitParagraphSpec.position -= removeTextSpec.length + }else { + if(removeTextSpec.position < splitParagraphSpec.position) { + splitParagraphSpec.position = removeTextSpec.position + } + } + return{opSpecsA:removeTextSpecResult, opSpecsB:splitParagraphSpecResult} + } + function passUnchanged(opSpecA, opSpecB) { + return{opSpecsA:[opSpecA], opSpecsB:[opSpecB]} + } + var transformations = {"AddCursor":{"AddCursor":passUnchanged, "AddMember":passUnchanged, "AddStyle":passUnchanged, "ApplyDirectStyling":passUnchanged, "InsertText":passUnchanged, "MoveCursor":passUnchanged, "RemoveCursor":passUnchanged, "RemoveMember":passUnchanged, "RemoveStyle":passUnchanged, "RemoveText":passUnchanged, "SetParagraphStyle":passUnchanged, "SplitParagraph":passUnchanged, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "AddMember":{"AddStyle":passUnchanged, + "InsertText":passUnchanged, "MoveCursor":passUnchanged, "RemoveCursor":passUnchanged, "RemoveStyle":passUnchanged, "RemoveText":passUnchanged, "SetParagraphStyle":passUnchanged, "SplitParagraph":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "AddStyle":{"AddStyle":passUnchanged, "ApplyDirectStyling":passUnchanged, "InsertText":passUnchanged, "MoveCursor":passUnchanged, "RemoveCursor":passUnchanged, "RemoveMember":passUnchanged, "RemoveStyle":transformAddStyleRemoveStyle, + "RemoveText":passUnchanged, "SetParagraphStyle":passUnchanged, "SplitParagraph":passUnchanged, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "ApplyDirectStyling":{"ApplyDirectStyling":transformApplyDirectStylingApplyDirectStyling, "InsertText":transformApplyDirectStylingInsertText, "MoveCursor":passUnchanged, "RemoveCursor":passUnchanged, "RemoveStyle":passUnchanged, "RemoveText":transformApplyDirectStylingRemoveText, "SetParagraphStyle":passUnchanged, + "SplitParagraph":transformApplyDirectStylingSplitParagraph, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "InsertText":{"InsertText":transformInsertTextInsertText, "MoveCursor":transformInsertTextMoveCursor, "RemoveCursor":passUnchanged, "RemoveMember":passUnchanged, "RemoveStyle":passUnchanged, "RemoveText":transformInsertTextRemoveText, "SplitParagraph":transformInsertTextSplitParagraph, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, + "MoveCursor":{"MoveCursor":passUnchanged, "RemoveCursor":transformMoveCursorRemoveCursor, "RemoveMember":passUnchanged, "RemoveStyle":passUnchanged, "RemoveText":transformMoveCursorRemoveText, "SetParagraphStyle":passUnchanged, "SplitParagraph":transformMoveCursorSplitParagraph, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "RemoveCursor":{"RemoveCursor":transformRemoveCursorRemoveCursor, "RemoveMember":passUnchanged, "RemoveStyle":passUnchanged, + "RemoveText":passUnchanged, "SetParagraphStyle":passUnchanged, "SplitParagraph":passUnchanged, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "RemoveMember":{"RemoveStyle":passUnchanged, "RemoveText":passUnchanged, "SetParagraphStyle":passUnchanged, "SplitParagraph":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "RemoveStyle":{"RemoveStyle":transformRemoveStyleRemoveStyle, "RemoveText":passUnchanged, "SetParagraphStyle":transformRemoveStyleSetParagraphStyle, + "SplitParagraph":passUnchanged, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":transformRemoveStyleUpdateParagraphStyle}, "RemoveText":{"RemoveText":transformRemoveTextRemoveText, "SplitParagraph":transformRemoveTextSplitParagraph, "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "SetParagraphStyle":{"UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "SplitParagraph":{"SplitParagraph":transformSplitParagraphSplitParagraph, + "UpdateMember":passUnchanged, "UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "UpdateMember":{"UpdateMetadata":passUnchanged, "UpdateParagraphStyle":passUnchanged}, "UpdateMetadata":{"UpdateMetadata":transformUpdateMetadataUpdateMetadata, "UpdateParagraphStyle":passUnchanged}, "UpdateParagraphStyle":{"UpdateParagraphStyle":transformUpdateParagraphStyleUpdateParagraphStyle}}; + this.passUnchanged = passUnchanged; + this.extendTransformations = function(moreTransformations) { + Object.keys(moreTransformations).forEach(function(optypeA) { + var moreTransformationsOptypeAMap = moreTransformations[optypeA], optypeAMap, isExtendingOptypeAMap = transformations.hasOwnProperty(optypeA); + runtime.log((isExtendingOptypeAMap ? "Extending" : "Adding") + " map for optypeA: " + optypeA); + if(!isExtendingOptypeAMap) { + transformations[optypeA] = {} + } + optypeAMap = transformations[optypeA]; + Object.keys(moreTransformationsOptypeAMap).forEach(function(optypeB) { + var isOverwritingOptypeBEntry = optypeAMap.hasOwnProperty(optypeB); + runtime.assert(optypeA <= optypeB, "Wrong order:" + optypeA + ", " + optypeB); + runtime.log(" " + (isOverwritingOptypeBEntry ? "Overwriting" : "Adding") + " entry for optypeB: " + optypeB); + optypeAMap[optypeB] = moreTransformationsOptypeAMap[optypeB] + }) + }) + }; + this.transformOpspecVsOpspec = function(opSpecA, opSpecB) { + var isOptypeAAlphaNumericSmaller = opSpecA.optype <= opSpecB.optype, helper, transformationFunctionMap, transformationFunction, result; + runtime.log("Crosstransforming:"); + runtime.log(runtime.toJson(opSpecA)); + runtime.log(runtime.toJson(opSpecB)); + if(!isOptypeAAlphaNumericSmaller) { + helper = opSpecA; + opSpecA = opSpecB; + opSpecB = helper + } + transformationFunctionMap = transformations[opSpecA.optype]; + transformationFunction = transformationFunctionMap && transformationFunctionMap[opSpecB.optype]; + if(transformationFunction) { + result = transformationFunction(opSpecA, opSpecB, !isOptypeAAlphaNumericSmaller); + if(!isOptypeAAlphaNumericSmaller && result !== null) { + result = {opSpecsA:result.opSpecsB, opSpecsB:result.opSpecsA} + } + }else { + result = null + } + runtime.log("result:"); + if(result) { + runtime.log(runtime.toJson(result.opSpecsA)); + runtime.log(runtime.toJson(result.opSpecsB)) + }else { + runtime.log("null") } - function stringFromKeyPress(event) { - if(event.which === null || event.which === undefined) { - return String.fromCharCode(event.keyCode) - } - if(event.which !== 0 && event.charCode !== 0) { - return String.fromCharCode(event.which) + return result + } +}; +/* + + Copyright (C) 2013 KO GmbH + + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("ops.OperationFactory"); +runtime.loadClass("ops.OperationTransformMatrix"); +ops.OperationTransformer = function OperationTransformer() { + var operationFactory, operationTransformMatrix = new ops.OperationTransformMatrix; + function operations(opspecs) { + var ops = []; + opspecs.forEach(function(opspec) { + ops.push(operationFactory.create(opspec)) + }); + return ops + } + function transformOpVsOp(opSpecA, opSpecB) { + return operationTransformMatrix.transformOpspecVsOpspec(opSpecA, opSpecB) + } + function transformOpListVsOp(opSpecsA, opSpecB) { + var transformResult, transformListResult, transformedOpspecsA = [], transformedOpspecsB = []; + while(opSpecsA.length > 0 && opSpecB) { + transformResult = transformOpVsOp(opSpecsA.shift(), (opSpecB)); + if(!transformResult) { + return null } - return null - } - function handleCut(e) { - var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); - if(selectedRange.collapsed) { - return + transformedOpspecsA = transformedOpspecsA.concat(transformResult.opSpecsA); + if(transformResult.opSpecsB.length === 0) { + transformedOpspecsA = transformedOpspecsA.concat(opSpecsA); + opSpecB = null; + break } - if(clipboard.setDataFromRange(e, cursor.getSelectedRange())) { - textManipulator.removeCurrentSelection() - }else { - runtime.log("Cut operation failed") + while(transformResult.opSpecsB.length > 1) { + transformListResult = transformOpListVsOp(opSpecsA, transformResult.opSpecsB.shift()); + if(!transformListResult) { + return null + } + transformedOpspecsB = transformedOpspecsB.concat(transformListResult.opSpecsB); + opSpecsA = transformListResult.opSpecsA } + opSpecB = transformResult.opSpecsB.pop() } - function handleBeforeCut() { - var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); - return selectedRange.collapsed !== false + if(opSpecB) { + transformedOpspecsB.push(opSpecB) } - function handleCopy(e) { - var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); - if(selectedRange.collapsed) { - return - } - if(!clipboard.setDataFromRange(e, cursor.getSelectedRange())) { - runtime.log("Cut operation failed") + return{opSpecsA:transformedOpspecsA, opSpecsB:transformedOpspecsB} + } + this.setOperationFactory = function(f) { + operationFactory = f + }; + this.getOperationTransformMatrix = function() { + return operationTransformMatrix + }; + this.transform = function(opSpecsA, opSpecsB) { + var transformResult, transformedOpspecsB = []; + while(opSpecsB.length > 0) { + transformResult = transformOpListVsOp(opSpecsA, opSpecsB.shift()); + if(!transformResult) { + return null } + opSpecsA = transformResult.opSpecsA; + transformedOpspecsB = transformedOpspecsB.concat(transformResult.opSpecsB) } - function handlePaste(e) { - var plainText; - if(window.clipboardData && window.clipboardData.getData) { - plainText = window.clipboardData.getData("Text") + return{opsA:operations(opSpecsA), opsB:operations(transformedOpspecsB)} + } +}; +/* + + Copyright (C) 2012 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +ops.TrivialOperationRouter = function TrivialOperationRouter() { + var operationFactory, playbackFunction; + this.setOperationFactory = function(f) { + operationFactory = f + }; + this.setPlaybackFunction = function(playback_func) { + playbackFunction = playback_func + }; + this.push = function(operations) { + operations.forEach(function(op) { + var timedOp, opspec = op.spec(); + opspec.timestamp = (new Date).getTime(); + timedOp = operationFactory.create(opspec); + playbackFunction(timedOp) + }) + }; + this.close = function(cb) { + cb() + }; + this.subscribe = function(eventId, cb) { + }; + this.unsubscribe = function(eventId, cb) { + }; + this.hasLocalUnsyncedOps = function() { + return false + }; + this.hasSessionHostConnection = function() { + return true + } +}; +/* + + Copyright (C) 2012-2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("ops.EditInfo"); +runtime.loadClass("gui.EditInfoHandle"); +gui.EditInfoMarker = function EditInfoMarker(editInfo, initialVisibility) { + var self = this, editInfoNode, handle, marker, editinfons = "urn:webodf:names:editinfo", decay1, decay2, decayTimeStep = 1E4; + function applyDecay(opacity, delay) { + return runtime.setTimeout(function() { + marker.style.opacity = opacity + }, delay) + } + function deleteDecay(timer) { + runtime.clearTimeout(timer) + } + function setLastAuthor(memberid) { + marker.setAttributeNS(editinfons, "editinfo:memberid", memberid) + } + this.addEdit = function(memberid, timestamp) { + var age = Date.now() - timestamp; + editInfo.addEdit(memberid, timestamp); + handle.setEdits(editInfo.getSortedEdits()); + setLastAuthor(memberid); + if(decay1) { + deleteDecay(decay1) + } + if(decay2) { + deleteDecay(decay2) + } + if(age < decayTimeStep) { + applyDecay(1, 0); + decay1 = applyDecay(0.5, decayTimeStep - age); + decay2 = applyDecay(0.2, decayTimeStep * 2 - age) + }else { + if(age >= decayTimeStep && age < decayTimeStep * 2) { + applyDecay(0.5, 0); + decay2 = applyDecay(0.2, decayTimeStep * 2 - age) }else { - if(e.clipboardData && e.clipboardData.getData) { - plainText = e.clipboardData.getData("text/plain") - } - } - if(plainText) { - textManipulator.removeCurrentSelection(); - session.enqueue(pasteHandler.createPasteOps(plainText)); - cancelEvent(e) + applyDecay(0.2, 0) } } - function handleBeforePaste() { - return false + }; + this.getEdits = function() { + return editInfo.getEdits() + }; + this.clearEdits = function() { + editInfo.clearEdits(); + handle.setEdits([]); + if(marker.hasAttributeNS(editinfons, "editinfo:memberid")) { + marker.removeAttributeNS(editinfons, "editinfo:memberid") } - function updateUndoStack(op) { - if(undoManager) { - undoManager.onOperationExecuted(op) + }; + this.getEditInfo = function() { + return editInfo + }; + this.show = function() { + marker.style.display = "block" + }; + this.hide = function() { + self.hideHandle(); + marker.style.display = "none" + }; + this.showHandle = function() { + handle.show() + }; + this.hideHandle = function() { + handle.hide() + }; + this.destroy = function(callback) { + editInfoNode.removeChild(marker); + handle.destroy(function(err) { + if(err) { + callback(err) + }else { + editInfo.destroy(callback) } + }) + }; + function init() { + var dom = editInfo.getOdtDocument().getDOM(), htmlns = dom.documentElement.namespaceURI; + marker = dom.createElementNS(htmlns, "div"); + marker.setAttribute("class", "editInfoMarker"); + marker.onmouseover = function() { + self.showHandle() + }; + marker.onmouseout = function() { + self.hideHandle() + }; + editInfoNode = editInfo.getNode(); + editInfoNode.appendChild(marker); + handle = new gui.EditInfoHandle(editInfoNode); + if(!initialVisibility) { + self.hide() } - function forwardUndoStackChange(e) { - odtDocument.emit(ops.OdtDocument.signalUndoStackChanged, e) + } + init() +}; +/* + + Copyright (C) 2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +gui.PlainTextPasteboard = function PlainTextPasteboard(odtDocument, inputMemberId) { + function createOp(op, data) { + op.init(data); + return op + } + this.createPasteOps = function(data) { + var originalCursorPosition = odtDocument.getCursorPosition(inputMemberId), cursorPosition = originalCursorPosition, operations = [], paragraphs; + paragraphs = data.replace(/\r/g, "").split("\n"); + paragraphs.forEach(function(text) { + operations.push(createOp(new ops.OpSplitParagraph, {memberid:inputMemberId, position:cursorPosition})); + cursorPosition += 1; + operations.push(createOp(new ops.OpInsertText, {memberid:inputMemberId, position:cursorPosition, text:text})); + cursorPosition += text.length + }); + operations.push(createOp(new ops.OpRemoveText, {memberid:inputMemberId, position:originalCursorPosition, length:1})); + return operations + } +}; +runtime.loadClass("core.DomUtils"); +runtime.loadClass("odf.OdfUtils"); +runtime.loadClass("odf.OdfNodeFilter"); +runtime.loadClass("gui.SelectionMover"); +gui.SelectionView = function SelectionView(cursor) { + var odtDocument = cursor.getOdtDocument(), documentRoot, root, doc = odtDocument.getDOM(), overlayTop = doc.createElement("div"), overlayMiddle = doc.createElement("div"), overlayBottom = doc.createElement("div"), odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils, isVisible = true, positionIterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), FILTER_ACCEPT = NodeFilter.FILTER_ACCEPT, FILTER_REJECT = NodeFilter.FILTER_REJECT; + function addOverlays() { + var newDocumentRoot = odtDocument.getRootNode(); + if(documentRoot !== newDocumentRoot) { + documentRoot = newDocumentRoot; + root = (documentRoot.parentNode.parentNode.parentNode); + root.appendChild(overlayTop); + root.appendChild(overlayMiddle); + root.appendChild(overlayBottom) } - function undo() { - if(undoManager) { - undoManager.moveBackward(1); - maintainCursorSelection(); - return true + } + function setRect(div, rect) { + div.style.left = rect.left + "px"; + div.style.top = rect.top + "px"; + div.style.width = rect.width + "px"; + div.style.height = rect.height + "px" + } + function showOverlays(choice) { + var display; + isVisible = choice; + display = choice === true ? "block" : "none"; + overlayTop.style.display = overlayMiddle.style.display = overlayBottom.style.display = display + } + function translateRect(rect) { + var rootRect = domUtils.getBoundingClientRect(root), zoomLevel = odtDocument.getOdfCanvas().getZoomLevel(), resultRect = {}; + resultRect.top = domUtils.adaptRangeDifferenceToZoomLevel(rect.top - rootRect.top, zoomLevel); + resultRect.left = domUtils.adaptRangeDifferenceToZoomLevel(rect.left - rootRect.left, zoomLevel); + resultRect.bottom = domUtils.adaptRangeDifferenceToZoomLevel(rect.bottom - rootRect.top, zoomLevel); + resultRect.right = domUtils.adaptRangeDifferenceToZoomLevel(rect.right - rootRect.left, zoomLevel); + resultRect.width = domUtils.adaptRangeDifferenceToZoomLevel(rect.width, zoomLevel); + resultRect.height = domUtils.adaptRangeDifferenceToZoomLevel(rect.height, zoomLevel); + return resultRect + } + function isRangeVisible(range) { + var bcr = range.getBoundingClientRect(); + return Boolean(bcr && bcr.height !== 0) + } + function lastVisibleRect(range, nodes) { + var nextNodeIndex = nodes.length - 1, node = nodes[nextNodeIndex], startOffset = range.endContainer === node ? range.endOffset : node.length || node.childNodes.length, endOffset = startOffset; + range.setStart(node, startOffset); + range.setEnd(node, endOffset); + while(!isRangeVisible(range)) { + if(node.nodeType === Node.ELEMENT_NODE && startOffset > 0) { + startOffset = 0 + }else { + if(node.nodeType === Node.TEXT_NODE && startOffset > 0) { + startOffset -= 1 + }else { + if(nodes[nextNodeIndex]) { + node = nodes[nextNodeIndex]; + nextNodeIndex -= 1; + startOffset = endOffset = node.length || node.childNodes.length + }else { + return false + } + } } - return false + range.setStart(node, startOffset); + range.setEnd(node, endOffset) } - function redo() { - if(undoManager) { - undoManager.moveForward(1); - maintainCursorSelection(); - return true + return true + } + function firstVisibleRect(range, nodes) { + var nextNodeIndex = 0, node = nodes[nextNodeIndex], startOffset = range.startContainer === node ? range.startOffset : 0, endOffset = startOffset; + range.setStart(node, startOffset); + range.setEnd(node, endOffset); + while(!isRangeVisible(range)) { + if(node.nodeType === Node.ELEMENT_NODE && endOffset < node.childNodes.length) { + endOffset = node.childNodes.length + }else { + if(node.nodeType === Node.TEXT_NODE && endOffset < node.length) { + endOffset += 1 + }else { + if(nodes[nextNodeIndex]) { + node = nodes[nextNodeIndex]; + nextNodeIndex += 1; + startOffset = endOffset = 0 + }else { + return false + } + } } - return false + range.setStart(node, startOffset); + range.setEnd(node, endOffset) } - function filterMouseClicks(e) { - var target = getTarget(e); - clickStartedWithinContainer = target && domUtils.containsNode(odtDocument.getOdfCanvas().getElement(), target); - if(clickStartedWithinContainer) { - isMouseMoved = false; - mouseDownRootFilter = odtDocument.createRootFilter(target) - } + return true + } + function getExtremeRanges(range) { + var nodes = odfUtils.getTextElements(range, true, false), firstRange = (range.cloneRange()), lastRange = (range.cloneRange()), fillerRange = range.cloneRange(); + if(!nodes.length) { + return null } - function cursorToSelection(cursor) { - var range = cursor.getSelectedRange(); - if(cursor.hasForwardSelection()) { - return{anchorNode:range.startContainer, anchorOffset:range.startOffset, focusNode:range.endContainer, focusOffset:range.endOffset} - } - return{anchorNode:range.endContainer, anchorOffset:range.endOffset, focusNode:range.startContainer, focusOffset:range.startOffset} + if(!firstVisibleRect(firstRange, nodes)) { + return null } - function mutableSelection(selection) { - if(selection) { - return{anchorNode:selection.anchorNode, anchorOffset:selection.anchorOffset, focusNode:selection.focusNode, focusOffset:selection.focusOffset} - } + if(!lastVisibleRect(lastRange, nodes)) { return null } - function handleMouseClickEvent(event) { - var target = getTarget(event), eventDetails = {detail:event.detail, clientX:event.clientX, clientY:event.clientY, target:target}; - drawShadowCursorTask.processRequests(); - if(odfUtils.isImage(target) && odfUtils.isCharacterFrame(target.parentNode)) { - selectImage(target.parentNode) + fillerRange.setStart(firstRange.startContainer, firstRange.startOffset); + fillerRange.setEnd(lastRange.endContainer, lastRange.endOffset); + return{firstRange:firstRange, lastRange:lastRange, fillerRange:fillerRange} + } + function getBoundingRect(rect1, rect2) { + var resultRect = {}; + resultRect.top = Math.min(rect1.top, rect2.top); + resultRect.left = Math.min(rect1.left, rect2.left); + resultRect.right = Math.max(rect1.right, rect2.right); + resultRect.bottom = Math.max(rect1.bottom, rect2.bottom); + resultRect.width = resultRect.right - resultRect.left; + resultRect.height = resultRect.bottom - resultRect.top; + return resultRect + } + function checkAndGrowOrCreateRect(originalRect, newRect) { + if(newRect && (newRect.width > 0 && newRect.height > 0)) { + if(!originalRect) { + originalRect = newRect }else { - if(clickStartedWithinContainer && !imageSelector.isSelectorElement(target)) { - if(isMouseMoved) { - selectRange(cursorToSelection(shadowCursor), event) - }else { - runtime.setTimeout(function() { - selectRange(mutableSelection(window.getSelection()), eventDetails) - }, 0) - } - } + originalRect = getBoundingRect(originalRect, newRect) } - clickStartedWithinContainer = false; - isMouseMoved = false - } - function handleContextMenu(e) { - handleMouseClickEvent(e) } - function handleMouseUp(event) { - var target = getTarget(event), annotationNode = null; - if(target.className === "annotationRemoveButton") { - annotationNode = domUtils.getElementsByTagNameNS(target.parentNode, odf.Namespaces.officens, "annotation")[0]; - annotationController.removeAnnotation(annotationNode) - }else { - handleMouseClickEvent(event) + return originalRect + } + function getFillerRect(fillerRange) { + var containerNode = fillerRange.commonAncestorContainer, firstNode = (fillerRange.startContainer), lastNode = (fillerRange.endContainer), firstOffset = fillerRange.startOffset, lastOffset = fillerRange.endOffset, currentNode, lastMeasuredNode, firstSibling, lastSibling, grownRect = null, currentRect, range = doc.createRange(), rootFilter, odfNodeFilter = new odf.OdfNodeFilter, treeWalker; + function acceptNode(node) { + positionIterator.setUnfilteredPosition(node, 0); + if(odfNodeFilter.acceptNode(node) === FILTER_ACCEPT && rootFilter.acceptPosition(positionIterator) === FILTER_ACCEPT) { + return FILTER_ACCEPT } + return FILTER_REJECT } - function updateShadowCursor() { - var selection = window.getSelection(), selectionRange, isForwardSelection; - if(clickStartedWithinContainer && selection.rangeCount > 0) { - isMouseMoved = true; - imageSelector.clearSelection(); - shadowCursorIterator.setUnfilteredPosition((selection.focusNode), selection.focusOffset); - if(mouseDownRootFilter.acceptPosition(shadowCursorIterator) === FILTER_ACCEPT) { - selectionRange = selection.getRangeAt(0).cloneRange(); - isForwardSelection = selection.anchorNode === selectionRange.startContainer && selection.anchorOffset === selectionRange.startOffset; - shadowCursor.setSelectedRange(selectionRange, isForwardSelection); - odtDocument.emit(ops.OdtDocument.signalCursorMoved, shadowCursor) - } + function getRectFromNodeAfterFiltering(node) { + var rect = null; + if(acceptNode(node) === FILTER_ACCEPT) { + rect = domUtils.getBoundingClientRect(node) } + return rect } - this.startEditing = function() { - var op; - odtDocument.getOdfCanvas().getElement().classList.add("virtualSelections"); - eventManager.subscribe("keydown", keyDownHandler.handleEvent); - eventManager.subscribe("keypress", keyPressHandler.handleEvent); - eventManager.subscribe("keyup", dummyHandler); - eventManager.subscribe("beforecut", handleBeforeCut); - eventManager.subscribe("cut", handleCut); - eventManager.subscribe("copy", handleCopy); - eventManager.subscribe("beforepaste", handleBeforePaste); - eventManager.subscribe("paste", handlePaste); - eventManager.subscribe("mousedown", filterMouseClicks); - eventManager.subscribe("mousemove", drawShadowCursorTask.trigger); - eventManager.subscribe("mouseup", handleMouseUp); - eventManager.subscribe("contextmenu", handleContextMenu); - eventManager.subscribe("focus", delayedMaintainCursor); - odtDocument.subscribe(ops.OdtDocument.signalOperationExecuted, maintainCursorSelection); - odtDocument.subscribe(ops.OdtDocument.signalOperationExecuted, updateUndoStack); - op = new ops.OpAddCursor; - op.init({memberid:inputMemberId}); - session.enqueue([op]); - if(undoManager) { - undoManager.saveInitialState() - } - }; - this.endEditing = function() { - var op; - op = new ops.OpRemoveCursor; - op.init({memberid:inputMemberId}); - session.enqueue([op]); - if(undoManager) { - undoManager.resetInitialState() - } - odtDocument.unsubscribe(ops.OdtDocument.signalOperationExecuted, updateUndoStack); - odtDocument.unsubscribe(ops.OdtDocument.signalOperationExecuted, maintainCursorSelection); - eventManager.unsubscribe("keydown", keyDownHandler.handleEvent); - eventManager.unsubscribe("keypress", keyPressHandler.handleEvent); - eventManager.unsubscribe("keyup", dummyHandler); - eventManager.unsubscribe("cut", handleCut); - eventManager.unsubscribe("beforecut", handleBeforeCut); - eventManager.unsubscribe("copy", handleCopy); - eventManager.unsubscribe("paste", handlePaste); - eventManager.unsubscribe("beforepaste", handleBeforePaste); - eventManager.unsubscribe("mousemove", drawShadowCursorTask.trigger); - eventManager.unsubscribe("mousedown", filterMouseClicks); - eventManager.unsubscribe("mouseup", handleMouseUp); - eventManager.unsubscribe("contextmenu", handleContextMenu); - eventManager.unsubscribe("focus", delayedMaintainCursor); - odtDocument.getOdfCanvas().getElement().classList.remove("virtualSelections") - }; - this.getInputMemberId = function() { - return inputMemberId - }; - this.getSession = function() { - return session - }; - this.setUndoManager = function(manager) { - if(undoManager) { - undoManager.unsubscribe(gui.UndoManager.signalUndoStackChanged, forwardUndoStackChange) - } - undoManager = manager; - if(undoManager) { - undoManager.setOdtDocument(odtDocument); - undoManager.setPlaybackFunction(function(op) { - op.execute(odtDocument) - }); - undoManager.subscribe(gui.UndoManager.signalUndoStackChanged, forwardUndoStackChange) - } - }; - this.getUndoManager = function() { - return undoManager - }; - this.getAnnotationController = function() { - return annotationController - }; - this.getDirectTextStyler = function() { - return directTextStyler - }; - this.getDirectParagraphStyler = function() { - return directParagraphStyler - }; - this.getImageManager = function() { - return imageManager - }; - this.getTextManipulator = function() { - return textManipulator - }; - this.getEventManager = function() { - return eventManager - }; - this.getKeyboardHandlers = function() { - return{keydown:keyDownHandler, keypress:keyPressHandler} - }; - this.destroy = function(callback) { - var destroyCallbacks = [drawShadowCursorTask.destroy, directTextStyler.destroy]; - if(directParagraphStyler) { - destroyCallbacks.push(directParagraphStyler.destroy) - } - async.destroyAll(destroyCallbacks, callback) - }; - function returnTrue(fn) { - return function() { - fn(); - return true - } + if(firstNode === containerNode || lastNode === containerNode) { + range = fillerRange.cloneRange(); + grownRect = range.getBoundingClientRect(); + range.detach(); + return grownRect } - function rangeSelectionOnly(fn) { - return function(e) { - var selectionType = odtDocument.getCursor(inputMemberId).getSelectionType(); - if(selectionType === ops.OdtCursor.RangeSelection) { - return fn(e) - } - return true - } + firstSibling = firstNode; + while(firstSibling.parentNode !== containerNode) { + firstSibling = firstSibling.parentNode } - function init() { - var isMacOS = window.navigator.appVersion.toLowerCase().indexOf("mac") !== -1, modifier = gui.KeyboardHandler.Modifier, keyCode = gui.KeyboardHandler.KeyCode; - drawShadowCursorTask = new core.ScheduledTask(updateShadowCursor, 0); - keyDownHandler.bind(keyCode.Tab, modifier.None, rangeSelectionOnly(function() { - textManipulator.insertText("\t"); - return true - })); - keyDownHandler.bind(keyCode.Left, modifier.None, rangeSelectionOnly(moveCursorToLeft)); - keyDownHandler.bind(keyCode.Right, modifier.None, rangeSelectionOnly(moveCursorToRight)); - keyDownHandler.bind(keyCode.Up, modifier.None, rangeSelectionOnly(moveCursorUp)); - keyDownHandler.bind(keyCode.Down, modifier.None, rangeSelectionOnly(moveCursorDown)); - keyDownHandler.bind(keyCode.Backspace, modifier.None, returnTrue(textManipulator.removeTextByBackspaceKey)); - keyDownHandler.bind(keyCode.Delete, modifier.None, textManipulator.removeTextByDeleteKey); - keyDownHandler.bind(keyCode.Left, modifier.Shift, rangeSelectionOnly(extendSelectionToLeft)); - keyDownHandler.bind(keyCode.Right, modifier.Shift, rangeSelectionOnly(extendSelectionToRight)); - keyDownHandler.bind(keyCode.Up, modifier.Shift, rangeSelectionOnly(extendSelectionUp)); - keyDownHandler.bind(keyCode.Down, modifier.Shift, rangeSelectionOnly(extendSelectionDown)); - keyDownHandler.bind(keyCode.Home, modifier.None, rangeSelectionOnly(moveCursorToLineStart)); - keyDownHandler.bind(keyCode.End, modifier.None, rangeSelectionOnly(moveCursorToLineEnd)); - keyDownHandler.bind(keyCode.Home, modifier.Ctrl, rangeSelectionOnly(moveCursorToDocumentStart)); - keyDownHandler.bind(keyCode.End, modifier.Ctrl, rangeSelectionOnly(moveCursorToDocumentEnd)); - keyDownHandler.bind(keyCode.Home, modifier.Shift, rangeSelectionOnly(extendSelectionToLineStart)); - keyDownHandler.bind(keyCode.End, modifier.Shift, rangeSelectionOnly(extendSelectionToLineEnd)); - keyDownHandler.bind(keyCode.Up, modifier.CtrlShift, rangeSelectionOnly(extendSelectionToParagraphStart)); - keyDownHandler.bind(keyCode.Down, modifier.CtrlShift, rangeSelectionOnly(extendSelectionToParagraphEnd)); - keyDownHandler.bind(keyCode.Home, modifier.CtrlShift, rangeSelectionOnly(extendSelectionToDocumentStart)); - keyDownHandler.bind(keyCode.End, modifier.CtrlShift, rangeSelectionOnly(extendSelectionToDocumentEnd)); - if(isMacOS) { - keyDownHandler.bind(keyCode.Clear, modifier.None, textManipulator.removeCurrentSelection); - keyDownHandler.bind(keyCode.Left, modifier.Meta, rangeSelectionOnly(moveCursorToLineStart)); - keyDownHandler.bind(keyCode.Right, modifier.Meta, rangeSelectionOnly(moveCursorToLineEnd)); - keyDownHandler.bind(keyCode.Home, modifier.Meta, rangeSelectionOnly(moveCursorToDocumentStart)); - keyDownHandler.bind(keyCode.End, modifier.Meta, rangeSelectionOnly(moveCursorToDocumentEnd)); - keyDownHandler.bind(keyCode.Left, modifier.MetaShift, rangeSelectionOnly(extendSelectionToLineStart)); - keyDownHandler.bind(keyCode.Right, modifier.MetaShift, rangeSelectionOnly(extendSelectionToLineEnd)); - keyDownHandler.bind(keyCode.Up, modifier.AltShift, rangeSelectionOnly(extendSelectionToParagraphStart)); - keyDownHandler.bind(keyCode.Down, modifier.AltShift, rangeSelectionOnly(extendSelectionToParagraphEnd)); - keyDownHandler.bind(keyCode.Up, modifier.MetaShift, rangeSelectionOnly(extendSelectionToDocumentStart)); - keyDownHandler.bind(keyCode.Down, modifier.MetaShift, rangeSelectionOnly(extendSelectionToDocumentEnd)); - keyDownHandler.bind(keyCode.A, modifier.Meta, rangeSelectionOnly(extendSelectionToEntireDocument)); - keyDownHandler.bind(keyCode.B, modifier.Meta, rangeSelectionOnly(directTextStyler.toggleBold)); - keyDownHandler.bind(keyCode.I, modifier.Meta, rangeSelectionOnly(directTextStyler.toggleItalic)); - keyDownHandler.bind(keyCode.U, modifier.Meta, rangeSelectionOnly(directTextStyler.toggleUnderline)); - if(directParagraphStyler) { - keyDownHandler.bind(keyCode.L, modifier.MetaShift, rangeSelectionOnly(directParagraphStyler.alignParagraphLeft)); - keyDownHandler.bind(keyCode.E, modifier.MetaShift, rangeSelectionOnly(directParagraphStyler.alignParagraphCenter)); - keyDownHandler.bind(keyCode.R, modifier.MetaShift, rangeSelectionOnly(directParagraphStyler.alignParagraphRight)); - keyDownHandler.bind(keyCode.J, modifier.MetaShift, rangeSelectionOnly(directParagraphStyler.alignParagraphJustified)) - } - if(annotationController) { - keyDownHandler.bind(keyCode.C, modifier.MetaShift, annotationController.addAnnotation) - } - keyDownHandler.bind(keyCode.Z, modifier.Meta, undo); - keyDownHandler.bind(keyCode.Z, modifier.MetaShift, redo) + lastSibling = lastNode; + while(lastSibling.parentNode !== containerNode) { + lastSibling = lastSibling.parentNode + } + rootFilter = odtDocument.createRootFilter(firstNode); + currentNode = firstSibling.nextSibling; + while(currentNode && currentNode !== lastSibling) { + currentRect = getRectFromNodeAfterFiltering(currentNode); + grownRect = checkAndGrowOrCreateRect(grownRect, currentRect); + currentNode = currentNode.nextSibling + } + if(odfUtils.isParagraph(firstSibling)) { + grownRect = checkAndGrowOrCreateRect(grownRect, domUtils.getBoundingClientRect(firstSibling)) + }else { + if(firstSibling.nodeType === Node.TEXT_NODE) { + currentNode = firstSibling; + range.setStart(currentNode, firstOffset); + range.setEnd(currentNode, currentNode === lastSibling ? lastOffset : currentNode.length); + currentRect = range.getBoundingClientRect(); + grownRect = checkAndGrowOrCreateRect(grownRect, currentRect) }else { - keyDownHandler.bind(keyCode.A, modifier.Ctrl, rangeSelectionOnly(extendSelectionToEntireDocument)); - keyDownHandler.bind(keyCode.B, modifier.Ctrl, rangeSelectionOnly(directTextStyler.toggleBold)); - keyDownHandler.bind(keyCode.I, modifier.Ctrl, rangeSelectionOnly(directTextStyler.toggleItalic)); - keyDownHandler.bind(keyCode.U, modifier.Ctrl, rangeSelectionOnly(directTextStyler.toggleUnderline)); - if(directParagraphStyler) { - keyDownHandler.bind(keyCode.L, modifier.CtrlShift, rangeSelectionOnly(directParagraphStyler.alignParagraphLeft)); - keyDownHandler.bind(keyCode.E, modifier.CtrlShift, rangeSelectionOnly(directParagraphStyler.alignParagraphCenter)); - keyDownHandler.bind(keyCode.R, modifier.CtrlShift, rangeSelectionOnly(directParagraphStyler.alignParagraphRight)); - keyDownHandler.bind(keyCode.J, modifier.CtrlShift, rangeSelectionOnly(directParagraphStyler.alignParagraphJustified)) - } - if(annotationController) { - keyDownHandler.bind(keyCode.C, modifier.CtrlAlt, annotationController.addAnnotation) + treeWalker = doc.createTreeWalker(firstSibling, NodeFilter.SHOW_TEXT, acceptNode, false); + currentNode = treeWalker.currentNode = firstNode; + while(currentNode && currentNode !== lastNode) { + range.setStart(currentNode, firstOffset); + range.setEnd(currentNode, currentNode.length); + currentRect = range.getBoundingClientRect(); + grownRect = checkAndGrowOrCreateRect(grownRect, currentRect); + lastMeasuredNode = currentNode; + firstOffset = 0; + currentNode = treeWalker.nextNode() } - keyDownHandler.bind(keyCode.Z, modifier.Ctrl, undo); - keyDownHandler.bind(keyCode.Z, modifier.CtrlShift, redo) } - keyPressHandler.setDefault(rangeSelectionOnly(function(e) { - var text = stringFromKeyPress(e); - if(text && !(e.altKey || (e.ctrlKey || e.metaKey))) { - textManipulator.insertText(text); - return true + } + if(!lastMeasuredNode) { + lastMeasuredNode = firstNode + } + if(odfUtils.isParagraph(lastSibling)) { + grownRect = checkAndGrowOrCreateRect(grownRect, domUtils.getBoundingClientRect(lastSibling)) + }else { + if(lastSibling.nodeType === Node.TEXT_NODE) { + currentNode = lastSibling; + range.setStart(currentNode, currentNode === firstSibling ? firstOffset : 0); + range.setEnd(currentNode, lastOffset); + currentRect = range.getBoundingClientRect(); + grownRect = checkAndGrowOrCreateRect(grownRect, currentRect) + }else { + treeWalker = doc.createTreeWalker(lastSibling, NodeFilter.SHOW_TEXT, acceptNode, false); + currentNode = treeWalker.currentNode = lastNode; + while(currentNode && currentNode !== lastMeasuredNode) { + range.setStart(currentNode, 0); + range.setEnd(currentNode, lastOffset); + currentRect = range.getBoundingClientRect(); + grownRect = checkAndGrowOrCreateRect(grownRect, currentRect); + currentNode = treeWalker.previousNode(); + if(currentNode) { + lastOffset = currentNode.length + } } - return false - })); - keyPressHandler.bind(keyCode.Enter, modifier.None, rangeSelectionOnly(textManipulator.enqueueParagraphSplittingOps)) + } } - init() + return grownRect + } + function getCollapsedRectOfTextRange(range, useRightEdge) { + var clientRect = range.getBoundingClientRect(), collapsedRect = {}; + collapsedRect.width = 0; + collapsedRect.top = clientRect.top; + collapsedRect.bottom = clientRect.bottom; + collapsedRect.height = clientRect.height; + collapsedRect.left = collapsedRect.right = useRightEdge ? clientRect.right : clientRect.left; + return collapsedRect + } + function repositionOverlays(selectedRange) { + var extremes = getExtremeRanges(selectedRange), firstRange, lastRange, fillerRange, firstRect, fillerRect, lastRect; + if(selectedRange.collapsed || !extremes) { + showOverlays(false) + }else { + showOverlays(true); + firstRange = extremes.firstRange; + lastRange = extremes.lastRange; + fillerRange = extremes.fillerRange; + firstRect = translateRect(getCollapsedRectOfTextRange(firstRange, false)); + lastRect = translateRect(getCollapsedRectOfTextRange(lastRange, true)); + fillerRect = getFillerRect(fillerRange); + if(!fillerRect) { + fillerRect = getBoundingRect(firstRect, lastRect) + }else { + fillerRect = translateRect(fillerRect) + } + setRect(overlayTop, {left:firstRect.left, top:firstRect.top, width:Math.max(0, fillerRect.width - (firstRect.left - fillerRect.left)), height:firstRect.height}); + if(lastRect.top === firstRect.top || lastRect.bottom === firstRect.bottom) { + overlayMiddle.style.display = overlayBottom.style.display = "none" + }else { + setRect(overlayBottom, {left:fillerRect.left, top:lastRect.top, width:Math.max(0, lastRect.right - fillerRect.left), height:lastRect.height}); + setRect(overlayMiddle, {left:fillerRect.left, top:firstRect.top + firstRect.height, width:Math.max(0, parseFloat(overlayTop.style.left) + parseFloat(overlayTop.style.width) - parseFloat(overlayBottom.style.left)), height:Math.max(0, lastRect.top - firstRect.bottom)}) + } + firstRange.detach(); + lastRange.detach(); + fillerRange.detach() + } + } + function rerender() { + addOverlays(); + if(cursor.getSelectionType() === ops.OdtCursor.RangeSelection) { + showOverlays(true); + repositionOverlays(cursor.getSelectedRange()) + }else { + showOverlays(false) + } + } + this.rerender = rerender; + this.show = rerender; + this.hide = function() { + showOverlays(false) }; - return gui.SessionController -}(); + this.visible = function() { + return isVisible + }; + function handleCursorMove(movedCursor) { + if(movedCursor === cursor) { + rerender() + } + } + this.destroy = function(callback) { + root.removeChild(overlayTop); + root.removeChild(overlayMiddle); + root.removeChild(overlayBottom); + cursor.getOdtDocument().unsubscribe(ops.OdtDocument.signalCursorMoved, handleCursorMove); + callback() + }; + function init() { + var editinfons = "urn:webodf:names:editinfo", memberid = cursor.getMemberId(); + addOverlays(); + overlayTop.setAttributeNS(editinfons, "editinfo:memberid", memberid); + overlayMiddle.setAttributeNS(editinfons, "editinfo:memberid", memberid); + overlayBottom.setAttributeNS(editinfons, "editinfo:memberid", memberid); + overlayTop.className = overlayMiddle.className = overlayBottom.className = "selectionOverlay"; + cursor.getOdtDocument().subscribe(ops.OdtDocument.signalCursorMoved, handleCursorMove) + } + init() +}; /* Copyright (C) 2013 KO GmbH @@ -16119,19 +15932,76 @@ gui.SessionController = function() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OperationRouter = function OperationRouter() { -}; -ops.OperationRouter.prototype.setOperationFactory = function(f) { -}; -ops.OperationRouter.prototype.setPlaybackFunction = function(playback_func) { -}; -ops.OperationRouter.prototype.push = function(operations) { -}; -ops.OperationRouter.prototype.close = function(callback) { +runtime.loadClass("gui.SelectionView"); +gui.SelectionViewManager = function SelectionViewManager() { + var selectionViews = {}; + function getSelectionView(memberId) { + return selectionViews.hasOwnProperty(memberId) ? selectionViews[memberId] : null + } + this.getSelectionView = getSelectionView; + function getSelectionViews() { + return Object.keys(selectionViews).map(function(memberid) { + return selectionViews[memberid] + }) + } + this.getSelectionViews = getSelectionViews; + function removeSelectionView(memberId) { + if(selectionViews.hasOwnProperty(memberId)) { + selectionViews[memberId].destroy(function() { + }); + delete selectionViews[memberId] + } + } + this.removeSelectionView = removeSelectionView; + function hideSelectionView(memberId) { + if(selectionViews.hasOwnProperty(memberId)) { + selectionViews[memberId].hide() + } + } + this.hideSelectionView = hideSelectionView; + function showSelectionView(memberId) { + if(selectionViews.hasOwnProperty(memberId)) { + selectionViews[memberId].show() + } + } + this.showSelectionView = showSelectionView; + this.rerenderSelectionViews = function() { + Object.keys(selectionViews).forEach(function(memberId) { + if(selectionViews[memberId].visible()) { + selectionViews[memberId].rerender() + } + }) + }; + this.registerCursor = function(cursor, virtualSelectionsInitiallyVisible) { + var memberId = cursor.getMemberId(), selectionView = new gui.SelectionView(cursor); + if(virtualSelectionsInitiallyVisible) { + selectionView.show() + }else { + selectionView.hide() + } + selectionViews[memberId] = selectionView; + return selectionView + }; + this.destroy = function(callback) { + var selectionViewArray = getSelectionViews(); + (function destroySelectionView(i, err) { + if(err) { + callback(err) + }else { + if(i < selectionViewArray.length) { + selectionViewArray[i].destroy(function(err) { + destroySelectionView(i + 1, err) + }) + }else { + callback() + } + } + })(0, undefined) + } }; /* - Copyright (C) 2012 KO GmbH + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -16166,72 +16036,167 @@ ops.OperationRouter.prototype.close = function(callback) { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.TrivialOperationRouter = function TrivialOperationRouter() { - var operationFactory, playbackFunction; - this.setOperationFactory = function(f) { - operationFactory = f +runtime.loadClass("core.DomUtils"); +runtime.loadClass("gui.UndoManager"); +runtime.loadClass("gui.UndoStateRules"); +gui.TrivialUndoManager = function TrivialUndoManager(defaultRules) { + var self = this, cursorns = "urn:webodf:names:cursor", domUtils = new core.DomUtils, initialDoc, initialState = [], playFunc, odtDocument, currentUndoState = [], undoStates = [], redoStates = [], eventNotifier = new core.EventNotifier([gui.UndoManager.signalUndoStackChanged, gui.UndoManager.signalUndoStateCreated, gui.UndoManager.signalUndoStateModified, gui.TrivialUndoManager.signalDocumentRootReplaced]), undoRules = defaultRules || new gui.UndoStateRules; + function emitStackChange() { + eventNotifier.emit(gui.UndoManager.signalUndoStackChanged, {undoAvailable:self.hasUndoStates(), redoAvailable:self.hasRedoStates()}) + } + function mostRecentUndoState() { + return undoStates[undoStates.length - 1] + } + function completeCurrentUndoState() { + if(currentUndoState !== initialState && currentUndoState !== mostRecentUndoState()) { + undoStates.push(currentUndoState) + } + } + function removeNode(node) { + var sibling = node.previousSibling || node.nextSibling; + node.parentNode.removeChild(node); + domUtils.normalizeTextNodes(sibling) + } + function removeCursors(root) { + domUtils.getElementsByTagNameNS(root, cursorns, "cursor").forEach(removeNode); + domUtils.getElementsByTagNameNS(root, cursorns, "anchor").forEach(removeNode) + } + function values(obj) { + return Object.keys(obj).map(function(key) { + return obj[key] + }) + } + function extractCursorStates(undoStates) { + var addCursor = {}, moveCursor = {}, requiredAddOps = {}, remainingAddOps, operations = undoStates.pop(); + odtDocument.getCursors().forEach(function(cursor) { + requiredAddOps[cursor.getMemberId()] = true + }); + remainingAddOps = Object.keys(requiredAddOps).length; + function processOp(op) { + var spec = op.spec(); + if(!requiredAddOps[spec.memberid]) { + return + } + switch(spec.optype) { + case "AddCursor": + if(!addCursor[spec.memberid]) { + addCursor[spec.memberid] = op; + delete requiredAddOps[spec.memberid]; + remainingAddOps -= 1 + } + break; + case "MoveCursor": + if(!moveCursor[spec.memberid]) { + moveCursor[spec.memberid] = op + } + break + } + } + while(operations && remainingAddOps > 0) { + operations.reverse(); + operations.forEach(processOp); + operations = undoStates.pop() + } + return values(addCursor).concat(values(moveCursor)) + } + this.subscribe = function(signal, callback) { + eventNotifier.subscribe(signal, callback) }; - this.setPlaybackFunction = function(playback_func) { - playbackFunction = playback_func + this.unsubscribe = function(signal, callback) { + eventNotifier.unsubscribe(signal, callback) }; - this.push = function(operations) { - operations.forEach(function(op) { - var timedOp, opspec = op.spec(); - opspec.timestamp = (new Date).getTime(); - timedOp = operationFactory.create(opspec); - playbackFunction(timedOp) - }) + this.hasUndoStates = function() { + return undoStates.length > 0 }; - this.close = function(cb) { - cb() - } -}; -gui.EditInfoHandle = function EditInfoHandle(parentElement) { - var edits = [], handle, document = (parentElement.ownerDocument), htmlns = document.documentElement.namespaceURI, editinfons = "urn:webodf:names:editinfo"; - function renderEdits() { - var i, infoDiv, colorSpan, authorSpan, timeSpan; - handle.innerHTML = ""; - for(i = 0;i < edits.length;i += 1) { - infoDiv = document.createElementNS(htmlns, "div"); - infoDiv.className = "editInfo"; - colorSpan = document.createElementNS(htmlns, "span"); - colorSpan.className = "editInfoColor"; - colorSpan.setAttributeNS(editinfons, "editinfo:memberid", edits[i].memberid); - authorSpan = document.createElementNS(htmlns, "span"); - authorSpan.className = "editInfoAuthor"; - authorSpan.setAttributeNS(editinfons, "editinfo:memberid", edits[i].memberid); - timeSpan = document.createElementNS(htmlns, "span"); - timeSpan.className = "editInfoTime"; - timeSpan.setAttributeNS(editinfons, "editinfo:memberid", edits[i].memberid); - timeSpan.innerHTML = edits[i].time; - infoDiv.appendChild(colorSpan); - infoDiv.appendChild(authorSpan); - infoDiv.appendChild(timeSpan); - handle.appendChild(infoDiv) + this.hasRedoStates = function() { + return redoStates.length > 0 + }; + this.setOdtDocument = function(newDocument) { + odtDocument = newDocument + }; + this.resetInitialState = function() { + undoStates.length = 0; + redoStates.length = 0; + initialState.length = 0; + currentUndoState.length = 0; + initialDoc = null; + emitStackChange() + }; + this.saveInitialState = function() { + var odfContainer = odtDocument.getOdfCanvas().odfContainer(), annotationViewManager = odtDocument.getOdfCanvas().getAnnotationViewManager(); + if(annotationViewManager) { + annotationViewManager.forgetAnnotations() } - } - this.setEdits = function(editArray) { - edits = editArray; - renderEdits() + initialDoc = odfContainer.rootElement.cloneNode(true); + odtDocument.getOdfCanvas().refreshAnnotations(); + removeCursors(initialDoc); + completeCurrentUndoState(); + undoStates.unshift(initialState); + currentUndoState = initialState = extractCursorStates(undoStates); + undoStates.length = 0; + redoStates.length = 0; + emitStackChange() }; - this.show = function() { - handle.style.display = "block" + this.setPlaybackFunction = function(playback_func) { + playFunc = playback_func }; - this.hide = function() { - handle.style.display = "none" + this.onOperationExecuted = function(op) { + redoStates.length = 0; + if(undoRules.isEditOperation(op) && currentUndoState === initialState || !undoRules.isPartOfOperationSet(op, currentUndoState)) { + completeCurrentUndoState(); + currentUndoState = [op]; + undoStates.push(currentUndoState); + eventNotifier.emit(gui.UndoManager.signalUndoStateCreated, {operations:currentUndoState}); + emitStackChange() + }else { + currentUndoState.push(op); + eventNotifier.emit(gui.UndoManager.signalUndoStateModified, {operations:currentUndoState}) + } }; - this.destroy = function(callback) { - parentElement.removeChild(handle); - callback() + this.moveForward = function(states) { + var moved = 0, redoOperations; + while(states && redoStates.length) { + redoOperations = redoStates.pop(); + undoStates.push(redoOperations); + redoOperations.forEach(playFunc); + states -= 1; + moved += 1 + } + if(moved) { + currentUndoState = mostRecentUndoState(); + emitStackChange() + } + return moved }; - function init() { - handle = document.createElementNS(htmlns, "div"); - handle.setAttribute("class", "editInfoHandle"); - handle.style.display = "none"; - parentElement.appendChild(handle) + this.moveBackward = function(states) { + var odfCanvas = odtDocument.getOdfCanvas(), odfContainer = odfCanvas.odfContainer(), moved = 0; + while(states && undoStates.length) { + redoStates.push(undoStates.pop()); + states -= 1; + moved += 1 + } + if(moved) { + odfContainer.setRootElement(initialDoc.cloneNode(true)); + odfCanvas.setOdfContainer(odfContainer, true); + eventNotifier.emit(gui.TrivialUndoManager.signalDocumentRootReplaced, {}); + odtDocument.getCursors().forEach(function(cursor) { + odtDocument.removeCursor(cursor.getMemberId()) + }); + initialState.forEach(playFunc); + undoStates.forEach(function(ops) { + ops.forEach(playFunc) + }); + odfCanvas.refreshCSS(); + currentUndoState = mostRecentUndoState() || initialState; + emitStackChange() + } + return moved } - init() }; +gui.TrivialUndoManager.signalDocumentRootReplaced = "documentRootReplaced"; +(function() { + return gui.TrivialUndoManager +})(); /* Copyright (C) 2012-2013 KO GmbH @@ -16269,361 +16234,357 @@ gui.EditInfoHandle = function EditInfoHandle(parentElement) { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("ops.EditInfo"); -runtime.loadClass("gui.EditInfoHandle"); -gui.EditInfoMarker = function EditInfoMarker(editInfo, initialVisibility) { - var self = this, editInfoNode, handle, marker, editinfons = "urn:webodf:names:editinfo", decay1, decay2, decayTimeStep = 1E4; - function applyDecay(opacity, delay) { - return runtime.setTimeout(function() { - marker.style.opacity = opacity - }, delay) - } - function deleteDecay(timer) { - runtime.clearTimeout(timer) - } - function setLastAuthor(memberid) { - marker.setAttributeNS(editinfons, "editinfo:memberid", memberid) - } - this.addEdit = function(memberid, timestamp) { - var age = Date.now() - timestamp; - editInfo.addEdit(memberid, timestamp); - handle.setEdits(editInfo.getSortedEdits()); - setLastAuthor(memberid); - if(decay1) { - deleteDecay(decay1) - } - if(decay2) { - deleteDecay(decay2) - } - if(age < decayTimeStep) { - applyDecay(1, 0); - decay1 = applyDecay(0.5, decayTimeStep - age); - decay2 = applyDecay(0.2, decayTimeStep * 2 - age) - }else { - if(age >= decayTimeStep && age < decayTimeStep * 2) { - applyDecay(0.5, 0); - decay2 = applyDecay(0.2, decayTimeStep * 2 - age) - }else { - applyDecay(0.2, 0) - } - } - }; - this.getEdits = function() { - return editInfo.getEdits() - }; - this.clearEdits = function() { - editInfo.clearEdits(); - handle.setEdits([]); - if(marker.hasAttributeNS(editinfons, "editinfo:memberid")) { - marker.removeAttributeNS(editinfons, "editinfo:memberid") +runtime.loadClass("ops.TrivialOperationRouter"); +runtime.loadClass("ops.OperationFactory"); +runtime.loadClass("ops.OdtDocument"); +ops.Session = function Session(odfCanvas) { + var self = this, operationFactory = new ops.OperationFactory, odtDocument = new ops.OdtDocument(odfCanvas), operationRouter = null; + this.setOperationFactory = function(opFactory) { + operationFactory = opFactory; + if(operationRouter) { + operationRouter.setOperationFactory(operationFactory) } }; - this.getEditInfo = function() { - return editInfo - }; - this.show = function() { - marker.style.display = "block" + this.setOperationRouter = function(opRouter) { + operationRouter = opRouter; + opRouter.setPlaybackFunction(function(op) { + if(op.execute(odtDocument)) { + odtDocument.emit(ops.OdtDocument.signalOperationExecuted, op); + return true + } + return false + }); + opRouter.setOperationFactory(operationFactory) }; - this.hide = function() { - self.hideHandle(); - marker.style.display = "none" + this.getOperationFactory = function() { + return operationFactory }; - this.showHandle = function() { - handle.show() + this.getOdtDocument = function() { + return odtDocument }; - this.hideHandle = function() { - handle.hide() + this.enqueue = function(ops) { + operationRouter.push(ops) }; - this.destroy = function(callback) { - editInfoNode.removeChild(marker); - handle.destroy(function(err) { + this.close = function(callback) { + operationRouter.close(function(err) { if(err) { callback(err) }else { - editInfo.destroy(callback) + odtDocument.close(callback) } }) }; + this.destroy = function(callback) { + odtDocument.destroy(callback) + }; function init() { - var dom = editInfo.getOdtDocument().getDOM(), htmlns = dom.documentElement.namespaceURI; - marker = dom.createElementNS(htmlns, "div"); - marker.setAttribute("class", "editInfoMarker"); - marker.onmouseover = function() { - self.showHandle() - }; - marker.onmouseout = function() { - self.hideHandle() - }; - editInfoNode = editInfo.getNode(); - editInfoNode.appendChild(marker); - handle = new gui.EditInfoHandle(editInfoNode); - if(!initialVisibility) { - self.hide() - } + self.setOperationRouter(new ops.TrivialOperationRouter) } init() }; /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2013 KO GmbH @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . + This file is part of WebODF. - 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. + WebODF 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. - 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. + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. - This license applies to this entire compilation. + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . @licend + @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("gui.Caret"); -runtime.loadClass("ops.EditInfo"); -runtime.loadClass("gui.EditInfoMarker"); -gui.SessionViewOptions = function() { - this.editInfoMarkersInitiallyVisible = true; - this.caretAvatarsInitiallyVisible = true; - this.caretBlinksOnRangeSelect = true -}; -gui.SessionView = function() { - function configOption(userValue, defaultValue) { - return userValue !== undefined ? Boolean(userValue) : defaultValue - } - function SessionView(viewOptions, localMemberId, session, caretManager, selectionViewManager) { - var avatarInfoStyles, editInfons = "urn:webodf:names:editinfo", editInfoMap = {}, showEditInfoMarkers = configOption(viewOptions.editInfoMarkersInitiallyVisible, true), showCaretAvatars = configOption(viewOptions.caretAvatarsInitiallyVisible, true), blinkOnRangeSelect = configOption(viewOptions.caretBlinksOnRangeSelect, true), rerenderIntervalId, rerenderSelectionViews = false, RERENDER_INTERVAL = 200; - function createAvatarInfoNodeMatch(nodeName, memberId, pseudoClass) { - return nodeName + '[editinfo|memberid="' + memberId + '"]' + pseudoClass - } - function getAvatarInfoStyle(nodeName, memberId, pseudoClass) { - var node = avatarInfoStyles.firstChild, nodeMatch = createAvatarInfoNodeMatch(nodeName, memberId, pseudoClass) + "{"; - while(node) { - if(node.nodeType === Node.TEXT_NODE && node.data.indexOf(nodeMatch) === 0) { - return node - } - node = node.nextSibling +runtime.loadClass("core.EventNotifier"); +runtime.loadClass("core.PositionFilter"); +runtime.loadClass("ops.Session"); +runtime.loadClass("ops.OpAddAnnotation"); +runtime.loadClass("ops.OpRemoveAnnotation"); +runtime.loadClass("gui.SelectionMover"); +gui.AnnotationController = function AnnotationController(session, inputMemberId) { + var odtDocument = session.getOdtDocument(), isAnnotatable = false, eventNotifier = new core.EventNotifier([gui.AnnotationController.annotatableChanged]), officens = odf.Namespaces.officens; + function isWithinAnnotation(node, container) { + while(node && node !== container) { + if(node.namespaceURI === officens && node.localName === "annotation") { + return true } - return null + node = node.parentNode } - function setAvatarInfoStyle(memberId, name, color) { - function setStyle(nodeName, rule, pseudoClass) { - var styleRule = createAvatarInfoNodeMatch(nodeName, memberId, pseudoClass) + rule, styleNode = getAvatarInfoStyle(nodeName, memberId, pseudoClass); - if(styleNode) { - styleNode.data = styleRule - }else { - avatarInfoStyles.appendChild(document.createTextNode(styleRule)) - } - } - setStyle("div.editInfoMarker", "{ background-color: " + color + "; }", ""); - setStyle("span.editInfoColor", "{ background-color: " + color + "; }", ""); - setStyle("span.editInfoAuthor", '{ content: "' + name + '"; }', ":before"); - setStyle("dc|creator", "{ background-color: " + color + "; }", ""); - setStyle("div.selectionOverlay", "{ background-color: " + color + ";}", "") + return false + } + function updatedCachedValues() { + var cursor = odtDocument.getCursor(inputMemberId), cursorNode = cursor && cursor.getNode(), newIsAnnotatable = false; + if(cursorNode) { + newIsAnnotatable = !isWithinAnnotation(cursorNode, odtDocument.getRootNode()) } - function highlightEdit(element, memberId, timestamp) { - var editInfo, editInfoMarker, id = "", editInfoNode = element.getElementsByTagNameNS(editInfons, "editinfo")[0]; - if(editInfoNode) { - id = editInfoNode.getAttributeNS(editInfons, "id"); - editInfoMarker = editInfoMap[id] - }else { - id = Math.random().toString(); - editInfo = new ops.EditInfo(element, session.getOdtDocument()); - editInfoMarker = new gui.EditInfoMarker(editInfo, showEditInfoMarkers); - editInfoNode = element.getElementsByTagNameNS(editInfons, "editinfo")[0]; - editInfoNode.setAttributeNS(editInfons, "id", id); - editInfoMap[id] = editInfoMarker - } - editInfoMarker.addEdit(memberId, new Date(timestamp)) + if(newIsAnnotatable !== isAnnotatable) { + isAnnotatable = newIsAnnotatable; + eventNotifier.emit(gui.AnnotationController.annotatableChanged, isAnnotatable) } - function setEditInfoMarkerVisibility(visible) { - var editInfoMarker, keyname; - for(keyname in editInfoMap) { - if(editInfoMap.hasOwnProperty(keyname)) { - editInfoMarker = editInfoMap[keyname]; - if(visible) { - editInfoMarker.show() - }else { - editInfoMarker.hide() - } - } - } + } + function onCursorAdded(cursor) { + if(cursor.getMemberId() === inputMemberId) { + updatedCachedValues() } - function setCaretAvatarVisibility(visible) { - caretManager.getCarets().forEach(function(caret) { - if(visible) { - caret.showHandle() - }else { - caret.hideHandle() - } - }) + } + function onCursorRemoved(memberId) { + if(memberId === inputMemberId) { + updatedCachedValues() } - this.showEditInfoMarkers = function() { - if(showEditInfoMarkers) { - return - } - showEditInfoMarkers = true; - setEditInfoMarkerVisibility(showEditInfoMarkers) - }; - this.hideEditInfoMarkers = function() { - if(!showEditInfoMarkers) { - return - } - showEditInfoMarkers = false; - setEditInfoMarkerVisibility(showEditInfoMarkers) - }; - this.showCaretAvatars = function() { - if(showCaretAvatars) { - return - } - showCaretAvatars = true; - setCaretAvatarVisibility(showCaretAvatars) - }; - this.hideCaretAvatars = function() { - if(!showCaretAvatars) { - return - } - showCaretAvatars = false; - setCaretAvatarVisibility(showCaretAvatars) - }; - this.getSession = function() { - return session - }; - this.getCaret = function(memberid) { - return caretManager.getCaret(memberid) - }; - function renderMemberData(member) { - var memberId = member.getMemberId(), properties = member.getProperties(); - setAvatarInfoStyle(memberId, properties.fullName, properties.color); - if(localMemberId === memberId) { - setAvatarInfoStyle("", "", properties.color) - } + } + function onCursorMoved(cursor) { + if(cursor.getMemberId() === inputMemberId) { + updatedCachedValues() } - function onCursorAdded(cursor) { - var memberId = cursor.getMemberId(), properties = session.getOdtDocument().getMember(memberId).getProperties(), caret; - caretManager.registerCursor(cursor, showCaretAvatars, blinkOnRangeSelect); - selectionViewManager.registerCursor(cursor, true); - caret = caretManager.getCaret(memberId); - if(caret) { - caret.setAvatarImageUrl(properties.imageUrl); - caret.setColor(properties.color) - } - runtime.log("+++ View here +++ eagerly created an Caret for '" + memberId + "'! +++") + } + this.isAnnotatable = function() { + return isAnnotatable + }; + this.addAnnotation = function() { + var op = new ops.OpAddAnnotation, selection = odtDocument.getCursorSelection(inputMemberId), length = selection.length, position = selection.position; + if(!isAnnotatable) { + return } - function onCursorMoved(cursor) { - var memberId = cursor.getMemberId(), localSelectionView = selectionViewManager.getSelectionView(localMemberId), shadowSelectionView = selectionViewManager.getSelectionView(gui.ShadowCursor.ShadowCursorMemberId), localCaret = caretManager.getCaret(localMemberId); - if(memberId === localMemberId) { - shadowSelectionView.hide(); - localSelectionView.show(); - if(localCaret) { - localCaret.show() - } - }else { - if(memberId === gui.ShadowCursor.ShadowCursorMemberId) { - shadowSelectionView.show(); - localSelectionView.hide(); - if(localCaret) { - localCaret.hide() - } + position = length >= 0 ? position : position + length; + length = Math.abs(length); + op.init({memberid:inputMemberId, position:position, length:length, name:inputMemberId + Date.now()}); + session.enqueue([op]) + }; + this.removeAnnotation = function(annotationNode) { + var startStep, endStep, op, moveCursor; + startStep = odtDocument.convertDomPointToCursorStep(annotationNode, 0) + 1; + endStep = odtDocument.convertDomPointToCursorStep(annotationNode, annotationNode.childNodes.length); + op = new ops.OpRemoveAnnotation; + op.init({memberid:inputMemberId, position:startStep, length:endStep - startStep}); + moveCursor = new ops.OpMoveCursor; + moveCursor.init({memberid:inputMemberId, position:startStep > 0 ? startStep - 1 : startStep, length:0}); + session.enqueue([op, moveCursor]) + }; + this.subscribe = function(eventid, cb) { + eventNotifier.subscribe(eventid, cb) + }; + this.unsubscribe = function(eventid, cb) { + eventNotifier.unsubscribe(eventid, cb) + }; + this.destroy = function(callback) { + odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); + callback() + }; + function init() { + odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); + odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); + odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); + updatedCachedValues() + } + init() +}; +gui.AnnotationController.annotatableChanged = "annotatable/changed"; +(function() { + return gui.AnnotationController +})(); +/* + + Copyright (C) 2013 KO GmbH + + @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. + + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . + + 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: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("core.EventNotifier"); +runtime.loadClass("core.Utils"); +runtime.loadClass("odf.OdfUtils"); +runtime.loadClass("ops.OpAddStyle"); +runtime.loadClass("ops.OpSetParagraphStyle"); +runtime.loadClass("gui.StyleHelper"); +gui.DirectParagraphStyler = function DirectParagraphStyler(session, inputMemberId, objectNameGenerator) { + var odtDocument = session.getOdtDocument(), utils = new core.Utils, odfUtils = new odf.OdfUtils, styleHelper = new gui.StyleHelper(odtDocument.getFormatting()), eventNotifier = new core.EventNotifier([gui.DirectParagraphStyler.paragraphStylingChanged]), isAlignedLeftValue, isAlignedCenterValue, isAlignedRightValue, isAlignedJustifiedValue; + function updatedCachedValues() { + var cursor = odtDocument.getCursor(inputMemberId), range = cursor && cursor.getSelectedRange(), diffMap; + function noteChange(oldValue, newValue, id) { + if(oldValue !== newValue) { + if(diffMap === undefined) { + diffMap = {} } + diffMap[id] = newValue } + return newValue } - function onCursorRemoved(memberid) { - selectionViewManager.removeSelectionView(memberid) + isAlignedLeftValue = noteChange(isAlignedLeftValue, range ? styleHelper.isAlignedLeft(range) : false, "isAlignedLeft"); + isAlignedCenterValue = noteChange(isAlignedCenterValue, range ? styleHelper.isAlignedCenter(range) : false, "isAlignedCenter"); + isAlignedRightValue = noteChange(isAlignedRightValue, range ? styleHelper.isAlignedRight(range) : false, "isAlignedRight"); + isAlignedJustifiedValue = noteChange(isAlignedJustifiedValue, range ? styleHelper.isAlignedJustified(range) : false, "isAlignedJustified"); + if(diffMap) { + eventNotifier.emit(gui.DirectParagraphStyler.paragraphStylingChanged, diffMap) } - function onParagraphChanged(info) { - highlightEdit(info.paragraphElement, info.memberId, info.timeStamp) + } + function onCursorAdded(cursor) { + if(cursor.getMemberId() === inputMemberId) { + updatedCachedValues() } - function requestRerenderOfSelectionViews() { - rerenderSelectionViews = true + } + function onCursorRemoved(memberId) { + if(memberId === inputMemberId) { + updatedCachedValues() } - function startRerenderLoop() { - rerenderIntervalId = runtime.getWindow().setInterval(function() { - if(rerenderSelectionViews) { - selectionViewManager.rerenderSelectionViews(); - rerenderSelectionViews = false - } - }, RERENDER_INTERVAL) + } + function onCursorMoved(cursor) { + if(cursor.getMemberId() === inputMemberId) { + updatedCachedValues() } - function stopRerenderLoop() { - runtime.getWindow().clearInterval(rerenderIntervalId) + } + function onParagraphStyleModified() { + updatedCachedValues() + } + function onParagraphChanged(args) { + var cursor = odtDocument.getCursor(inputMemberId); + if(cursor && odtDocument.getParagraphElement(cursor.getNode()) === args.paragraphElement) { + updatedCachedValues() } - this.destroy = function(callback) { - var odtDocument = session.getOdtDocument(), editInfoArray = Object.keys(editInfoMap).map(function(keyname) { - return editInfoMap[keyname] - }); - odtDocument.unsubscribe(ops.OdtDocument.signalMemberAdded, renderMemberData); - odtDocument.unsubscribe(ops.OdtDocument.signalMemberUpdated, renderMemberData); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); - odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, requestRerenderOfSelectionViews); - odtDocument.unsubscribe(ops.OdtDocument.signalTableAdded, requestRerenderOfSelectionViews); - odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, requestRerenderOfSelectionViews); - stopRerenderLoop(); - avatarInfoStyles.parentNode.removeChild(avatarInfoStyles); - (function destroyEditInfo(i, err) { - if(err) { - callback(err) - }else { - if(i < editInfoArray.length) { - editInfoArray[i].destroy(function(err) { - destroyEditInfo(i + 1, err) - }) - }else { - callback() - } - } - })(0, undefined) - }; - function init() { - var odtDocument = session.getOdtDocument(), head = document.getElementsByTagName("head")[0]; - odtDocument.subscribe(ops.OdtDocument.signalMemberAdded, renderMemberData); - odtDocument.subscribe(ops.OdtDocument.signalMemberUpdated, renderMemberData); - odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); - odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); - startRerenderLoop(); - odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, requestRerenderOfSelectionViews); - odtDocument.subscribe(ops.OdtDocument.signalTableAdded, requestRerenderOfSelectionViews); - odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, requestRerenderOfSelectionViews); - avatarInfoStyles = document.createElementNS(head.namespaceURI, "style"); - avatarInfoStyles.type = "text/css"; - avatarInfoStyles.media = "screen, print, handheld, projection"; - avatarInfoStyles.appendChild(document.createTextNode("@namespace editinfo url(urn:webodf:names:editinfo);")); - avatarInfoStyles.appendChild(document.createTextNode("@namespace dc url(http://purl.org/dc/elements/1.1/);")); - head.appendChild(avatarInfoStyles) + } + this.isAlignedLeft = function() { + return isAlignedLeftValue + }; + this.isAlignedCenter = function() { + return isAlignedCenterValue + }; + this.isAlignedRight = function() { + return isAlignedRightValue + }; + this.isAlignedJustified = function() { + return isAlignedJustifiedValue + }; + function roundUp(step) { + return step === ops.StepsTranslator.NEXT_STEP + } + function applyParagraphDirectStyling(applyDirectStyling) { + var range = odtDocument.getCursor(inputMemberId).getSelectedRange(), paragraphs = odfUtils.getParagraphElements(range), formatting = odtDocument.getFormatting(); + paragraphs.forEach(function(paragraph) { + var paragraphStartPoint = odtDocument.convertDomPointToCursorStep(paragraph, 0, roundUp), paragraphStyleName = paragraph.getAttributeNS(odf.Namespaces.textns, "style-name"), newParagraphStyleName = objectNameGenerator.generateStyleName(), opAddStyle, opSetParagraphStyle, paragraphProperties; + if(paragraphStyleName) { + paragraphProperties = formatting.createDerivedStyleObject(paragraphStyleName, "paragraph", {}) + } + paragraphProperties = applyDirectStyling(paragraphProperties || {}); + opAddStyle = new ops.OpAddStyle; + opAddStyle.init({memberid:inputMemberId, styleName:newParagraphStyleName, styleFamily:"paragraph", isAutomaticStyle:true, setProperties:paragraphProperties}); + opSetParagraphStyle = new ops.OpSetParagraphStyle; + opSetParagraphStyle.init({memberid:inputMemberId, styleName:newParagraphStyleName, position:paragraphStartPoint}); + session.enqueue([opAddStyle, opSetParagraphStyle]) + }) + } + function applySimpleParagraphDirectStyling(styleOverrides) { + applyParagraphDirectStyling(function(paragraphStyle) { + return utils.mergeObjects(paragraphStyle, styleOverrides) + }) + } + function alignParagraph(alignment) { + applySimpleParagraphDirectStyling({"style:paragraph-properties":{"fo:text-align":alignment}}) + } + this.alignParagraphLeft = function() { + alignParagraph("left"); + return true + }; + this.alignParagraphCenter = function() { + alignParagraph("center"); + return true + }; + this.alignParagraphRight = function() { + alignParagraph("right"); + return true + }; + this.alignParagraphJustified = function() { + alignParagraph("justify"); + return true + }; + function modifyParagraphIndent(direction, paragraphStyle) { + var tabStopDistance = odtDocument.getFormatting().getDefaultTabStopDistance(), paragraphProperties = paragraphStyle["style:paragraph-properties"], indentValue = paragraphProperties && paragraphProperties["fo:margin-left"], indent = indentValue && odfUtils.parseLength(indentValue), newIndent; + if(indent && indent.unit === tabStopDistance.unit) { + newIndent = indent.value + direction * tabStopDistance.value + indent.unit + }else { + newIndent = direction * tabStopDistance.value + tabStopDistance.unit } - init() + return utils.mergeObjects(paragraphStyle, {"style:paragraph-properties":{"fo:margin-left":newIndent}}) } - return SessionView -}(); + this.indent = function() { + applyParagraphDirectStyling(modifyParagraphIndent.bind(null, 1)); + return true + }; + this.outdent = function() { + applyParagraphDirectStyling(modifyParagraphIndent.bind(null, -1)); + return true + }; + this.subscribe = function(eventid, cb) { + eventNotifier.subscribe(eventid, cb) + }; + this.unsubscribe = function(eventid, cb) { + eventNotifier.unsubscribe(eventid, cb) + }; + this.destroy = function(callback) { + odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); + callback() + }; + function init() { + odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); + odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); + odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); + odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); + odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); + updatedCachedValues() + } + init() +}; +gui.DirectParagraphStyler.paragraphStylingChanged = "paragraphStyling/changed"; +(function() { + return gui.DirectParagraphStyler +})(); /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -16658,199 +16619,273 @@ gui.SessionView = function() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("gui.Caret"); -gui.CaretManager = function CaretManager(sessionController) { - var carets = {}, window = runtime.getWindow(), scrollIntoViewScheduled = false; - function getCaret(memberId) { - return carets.hasOwnProperty(memberId) ? carets[memberId] : null - } - function getCarets() { - return Object.keys(carets).map(function(memberid) { - return carets[memberid] - }) +runtime.loadClass("core.EventNotifier"); +runtime.loadClass("core.Utils"); +runtime.loadClass("ops.OpApplyDirectStyling"); +runtime.loadClass("gui.StyleHelper"); +gui.DirectTextStyler = function DirectTextStyler(session, inputMemberId) { + var self = this, utils = new core.Utils, odtDocument = session.getOdtDocument(), styleHelper = new gui.StyleHelper(odtDocument.getFormatting()), eventNotifier = new core.EventNotifier([gui.DirectTextStyler.textStylingChanged]), directCursorStyleProperties, currentSelectionStyles = [], isBoldValue = false, isItalicValue = false, hasUnderlineValue = false, hasStrikeThroughValue = false, fontSizeValue, fontNameValue; + function get(obj, keys) { + var i = 0, key = keys[i]; + while(key && obj) { + obj = obj[key]; + i += 1; + key = keys[i] + } + return keys.length === i ? obj : undefined } - function getCanvasElement() { - return sessionController.getSession().getOdtDocument().getOdfCanvas().getElement() + function getCommonValue(objArray, keys) { + var value = get(objArray[0], keys); + return objArray.every(function(obj) { + return value === get(obj, keys) + }) ? value : undefined } - function removeCaret(memberId) { - if(memberId === sessionController.getInputMemberId()) { - getCanvasElement().removeAttribute("tabindex") + function getAppliedStyles() { + var cursor = odtDocument.getCursor(inputMemberId), range = cursor && cursor.getSelectedRange(), selectionStyles = range && styleHelper.getAppliedStyles(range) || []; + if(selectionStyles[0] && directCursorStyleProperties) { + selectionStyles[0] = utils.mergeObjects(selectionStyles[0], (directCursorStyleProperties)) } - delete carets[memberId] + return selectionStyles } - function refreshLocalCaretBlinking(cursor) { - var caret, memberId = cursor.getMemberId(); - if(memberId === sessionController.getInputMemberId()) { - caret = getCaret(memberId); - if(caret) { - caret.refreshCursorBlinking() + function updatedCachedValues() { + var fontSize, diffMap; + currentSelectionStyles = getAppliedStyles(); + function noteChange(oldValue, newValue, id) { + if(oldValue !== newValue) { + if(diffMap === undefined) { + diffMap = {} + } + diffMap[id] = newValue } + return newValue + } + isBoldValue = noteChange(isBoldValue, currentSelectionStyles ? styleHelper.isBold(currentSelectionStyles) : false, "isBold"); + isItalicValue = noteChange(isItalicValue, currentSelectionStyles ? styleHelper.isItalic(currentSelectionStyles) : false, "isItalic"); + hasUnderlineValue = noteChange(hasUnderlineValue, currentSelectionStyles ? styleHelper.hasUnderline(currentSelectionStyles) : false, "hasUnderline"); + hasStrikeThroughValue = noteChange(hasStrikeThroughValue, currentSelectionStyles ? styleHelper.hasStrikeThrough(currentSelectionStyles) : false, "hasStrikeThrough"); + fontSize = currentSelectionStyles && getCommonValue(currentSelectionStyles, ["style:text-properties", "fo:font-size"]); + fontSizeValue = noteChange(fontSizeValue, fontSize && parseFloat(fontSize), "fontSize"); + fontNameValue = noteChange(fontNameValue, currentSelectionStyles && getCommonValue(currentSelectionStyles, ["style:text-properties", "style:font-name"]), "fontName"); + if(diffMap) { + eventNotifier.emit(gui.DirectTextStyler.textStylingChanged, diffMap) } } - function executeEnsureCaretVisible() { - var caret = getCaret(sessionController.getInputMemberId()); - scrollIntoViewScheduled = false; - if(caret) { - caret.ensureVisible() + function onCursorAdded(cursor) { + if(cursor.getMemberId() === inputMemberId) { + updatedCachedValues() } } - function scheduleCaretVisibilityCheck() { - var caret = getCaret(sessionController.getInputMemberId()); - if(caret) { - caret.handleUpdate(); - if(!scrollIntoViewScheduled) { - scrollIntoViewScheduled = true; - runtime.setTimeout(executeEnsureCaretVisible, 50) - } + function onCursorRemoved(memberId) { + if(memberId === inputMemberId) { + updatedCachedValues() } } - function ensureLocalCaretVisible(info) { - if(info.memberId === sessionController.getInputMemberId()) { - scheduleCaretVisibilityCheck() + function onCursorMoved(cursor) { + if(cursor.getMemberId() === inputMemberId) { + updatedCachedValues() } } - function focusLocalCaret() { - var caret = getCaret(sessionController.getInputMemberId()); - if(caret) { - caret.setFocus() + function onParagraphStyleModified() { + updatedCachedValues() + } + function onParagraphChanged(args) { + var cursor = odtDocument.getCursor(inputMemberId); + if(cursor && odtDocument.getParagraphElement(cursor.getNode()) === args.paragraphElement) { + updatedCachedValues() } } - function blurLocalCaret() { - var caret = getCaret(sessionController.getInputMemberId()); - if(caret) { - caret.removeFocus() + function toggle(predicate, toggleMethod) { + var cursor = odtDocument.getCursor(inputMemberId), appliedStyles; + if(!cursor) { + return false } + appliedStyles = styleHelper.getAppliedStyles(cursor.getSelectedRange()); + toggleMethod(!predicate(appliedStyles)); + return true } - function showLocalCaret() { - var caret = getCaret(sessionController.getInputMemberId()); - if(caret) { - caret.show() + function formatTextSelection(textProperties) { + var selection = odtDocument.getCursorSelection(inputMemberId), op, properties = {"style:text-properties":textProperties}; + if(selection.length !== 0) { + op = new ops.OpApplyDirectStyling; + op.init({memberid:inputMemberId, position:selection.position, length:selection.length, setProperties:properties}); + session.enqueue([op]) + }else { + directCursorStyleProperties = utils.mergeObjects(directCursorStyleProperties || {}, properties); + updatedCachedValues() + } + } + this.formatTextSelection = formatTextSelection; + function applyTextPropertyToSelection(propertyName, propertyValue) { + var textProperties = {}; + textProperties[propertyName] = propertyValue; + formatTextSelection(textProperties) + } + this.createCursorStyleOp = function(position, length) { + var styleOp = null; + if(directCursorStyleProperties) { + styleOp = new ops.OpApplyDirectStyling; + styleOp.init({memberid:inputMemberId, position:position, length:length, setProperties:directCursorStyleProperties}); + directCursorStyleProperties = null; + updatedCachedValues() + } + return styleOp + }; + function clearCursorStyle(op) { + var spec = op.spec(); + if(directCursorStyleProperties && spec.memberid === inputMemberId) { + if(spec.optype !== "SplitParagraph") { + directCursorStyleProperties = null; + updatedCachedValues() + } + } + } + function setBold(checked) { + var value = checked ? "bold" : "normal"; + applyTextPropertyToSelection("fo:font-weight", value) + } + this.setBold = setBold; + function setItalic(checked) { + var value = checked ? "italic" : "normal"; + applyTextPropertyToSelection("fo:font-style", value) + } + this.setItalic = setItalic; + function setHasUnderline(checked) { + var value = checked ? "solid" : "none"; + applyTextPropertyToSelection("style:text-underline-style", value) + } + this.setHasUnderline = setHasUnderline; + function setHasStrikethrough(checked) { + var value = checked ? "solid" : "none"; + applyTextPropertyToSelection("style:text-line-through-style", value) + } + this.setHasStrikethrough = setHasStrikethrough; + function setFontSize(value) { + applyTextPropertyToSelection("fo:font-size", value + "pt") + } + this.setFontSize = setFontSize; + function setFontName(value) { + applyTextPropertyToSelection("style:font-name", value) + } + this.setFontName = setFontName; + this.getAppliedStyles = function() { + return currentSelectionStyles + }; + this.toggleBold = toggle.bind(self, styleHelper.isBold, setBold); + this.toggleItalic = toggle.bind(self, styleHelper.isItalic, setItalic); + this.toggleUnderline = toggle.bind(self, styleHelper.hasUnderline, setHasUnderline); + this.toggleStrikethrough = toggle.bind(self, styleHelper.hasStrikeThrough, setHasStrikethrough); + this.isBold = function() { + return isBoldValue + }; + this.isItalic = function() { + return isItalicValue + }; + this.hasUnderline = function() { + return hasUnderlineValue + }; + this.hasStrikeThrough = function() { + return hasStrikeThroughValue + }; + this.fontSize = function() { + return fontSizeValue + }; + this.fontName = function() { + return fontNameValue + }; + this.subscribe = function(eventid, cb) { + eventNotifier.subscribe(eventid, cb) + }; + this.unsubscribe = function(eventid, cb) { + eventNotifier.unsubscribe(eventid, cb) + }; + this.destroy = function(callback) { + odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); + odtDocument.unsubscribe(ops.OdtDocument.signalOperationExecuted, clearCursorStyle); + callback() + }; + function init() { + odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); + odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); + odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); + odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); + odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); + odtDocument.subscribe(ops.OdtDocument.signalOperationExecuted, clearCursorStyle); + updatedCachedValues() + } + init() +}; +gui.DirectTextStyler.textStylingChanged = "textStyling/changed"; +(function() { + return gui.DirectTextStyler +})(); +runtime.loadClass("odf.Namespaces"); +runtime.loadClass("odf.ObjectNameGenerator"); +gui.ImageManager = function ImageManager(session, inputMemberId, objectNameGenerator) { + var cmPerPixel = 0.0264583333333334, fileExtensionByMimetype = {"image/gif":".gif", "image/jpeg":".jpg", "image/png":".png"}, textns = odf.Namespaces.textns, odtDocument = session.getOdtDocument(), formatting = odtDocument.getFormatting(), paragraphStyleToPageContentSizeMap = {}; + function createAddGraphicsStyleOp(name) { + var op = new ops.OpAddStyle; + op.init({memberid:inputMemberId, styleName:name, styleFamily:"graphic", isAutomaticStyle:false, setProperties:{"style:graphic-properties":{"text:anchor-type":"paragraph", "svg:x":"0cm", "svg:y":"0cm", "style:wrap":"dynamic", "style:number-wrapped-paragraphs":"no-limit", "style:wrap-contour":"false", "style:vertical-pos":"top", "style:vertical-rel":"paragraph", "style:horizontal-pos":"center", "style:horizontal-rel":"paragraph"}}}); + return op + } + function createAddFrameStyleOp(styleName, parentStyleName) { + var op = new ops.OpAddStyle; + op.init({memberid:inputMemberId, styleName:styleName, styleFamily:"graphic", isAutomaticStyle:true, setProperties:{"style:parent-style-name":parentStyleName, "style:graphic-properties":{"style:vertical-pos":"top", "style:vertical-rel":"baseline", "style:horizontal-pos":"center", "style:horizontal-rel":"paragraph", "fo:background-color":"transparent", "style:background-transparency":"100%", "style:shadow":"none", "style:mirror":"none", "fo:clip":"rect(0cm, 0cm, 0cm, 0cm)", "draw:luminance":"0%", + "draw:contrast":"0%", "draw:red":"0%", "draw:green":"0%", "draw:blue":"0%", "draw:gamma":"100%", "draw:color-inversion":"false", "draw:image-opacity":"100%", "draw:color-mode":"standard"}}}); + return op + } + function getFileExtension(mimetype) { + mimetype = mimetype.toLowerCase(); + return fileExtensionByMimetype.hasOwnProperty(mimetype) ? fileExtensionByMimetype[mimetype] : null + } + function insertImageInternal(mimetype, content, widthInCm, heightInCm) { + var graphicsStyleName = "Graphics", stylesElement = odtDocument.getOdfCanvas().odfContainer().rootElement.styles, fileExtension = getFileExtension(mimetype), fileName, graphicsStyleElement, frameStyleName, op, operations = []; + runtime.assert(fileExtension !== null, "Image type is not supported: " + mimetype); + fileName = "Pictures/" + objectNameGenerator.generateImageName() + fileExtension; + op = new ops.OpSetBlob; + op.init({memberid:inputMemberId, filename:fileName, mimetype:mimetype, content:content}); + operations.push(op); + graphicsStyleElement = formatting.getStyleElement(graphicsStyleName, "graphic", [stylesElement]); + if(!graphicsStyleElement) { + op = createAddGraphicsStyleOp(graphicsStyleName); + operations.push(op) + } + frameStyleName = objectNameGenerator.generateStyleName(); + op = createAddFrameStyleOp(frameStyleName, graphicsStyleName); + operations.push(op); + op = new ops.OpInsertImage; + op.init({memberid:inputMemberId, position:odtDocument.getCursorPosition(inputMemberId), filename:fileName, frameWidth:widthInCm + "cm", frameHeight:heightInCm + "cm", frameStyleName:frameStyleName, frameName:objectNameGenerator.generateFrameName()}); + operations.push(op); + session.enqueue(operations) + } + function trimmedSize(originalSize, pageContentSize) { + var widthRatio = 1, heightRatio = 1, ratio; + if(originalSize.width > pageContentSize.width) { + widthRatio = pageContentSize.width / originalSize.width } - } - function hideLocalCaret() { - var caret = getCaret(sessionController.getInputMemberId()); - if(caret) { - caret.hide() + if(originalSize.height > pageContentSize.height) { + heightRatio = pageContentSize.height / originalSize.height } + ratio = Math.min(widthRatio, heightRatio); + return{width:originalSize.width * ratio, height:originalSize.height * ratio} } - this.registerCursor = function(cursor, caretAvatarInitiallyVisible, blinkOnRangeSelect) { - var memberid = cursor.getMemberId(), caret = new gui.Caret(cursor, caretAvatarInitiallyVisible, blinkOnRangeSelect); - carets[memberid] = caret; - if(memberid === sessionController.getInputMemberId()) { - runtime.log("Starting to track input on new cursor of " + memberid); - cursor.handleUpdate = scheduleCaretVisibilityCheck; - getCanvasElement().setAttribute("tabindex", -1); - sessionController.getEventManager().focus() - }else { - cursor.handleUpdate = caret.handleUpdate + this.insertImage = function(mimetype, content, widthInPx, heightInPx) { + var paragraphElement, styleName, pageContentSize, originalSize, newSize; + runtime.assert(widthInPx > 0 && heightInPx > 0, "Both width and height of the image should be greater than 0px."); + paragraphElement = odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode()); + styleName = paragraphElement.getAttributeNS(textns, "style-name"); + if(!paragraphStyleToPageContentSizeMap.hasOwnProperty(styleName)) { + paragraphStyleToPageContentSizeMap[styleName] = formatting.getContentSize(styleName, "paragraph") } - return caret - }; - this.getCaret = getCaret; - this.getCarets = getCarets; - this.destroy = function(callback) { - var odtDocument = sessionController.getSession().getOdtDocument(), eventManager = sessionController.getEventManager(), caretArray = getCarets(); - odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, ensureLocalCaretVisible); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, refreshLocalCaretBlinking); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, removeCaret); - eventManager.unsubscribe("focus", focusLocalCaret); - eventManager.unsubscribe("blur", blurLocalCaret); - window.removeEventListener("focus", showLocalCaret, false); - window.removeEventListener("blur", hideLocalCaret, false); - (function destroyCaret(i, err) { - if(err) { - callback(err) - }else { - if(i < caretArray.length) { - caretArray[i].destroy(function(err) { - destroyCaret(i + 1, err) - }) - }else { - callback() - } - } - })(0, undefined); - carets = {} - }; - function init() { - var odtDocument = sessionController.getSession().getOdtDocument(), eventManager = sessionController.getEventManager(); - odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, ensureLocalCaretVisible); - odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, refreshLocalCaretBlinking); - odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, removeCaret); - eventManager.subscribe("focus", focusLocalCaret); - eventManager.subscribe("blur", blurLocalCaret); - window.addEventListener("focus", showLocalCaret, false); - window.addEventListener("blur", hideLocalCaret, false) + pageContentSize = paragraphStyleToPageContentSizeMap[styleName]; + originalSize = {width:widthInPx * cmPerPixel, height:heightInPx * cmPerPixel}; + newSize = trimmedSize(originalSize, pageContentSize); + insertImageInternal(mimetype, content, newSize.width, newSize.height) } - init() -}; -/* - - Copyright (C) 2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -gui.UndoManager = function UndoManager() { -}; -gui.UndoManager.prototype.subscribe = function(signal, callback) { -}; -gui.UndoManager.prototype.unsubscribe = function(signal, callback) { -}; -gui.UndoManager.prototype.setOdtDocument = function(newDocument) { -}; -gui.UndoManager.prototype.saveInitialState = function() { -}; -gui.UndoManager.prototype.resetInitialState = function() { -}; -gui.UndoManager.prototype.setPlaybackFunction = function(playback_func) { -}; -gui.UndoManager.prototype.hasUndoStates = function() { -}; -gui.UndoManager.prototype.hasRedoStates = function() { -}; -gui.UndoManager.prototype.moveForward = function(states) { -}; -gui.UndoManager.prototype.moveBackward = function(states) { }; -gui.UndoManager.prototype.onOperationExecuted = function(op) { -}; -gui.UndoManager.signalUndoStackChanged = "undoStackChanged"; -gui.UndoManager.signalUndoStateCreated = "undoStateCreated"; -gui.UndoManager.signalUndoStateModified = "undoStateModified"; -(function() { - return gui.UndoManager -})(); /* Copyright (C) 2013 KO GmbH @@ -16888,651 +16923,818 @@ gui.UndoManager.signalUndoStateModified = "undoStateModified"; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -gui.UndoStateRules = function UndoStateRules() { - function getOpType(op) { - return op.spec().optype +runtime.loadClass("core.PositionFilter"); +gui.TextManipulator = function TextManipulator(session, inputMemberId, directStyleOp) { + var odtDocument = session.getOdtDocument(), FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; + function createOpRemoveSelection(selection) { + var op = new ops.OpRemoveText; + op.init({memberid:inputMemberId, position:selection.position, length:selection.length}); + return op } - this.getOpType = getOpType; - function getOpPosition(op) { - return op.spec().position + function toForwardSelection(selection) { + if(selection.length < 0) { + selection.position += selection.length; + selection.length = -selection.length + } + return selection } - function isEditOperation(op) { - return op.isEdit + this.enqueueParagraphSplittingOps = function() { + var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op, operations = []; + if(selection.length > 0) { + op = createOpRemoveSelection(selection); + operations.push(op) + } + op = new ops.OpSplitParagraph; + op.init({memberid:inputMemberId, position:selection.position}); + operations.push(op); + session.enqueue(operations); + return true + }; + function hasPositionInDirection(cursorNode, forward) { + var rootConstrainedFilter = new core.PositionFilterChain, iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootElement(cursorNode)), nextPosition = (forward ? iterator.nextPosition : iterator.previousPosition); + rootConstrainedFilter.addFilter("BaseFilter", odtDocument.getPositionFilter()); + rootConstrainedFilter.addFilter("RootFilter", odtDocument.createRootFilter(inputMemberId)); + iterator.setUnfilteredPosition(cursorNode, 0); + while(nextPosition()) { + if(rootConstrainedFilter.acceptPosition(iterator) === FILTER_ACCEPT) { + return true + } + } + return false } - this.isEditOperation = isEditOperation; - function canAggregateOperation(optype) { - switch(optype) { - case "RemoveText": - ; - case "InsertText": - return true; - default: - return false + this.removeTextByBackspaceKey = function() { + var cursor = odtDocument.getCursor(inputMemberId), selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op = null; + if(selection.length === 0) { + if(hasPositionInDirection(cursor.getNode(), false)) { + op = new ops.OpRemoveText; + op.init({memberid:inputMemberId, position:selection.position - 1, length:1}); + session.enqueue([op]) + } + }else { + op = createOpRemoveSelection(selection); + session.enqueue([op]) + } + return op !== null + }; + this.removeTextByDeleteKey = function() { + var cursor = odtDocument.getCursor(inputMemberId), selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op = null; + if(selection.length === 0) { + if(hasPositionInDirection(cursor.getNode(), true)) { + op = new ops.OpRemoveText; + op.init({memberid:inputMemberId, position:selection.position, length:1}); + session.enqueue([op]) + } + }else { + op = createOpRemoveSelection(selection); + session.enqueue([op]) + } + return op !== null + }; + this.removeCurrentSelection = function() { + var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op; + if(selection.length !== 0) { + op = createOpRemoveSelection(selection); + session.enqueue([op]) + } + return true + }; + function insertText(text) { + var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op, stylingOp, operations = []; + if(selection.length > 0) { + op = createOpRemoveSelection(selection); + operations.push(op) + } + op = new ops.OpInsertText; + op.init({memberid:inputMemberId, position:selection.position, text:text}); + operations.push(op); + if(directStyleOp) { + stylingOp = directStyleOp(selection.position, text.length); + if(stylingOp) { + operations.push(stylingOp) + } + } + session.enqueue(operations) + } + this.insertText = insertText +}; +(function() { + return gui.TextManipulator +})(); +runtime.loadClass("core.DomUtils"); +runtime.loadClass("core.Async"); +runtime.loadClass("core.ScheduledTask"); +runtime.loadClass("odf.OdfUtils"); +runtime.loadClass("odf.ObjectNameGenerator"); +runtime.loadClass("ops.OdtCursor"); +runtime.loadClass("ops.OpAddCursor"); +runtime.loadClass("ops.OpRemoveCursor"); +runtime.loadClass("gui.Clipboard"); +runtime.loadClass("gui.DirectTextStyler"); +runtime.loadClass("gui.DirectParagraphStyler"); +runtime.loadClass("gui.KeyboardHandler"); +runtime.loadClass("gui.ImageManager"); +runtime.loadClass("gui.ImageSelector"); +runtime.loadClass("gui.TextManipulator"); +runtime.loadClass("gui.AnnotationController"); +runtime.loadClass("gui.EventManager"); +runtime.loadClass("gui.PlainTextPasteboard"); +gui.SessionController = function() { + var FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; + gui.SessionController = function SessionController(session, inputMemberId, shadowCursor, args) { + var window = (runtime.getWindow()), odtDocument = session.getOdtDocument(), async = new core.Async, domUtils = new core.DomUtils, odfUtils = new odf.OdfUtils, clipboard = new gui.Clipboard, keyDownHandler = new gui.KeyboardHandler, keyPressHandler = new gui.KeyboardHandler, keyboardMovementsFilter = new core.PositionFilterChain, baseFilter = odtDocument.getPositionFilter(), clickStartedWithinContainer = false, objectNameGenerator = new odf.ObjectNameGenerator(odtDocument.getOdfCanvas().odfContainer(), + inputMemberId), isMouseMoved = false, mouseDownRootFilter = null, undoManager = null, eventManager = new gui.EventManager(odtDocument), annotationController = new gui.AnnotationController(session, inputMemberId), directTextStyler = new gui.DirectTextStyler(session, inputMemberId), directParagraphStyler = args && args.directParagraphStylingEnabled ? new gui.DirectParagraphStyler(session, inputMemberId, objectNameGenerator) : null, createCursorStyleOp = (directTextStyler.createCursorStyleOp), textManipulator = + new gui.TextManipulator(session, inputMemberId, createCursorStyleOp), imageManager = new gui.ImageManager(session, inputMemberId, objectNameGenerator), imageSelector = new gui.ImageSelector(odtDocument.getOdfCanvas()), shadowCursorIterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), drawShadowCursorTask, suppressFocusEvent = false, redrawRegionSelectionTask, pasteHandler = new gui.PlainTextPasteboard(odtDocument, inputMemberId), clickCount = 0; + 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("RootFilter", odtDocument.createRootFilter(inputMemberId)); + function getTarget(e) { + return e.target || e.srcElement } - } - function isSameDirectionOfTravel(recentEditOps, thisOp) { - var existing1 = getOpPosition(recentEditOps[recentEditOps.length - 2]), existing2 = getOpPosition(recentEditOps[recentEditOps.length - 1]), thisPos = getOpPosition(thisOp), direction = existing2 - existing1; - return existing2 === thisPos - direction - } - function isContinuousOperation(recentEditOps, thisOp) { - var optype = getOpType(thisOp); - if(canAggregateOperation(optype) && optype === getOpType(recentEditOps[0])) { - if(recentEditOps.length === 1) { - return true + function cancelEvent(event) { + if(event.preventDefault) { + event.preventDefault() + }else { + event.returnValue = false } - if(isSameDirectionOfTravel(recentEditOps, thisOp)) { - return true + } + function dummyHandler(e) { + cancelEvent(e) + } + function createOpMoveCursor(position, length, selectionType) { + var op = new ops.OpMoveCursor; + op.init({memberid:inputMemberId, position:position, length:length || 0, selectionType:selectionType}); + return op + } + function caretPositionFromPoint(x, y) { + var doc = odtDocument.getDOM(), c, result = null; + if(doc.caretRangeFromPoint) { + c = doc.caretRangeFromPoint(x, y); + result = {container:c.startContainer, offset:c.startOffset} + }else { + if(doc.caretPositionFromPoint) { + c = doc.caretPositionFromPoint(x, y); + if(c && c.offsetNode) { + result = {container:c.offsetNode, offset:c.offset} + } + } } + return result } - return false - } - function isPartOfOperationSet(operation, lastOperations) { - if(isEditOperation(operation)) { - if(lastOperations.length === 0) { - return true + function expandToWordBoundaries(range) { + var alphaNumeric = /[A-Za-z0-9]/, iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), currentNode, c; + iterator.setUnfilteredPosition((range.startContainer), range.startOffset); + while(iterator.previousPosition()) { + currentNode = iterator.getCurrentNode(); + if(currentNode.nodeType === Node.TEXT_NODE) { + c = currentNode.data[iterator.unfilteredDomOffset()]; + if(!alphaNumeric.test(c)) { + break + } + }else { + if(!odfUtils.isTextSpan(currentNode)) { + break + } + } + range.setStart(iterator.container(), iterator.unfilteredDomOffset()) } - return isEditOperation(lastOperations[lastOperations.length - 1]) && isContinuousOperation(lastOperations.filter(isEditOperation), operation) + iterator.setUnfilteredPosition((range.endContainer), range.endOffset); + do { + currentNode = iterator.getCurrentNode(); + if(currentNode.nodeType === Node.TEXT_NODE) { + c = currentNode.data[iterator.unfilteredDomOffset()]; + if(!alphaNumeric.test(c)) { + break + } + }else { + if(!odfUtils.isTextSpan(currentNode)) { + break + } + } + }while(iterator.nextPosition()); + range.setEnd(iterator.container(), iterator.unfilteredDomOffset()) } - return true - } - this.isPartOfOperationSet = isPartOfOperationSet -}; -/* - - Copyright (C) 2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("core.DomUtils"); -runtime.loadClass("gui.UndoManager"); -runtime.loadClass("gui.UndoStateRules"); -gui.TrivialUndoManager = function TrivialUndoManager(defaultRules) { - var self = this, cursorns = "urn:webodf:names:cursor", domUtils = new core.DomUtils, initialDoc, initialState = [], playFunc, odtDocument, currentUndoState = [], undoStates = [], redoStates = [], eventNotifier = new core.EventNotifier([gui.UndoManager.signalUndoStackChanged, gui.UndoManager.signalUndoStateCreated, gui.UndoManager.signalUndoStateModified, gui.TrivialUndoManager.signalDocumentRootReplaced]), undoRules = defaultRules || new gui.UndoStateRules; - function emitStackChange() { - eventNotifier.emit(gui.UndoManager.signalUndoStackChanged, {undoAvailable:self.hasUndoStates(), redoAvailable:self.hasRedoStates()}) - } - function mostRecentUndoState() { - return undoStates[undoStates.length - 1] - } - function completeCurrentUndoState() { - if(currentUndoState !== initialState && currentUndoState !== mostRecentUndoState()) { - undoStates.push(currentUndoState) + function expandToParagraphBoundaries(range) { + var startParagraph = odtDocument.getParagraphElement(range.startContainer), endParagraph = odtDocument.getParagraphElement(range.endContainer); + if(startParagraph) { + range.setStart(startParagraph, 0) + } + if(endParagraph) { + if(odfUtils.isParagraph(range.endContainer) && range.endOffset === 0) { + range.setEndBefore(endParagraph) + }else { + range.setEnd(endParagraph, endParagraph.childNodes.length) + } + } } - } - function removeNode(node) { - var sibling = node.previousSibling || node.nextSibling; - node.parentNode.removeChild(node); - domUtils.normalizeTextNodes(sibling) - } - function removeCursors(root) { - domUtils.getElementsByTagNameNS(root, cursorns, "cursor").forEach(removeNode); - domUtils.getElementsByTagNameNS(root, cursorns, "anchor").forEach(removeNode) - } - function values(obj) { - return Object.keys(obj).map(function(key) { - return obj[key] - }) - } - function extractCursorStates(undoStates) { - var addCursor = {}, moveCursor = {}, requiredAddOps = {}, remainingAddOps, operations = undoStates.pop(); - odtDocument.getCursors().forEach(function(cursor) { - requiredAddOps[cursor.getMemberId()] = true - }); - remainingAddOps = Object.keys(requiredAddOps).length; - function processOp(op) { - var spec = op.spec(); - if(!requiredAddOps[spec.memberid]) { + function selectImage(frameNode) { + var stepsToAnchor = odtDocument.getDistanceFromCursor(inputMemberId, frameNode, 0), stepsToFocus = stepsToAnchor !== null ? stepsToAnchor + 1 : null, oldPosition, op; + if(stepsToFocus || stepsToAnchor) { + oldPosition = odtDocument.getCursorPosition(inputMemberId); + op = createOpMoveCursor(oldPosition + stepsToAnchor, stepsToFocus - stepsToAnchor, ops.OdtCursor.RegionSelection); + session.enqueue([op]) + } + eventManager.focus() + } + function selectionToRange(selection) { + var hasForwardSelection = domUtils.comparePoints((selection.anchorNode), selection.anchorOffset, (selection.focusNode), selection.focusOffset) >= 0, range = selection.focusNode.ownerDocument.createRange(); + if(hasForwardSelection) { + range.setStart(selection.anchorNode, selection.anchorOffset); + range.setEnd(selection.focusNode, selection.focusOffset) + }else { + range.setStart(selection.focusNode, selection.focusOffset); + range.setEnd(selection.anchorNode, selection.anchorOffset) + } + return{range:range, hasForwardSelection:hasForwardSelection} + } + function rangeToSelection(range, hasForwardSelection) { + if(hasForwardSelection) { + return{anchorNode:(range.startContainer), anchorOffset:range.startOffset, focusNode:(range.endContainer), focusOffset:range.endOffset} + } + return{anchorNode:(range.endContainer), anchorOffset:range.endOffset, focusNode:(range.startContainer), focusOffset:range.startOffset} + } + function constrain(lookup) { + return function(originalNode) { + var originalContainer = lookup(originalNode); + return function(step, node) { + return lookup(node) === originalContainer + } + } + } + function selectRange(range, hasForwardSelection, clickCount) { + var canvasElement = odtDocument.getOdfCanvas().getElement(), validSelection, startInsideCanvas, endInsideCanvas, existingSelection, newSelection, op; + startInsideCanvas = domUtils.containsNode(canvasElement, range.startContainer); + endInsideCanvas = domUtils.containsNode(canvasElement, range.endContainer); + if(!startInsideCanvas && !endInsideCanvas) { return } - switch(spec.optype) { - case "AddCursor": - if(!addCursor[spec.memberid]) { - addCursor[spec.memberid] = op; - delete requiredAddOps[spec.memberid]; - remainingAddOps -= 1 - } - break; - case "MoveCursor": - if(!moveCursor[spec.memberid]) { - moveCursor[spec.memberid] = op + if(startInsideCanvas && endInsideCanvas) { + if(clickCount === 2) { + expandToWordBoundaries(range) + }else { + if(clickCount >= 3) { + expandToParagraphBoundaries(range) } - break + } + } + validSelection = rangeToSelection(range, hasForwardSelection); + newSelection = odtDocument.convertDomToCursorRange(validSelection, constrain(odfUtils.getParagraphElement)); + existingSelection = odtDocument.getCursorSelection(inputMemberId); + if(newSelection.position !== existingSelection.position || newSelection.length !== existingSelection.length) { + op = createOpMoveCursor(newSelection.position, newSelection.length, ops.OdtCursor.RangeSelection); + session.enqueue([op]) + } + eventManager.focus() + } + this.selectRange = selectRange; + function extendCursorByAdjustment(lengthAdjust) { + var selection = odtDocument.getCursorSelection(inputMemberId), stepCounter = odtDocument.getCursor(inputMemberId).getStepCounter(), newLength; + if(lengthAdjust !== 0) { + lengthAdjust = lengthAdjust > 0 ? stepCounter.convertForwardStepsBetweenFilters(lengthAdjust, keyboardMovementsFilter, baseFilter) : -stepCounter.convertBackwardStepsBetweenFilters(-lengthAdjust, keyboardMovementsFilter, baseFilter); + newLength = selection.length + lengthAdjust; + session.enqueue([createOpMoveCursor(selection.position, newLength)]) + } + } + function moveCursorByAdjustment(positionAdjust) { + var position = odtDocument.getCursorPosition(inputMemberId), stepCounter = odtDocument.getCursor(inputMemberId).getStepCounter(); + if(positionAdjust !== 0) { + positionAdjust = positionAdjust > 0 ? stepCounter.convertForwardStepsBetweenFilters(positionAdjust, keyboardMovementsFilter, baseFilter) : -stepCounter.convertBackwardStepsBetweenFilters(-positionAdjust, keyboardMovementsFilter, baseFilter); + position = position + positionAdjust; + session.enqueue([createOpMoveCursor(position, 0)]) + } + } + function moveCursorToLeft() { + moveCursorByAdjustment(-1); + return true + } + this.moveCursorToLeft = moveCursorToLeft; + function moveCursorToRight() { + moveCursorByAdjustment(1); + return true + } + function extendSelectionToLeft() { + extendCursorByAdjustment(-1); + return true + } + function extendSelectionToRight() { + extendCursorByAdjustment(1); + return true + } + function moveCursorByLine(direction, extend) { + var paragraphNode = odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode()), steps; + runtime.assert(Boolean(paragraphNode), "SessionController: Cursor outside paragraph"); + steps = odtDocument.getCursor(inputMemberId).getStepCounter().countLinesSteps(direction, keyboardMovementsFilter); + if(extend) { + extendCursorByAdjustment(steps) + }else { + moveCursorByAdjustment(steps) } } - while(operations && remainingAddOps > 0) { - operations.reverse(); - operations.forEach(processOp); - operations = undoStates.pop() + function moveCursorUp() { + moveCursorByLine(-1, false); + return true + } + function moveCursorDown() { + moveCursorByLine(1, false); + return true + } + function extendSelectionUp() { + moveCursorByLine(-1, true); + return true } - return values(addCursor).concat(values(moveCursor)) - } - this.subscribe = function(signal, callback) { - eventNotifier.subscribe(signal, callback) - }; - this.unsubscribe = function(signal, callback) { - eventNotifier.unsubscribe(signal, callback) - }; - this.hasUndoStates = function() { - return undoStates.length > 0 - }; - this.hasRedoStates = function() { - return redoStates.length > 0 - }; - this.setOdtDocument = function(newDocument) { - odtDocument = newDocument - }; - this.resetInitialState = function() { - undoStates.length = 0; - redoStates.length = 0; - initialState.length = 0; - currentUndoState.length = 0; - initialDoc = null; - emitStackChange() - }; - this.saveInitialState = function() { - var odfContainer = odtDocument.getOdfCanvas().odfContainer(), annotationViewManager = odtDocument.getOdfCanvas().getAnnotationViewManager(); - if(annotationViewManager) { - annotationViewManager.forgetAnnotations() + function extendSelectionDown() { + moveCursorByLine(1, true); + return true } - initialDoc = odfContainer.rootElement.cloneNode(true); - odtDocument.getOdfCanvas().refreshAnnotations(); - removeCursors(initialDoc); - completeCurrentUndoState(); - undoStates.unshift(initialState); - currentUndoState = initialState = extractCursorStates(undoStates); - undoStates.length = 0; - redoStates.length = 0; - emitStackChange() - }; - this.setPlaybackFunction = function(playback_func) { - playFunc = playback_func - }; - this.onOperationExecuted = function(op) { - redoStates.length = 0; - if(undoRules.isEditOperation(op) && currentUndoState === initialState || !undoRules.isPartOfOperationSet(op, currentUndoState)) { - completeCurrentUndoState(); - currentUndoState = [op]; - undoStates.push(currentUndoState); - eventNotifier.emit(gui.UndoManager.signalUndoStateCreated, {operations:currentUndoState}); - emitStackChange() - }else { - currentUndoState.push(op); - eventNotifier.emit(gui.UndoManager.signalUndoStateModified, {operations:currentUndoState}) + function moveCursorToLineBoundary(direction, extend) { + var steps = odtDocument.getCursor(inputMemberId).getStepCounter().countStepsToLineBoundary(direction, keyboardMovementsFilter); + if(extend) { + extendCursorByAdjustment(steps) + }else { + moveCursorByAdjustment(steps) + } } - }; - this.moveForward = function(states) { - var moved = 0, redoOperations; - while(states && redoStates.length) { - redoOperations = redoStates.pop(); - undoStates.push(redoOperations); - redoOperations.forEach(playFunc); - states -= 1; - moved += 1 + function moveCursorToLineStart() { + moveCursorToLineBoundary(-1, false); + return true } - if(moved) { - currentUndoState = mostRecentUndoState(); - emitStackChange() + function moveCursorToLineEnd() { + moveCursorToLineBoundary(1, false); + return true } - return moved - }; - this.moveBackward = function(states) { - var odfCanvas = odtDocument.getOdfCanvas(), odfContainer = odfCanvas.odfContainer(), moved = 0; - while(states && undoStates.length) { - redoStates.push(undoStates.pop()); - states -= 1; - moved += 1 + function extendSelectionToLineStart() { + moveCursorToLineBoundary(-1, true); + return true } - if(moved) { - odfContainer.setRootElement(initialDoc.cloneNode(true)); - odfCanvas.setOdfContainer(odfContainer, true); - eventNotifier.emit(gui.TrivialUndoManager.signalDocumentRootReplaced, {}); - odtDocument.getCursors().forEach(function(cursor) { - odtDocument.removeCursor(cursor.getMemberId()) - }); - initialState.forEach(playFunc); - undoStates.forEach(function(ops) { - ops.forEach(playFunc) - }); - odfCanvas.refreshCSS(); - currentUndoState = mostRecentUndoState() || initialState; - emitStackChange() + function extendSelectionToLineEnd() { + moveCursorToLineBoundary(1, true); + return true } - return moved - } -}; -gui.TrivialUndoManager.signalDocumentRootReplaced = "documentRootReplaced"; -(function() { - return gui.TrivialUndoManager -})(); -runtime.loadClass("core.DomUtils"); -runtime.loadClass("odf.OdfUtils"); -runtime.loadClass("odf.OdfNodeFilter"); -runtime.loadClass("gui.SelectionMover"); -gui.SelectionView = function SelectionView(cursor) { - var odtDocument = cursor.getOdtDocument(), documentRoot, root, doc = odtDocument.getDOM(), overlayTop = doc.createElement("div"), overlayMiddle = doc.createElement("div"), overlayBottom = doc.createElement("div"), odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils, isVisible = true, positionIterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), FILTER_ACCEPT = NodeFilter.FILTER_ACCEPT, FILTER_REJECT = NodeFilter.FILTER_REJECT; - function addOverlays() { - var newDocumentRoot = odtDocument.getRootNode(); - if(documentRoot !== newDocumentRoot) { - documentRoot = newDocumentRoot; - root = documentRoot.parentNode.parentNode.parentNode; - root.appendChild(overlayTop); - root.appendChild(overlayMiddle); - root.appendChild(overlayBottom) + function extendSelectionToParagraphStart() { + var paragraphNode = (odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode())), iterator, node, steps; + runtime.assert(Boolean(paragraphNode), "SessionController: Cursor outside paragraph"); + steps = odtDocument.getDistanceFromCursor(inputMemberId, paragraphNode, 0); + iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()); + iterator.setUnfilteredPosition(paragraphNode, 0); + while(steps === 0 && iterator.previousPosition()) { + node = iterator.getCurrentNode(); + if(odfUtils.isParagraph(node)) { + steps = odtDocument.getDistanceFromCursor(inputMemberId, node, 0) + } + } + extendCursorByAdjustment(steps); + return true } - } - function setRect(div, rect) { - div.style.left = rect.left + "px"; - div.style.top = rect.top + "px"; - div.style.width = rect.width + "px"; - div.style.height = rect.height + "px" - } - function showOverlays(choice) { - var display; - isVisible = choice; - display = choice === true ? "block" : "none"; - overlayTop.style.display = overlayMiddle.style.display = overlayBottom.style.display = display - } - function translateRect(rect) { - var rootRect = domUtils.getBoundingClientRect(root), zoomLevel = odtDocument.getOdfCanvas().getZoomLevel(), resultRect = {}; - resultRect.top = domUtils.adaptRangeDifferenceToZoomLevel(rect.top - rootRect.top, zoomLevel); - resultRect.left = domUtils.adaptRangeDifferenceToZoomLevel(rect.left - rootRect.left, zoomLevel); - resultRect.bottom = domUtils.adaptRangeDifferenceToZoomLevel(rect.bottom - rootRect.top, zoomLevel); - resultRect.right = domUtils.adaptRangeDifferenceToZoomLevel(rect.right - rootRect.left, zoomLevel); - resultRect.width = domUtils.adaptRangeDifferenceToZoomLevel(rect.width, zoomLevel); - resultRect.height = domUtils.adaptRangeDifferenceToZoomLevel(rect.height, zoomLevel); - return resultRect - } - function isRangeVisible(range) { - var bcr = range.getBoundingClientRect(); - return Boolean(bcr && bcr.height !== 0) - } - function lastVisibleRect(range, nodes) { - var nextNodeIndex = nodes.length - 1, node = nodes[nextNodeIndex], startOffset = range.endContainer === node ? range.endOffset : node.length || node.childNodes.length, endOffset = startOffset; - range.setStart(node, startOffset); - range.setEnd(node, endOffset); - while(!isRangeVisible(range)) { - if(node.nodeType === Node.ELEMENT_NODE && startOffset > 0) { - startOffset = 0 + function extendSelectionToParagraphEnd() { + var paragraphNode = (odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode())), iterator, node, steps; + runtime.assert(Boolean(paragraphNode), "SessionController: Cursor outside paragraph"); + iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()); + iterator.moveToEndOfNode(paragraphNode); + steps = odtDocument.getDistanceFromCursor(inputMemberId, iterator.container(), iterator.unfilteredDomOffset()); + while(steps === 0 && iterator.nextPosition()) { + node = iterator.getCurrentNode(); + if(odfUtils.isParagraph(node)) { + iterator.moveToEndOfNode(node); + steps = odtDocument.getDistanceFromCursor(inputMemberId, iterator.container(), iterator.unfilteredDomOffset()) + } + } + extendCursorByAdjustment(steps); + return true + } + function moveCursorToDocumentBoundary(direction, extend) { + var iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), steps; + if(direction > 0) { + iterator.moveToEnd() + } + steps = odtDocument.getDistanceFromCursor(inputMemberId, iterator.container(), iterator.unfilteredDomOffset()); + if(extend) { + extendCursorByAdjustment(steps) }else { - if(node.nodeType === Node.TEXT_NODE && startOffset > 0) { - startOffset -= 1 - }else { - if(nodes[nextNodeIndex]) { - node = nodes[nextNodeIndex]; - nextNodeIndex -= 1; - startOffset = endOffset = node.length || node.childNodes.length + moveCursorByAdjustment(steps) + } + } + this.moveCursorToDocumentBoundary = moveCursorToDocumentBoundary; + function moveCursorToDocumentStart() { + moveCursorToDocumentBoundary(-1, false); + return true + } + function moveCursorToDocumentEnd() { + moveCursorToDocumentBoundary(1, false); + return true + } + function extendSelectionToDocumentStart() { + moveCursorToDocumentBoundary(-1, true); + return true + } + function extendSelectionToDocumentEnd() { + moveCursorToDocumentBoundary(1, true); + return true + } + function extendSelectionToEntireDocument() { + var rootNode = odtDocument.getRootNode(), lastWalkableStep = odtDocument.convertDomPointToCursorStep(rootNode, rootNode.childNodes.length); + session.enqueue([createOpMoveCursor(0, lastWalkableStep)]); + return true + } + this.extendSelectionToEntireDocument = extendSelectionToEntireDocument; + function maintainCursorSelection() { + var cursor = odtDocument.getCursor(inputMemberId), selection = window.getSelection(), imageElement, range; + if(cursor) { + imageSelector.clearSelection(); + if(cursor.getSelectionType() === ops.OdtCursor.RegionSelection) { + range = cursor.getSelectedRange(); + imageElement = odfUtils.getImageElements(range)[0]; + if(imageElement) { + imageSelector.select((imageElement.parentNode)) + } + } + if(eventManager.hasFocus()) { + range = cursor.getSelectedRange(); + if(selection.extend) { + if(cursor.hasForwardSelection()) { + selection.collapse(range.startContainer, range.startOffset); + selection.extend(range.endContainer, range.endOffset) + }else { + selection.collapse(range.endContainer, range.endOffset); + selection.extend(range.startContainer, range.startOffset) + } }else { - return false + suppressFocusEvent = true; + selection.removeAllRanges(); + selection.addRange(range.cloneRange()); + (odtDocument.getOdfCanvas().getElement()).setActive(); + runtime.setTimeout(function() { + suppressFocusEvent = false + }, 0) } } - } - range.setStart(node, startOffset); - range.setEnd(node, endOffset) - } - return true - } - function firstVisibleRect(range, nodes) { - var nextNodeIndex = 0, node = nodes[nextNodeIndex], startOffset = range.startContainer === node ? range.startOffset : 0, endOffset = startOffset; - range.setStart(node, startOffset); - range.setEnd(node, endOffset); - while(!isRangeVisible(range)) { - if(node.nodeType === Node.ELEMENT_NODE && endOffset < node.childNodes.length) { - endOffset = node.childNodes.length }else { - if(node.nodeType === Node.TEXT_NODE && endOffset < node.length) { - endOffset += 1 - }else { - if(nodes[nextNodeIndex]) { - node = nodes[nextNodeIndex]; - nextNodeIndex += 1; - startOffset = endOffset = 0 - }else { - return false - } - } + imageSelector.clearSelection() } - range.setStart(node, startOffset); - range.setEnd(node, endOffset) - } - return true - } - function getExtremeRanges(range) { - var nodes = odfUtils.getTextElements(range, true, false), firstRange = (range.cloneRange()), lastRange = (range.cloneRange()), fillerRange = range.cloneRange(); - if(!nodes.length) { - return null } - if(!firstVisibleRect(firstRange, nodes)) { - return null + function delayedMaintainCursor() { + if(suppressFocusEvent === false) { + runtime.setTimeout(maintainCursorSelection, 0) + } } - if(!lastVisibleRect(lastRange, nodes)) { + function stringFromKeyPress(event) { + if(event.which === null || event.which === undefined) { + return String.fromCharCode(event.keyCode) + } + if(event.which !== 0 && event.charCode !== 0) { + return String.fromCharCode(event.which) + } return null } - fillerRange.setStart(firstRange.startContainer, firstRange.startOffset); - fillerRange.setEnd(lastRange.endContainer, lastRange.endOffset); - return{firstRange:firstRange, lastRange:lastRange, fillerRange:fillerRange} - } - function getBoundingRect(rect1, rect2) { - var resultRect = {}; - resultRect.top = Math.min(rect1.top, rect2.top); - resultRect.left = Math.min(rect1.left, rect2.left); - resultRect.right = Math.max(rect1.right, rect2.right); - resultRect.bottom = Math.max(rect1.bottom, rect2.bottom); - resultRect.width = resultRect.right - resultRect.left; - resultRect.height = resultRect.bottom - resultRect.top; - return resultRect - } - function checkAndGrowOrCreateRect(originalRect, newRect) { - if(newRect && (newRect.width > 0 && newRect.height > 0)) { - if(!originalRect) { - originalRect = newRect + function handleCut(e) { + var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); + if(selectedRange.collapsed) { + return + } + if(clipboard.setDataFromRange(e, selectedRange)) { + textManipulator.removeCurrentSelection() }else { - originalRect = getBoundingRect(originalRect, newRect) + runtime.log("Cut operation failed") } } - return originalRect - } - function getFillerRect(fillerRange) { - var containerNode = fillerRange.commonAncestorContainer, firstNode = fillerRange.startContainer, lastNode = fillerRange.endContainer, firstOffset = fillerRange.startOffset, lastOffset = fillerRange.endOffset, currentNode, lastMeasuredNode, firstSibling, lastSibling, grownRect = null, currentRect, range = doc.createRange(), rootFilter, odfNodeFilter = new odf.OdfNodeFilter, treeWalker; - function acceptNode(node) { - positionIterator.setUnfilteredPosition(node, 0); - if(odfNodeFilter.acceptNode(node) === FILTER_ACCEPT && rootFilter.acceptPosition(positionIterator) === FILTER_ACCEPT) { - return FILTER_ACCEPT - } - return FILTER_REJECT + function handleBeforeCut() { + var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); + return selectedRange.collapsed !== false } - function getRectFromNodeAfterFiltering(node) { - var rect = null; - if(acceptNode(node) === FILTER_ACCEPT) { - rect = domUtils.getBoundingClientRect(node) + function handleCopy(e) { + var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); + if(selectedRange.collapsed) { + return + } + if(!clipboard.setDataFromRange(e, selectedRange)) { + runtime.log("Cut operation failed") } - return rect - } - if(firstNode === containerNode || lastNode === containerNode) { - range = fillerRange.cloneRange(); - grownRect = range.getBoundingClientRect(); - range.detach(); - return grownRect - } - firstSibling = firstNode; - while(firstSibling.parentNode !== containerNode) { - firstSibling = firstSibling.parentNode - } - lastSibling = lastNode; - while(lastSibling.parentNode !== containerNode) { - lastSibling = lastSibling.parentNode - } - rootFilter = odtDocument.createRootFilter(firstNode); - currentNode = firstSibling.nextSibling; - while(currentNode && currentNode !== lastSibling) { - currentRect = getRectFromNodeAfterFiltering(currentNode); - grownRect = checkAndGrowOrCreateRect(grownRect, currentRect); - currentNode = currentNode.nextSibling } - if(odfUtils.isParagraph(firstSibling)) { - grownRect = checkAndGrowOrCreateRect(grownRect, domUtils.getBoundingClientRect(firstSibling)) - }else { - if(firstSibling.nodeType === Node.TEXT_NODE) { - currentNode = firstSibling; - range.setStart(currentNode, firstOffset); - range.setEnd(currentNode, currentNode === lastSibling ? lastOffset : currentNode.length); - currentRect = range.getBoundingClientRect(); - grownRect = checkAndGrowOrCreateRect(grownRect, currentRect) + function handlePaste(e) { + var plainText; + if(window.clipboardData && window.clipboardData.getData) { + plainText = window.clipboardData.getData("Text") }else { - treeWalker = doc.createTreeWalker(firstSibling, NodeFilter.SHOW_TEXT, acceptNode, false); - currentNode = treeWalker.currentNode = firstNode; - while(currentNode && currentNode !== lastNode) { - range.setStart(currentNode, firstOffset); - range.setEnd(currentNode, currentNode.length); - currentRect = range.getBoundingClientRect(); - grownRect = checkAndGrowOrCreateRect(grownRect, currentRect); - lastMeasuredNode = currentNode; - firstOffset = 0; - currentNode = treeWalker.nextNode() + if(e.clipboardData && e.clipboardData.getData) { + plainText = e.clipboardData.getData("text/plain") } } + if(plainText) { + textManipulator.removeCurrentSelection(); + session.enqueue(pasteHandler.createPasteOps(plainText)); + cancelEvent(e) + } } - if(!lastMeasuredNode) { - lastMeasuredNode = firstNode + function handleBeforePaste() { + return false } - if(odfUtils.isParagraph(lastSibling)) { - grownRect = checkAndGrowOrCreateRect(grownRect, domUtils.getBoundingClientRect(lastSibling)) - }else { - if(lastSibling.nodeType === Node.TEXT_NODE) { - currentNode = lastSibling; - range.setStart(currentNode, currentNode === firstSibling ? firstOffset : 0); - range.setEnd(currentNode, lastOffset); - currentRect = range.getBoundingClientRect(); - grownRect = checkAndGrowOrCreateRect(grownRect, currentRect) - }else { - treeWalker = doc.createTreeWalker(lastSibling, NodeFilter.SHOW_TEXT, acceptNode, false); - currentNode = treeWalker.currentNode = lastNode; - while(currentNode && currentNode !== lastMeasuredNode) { - range.setStart(currentNode, 0); - range.setEnd(currentNode, lastOffset); - currentRect = range.getBoundingClientRect(); - grownRect = checkAndGrowOrCreateRect(grownRect, currentRect); - currentNode = treeWalker.previousNode(); - if(currentNode) { - lastOffset = currentNode.length - } - } + function updateUndoStack(op) { + if(undoManager) { + undoManager.onOperationExecuted(op) } } - return grownRect - } - function getCollapsedRectOfTextRange(range, useRightEdge) { - var clientRect = range.getBoundingClientRect(), collapsedRect = {}; - collapsedRect.width = 0; - collapsedRect.top = clientRect.top; - collapsedRect.bottom = clientRect.bottom; - collapsedRect.height = clientRect.height; - collapsedRect.left = collapsedRect.right = useRightEdge ? clientRect.right : clientRect.left; - return collapsedRect - } - function repositionOverlays(selectedRange) { - var extremes = getExtremeRanges(selectedRange), firstRange, lastRange, fillerRange, firstRect, fillerRect, lastRect; - if(selectedRange.collapsed || !extremes) { - showOverlays(false) - }else { - showOverlays(true); - firstRange = extremes.firstRange; - lastRange = extremes.lastRange; - fillerRange = extremes.fillerRange; - firstRect = translateRect(getCollapsedRectOfTextRange(firstRange, false)); - lastRect = translateRect(getCollapsedRectOfTextRange(lastRange, true)); - fillerRect = getFillerRect(fillerRange); - if(!fillerRect) { - fillerRect = getBoundingRect(firstRect, lastRect) - }else { - fillerRect = translateRect(fillerRect) + function forwardUndoStackChange(e) { + odtDocument.emit(ops.OdtDocument.signalUndoStackChanged, e) + } + function undo() { + if(undoManager) { + undoManager.moveBackward(1); + redrawRegionSelectionTask.trigger(); + return true } - setRect(overlayTop, {left:firstRect.left, top:firstRect.top, width:Math.max(0, fillerRect.width - (firstRect.left - fillerRect.left)), height:firstRect.height}); - if(lastRect.top === firstRect.top || lastRect.bottom === firstRect.bottom) { - overlayMiddle.style.display = overlayBottom.style.display = "none" - }else { - setRect(overlayBottom, {left:fillerRect.left, top:lastRect.top, width:Math.max(0, lastRect.right - fillerRect.left), height:lastRect.height}); - setRect(overlayMiddle, {left:fillerRect.left, top:firstRect.top + firstRect.height, width:Math.max(0, parseFloat(overlayTop.style.left) + parseFloat(overlayTop.style.width) - parseFloat(overlayBottom.style.left)), height:Math.max(0, lastRect.top - firstRect.bottom)}) + return false + } + function redo() { + if(undoManager) { + undoManager.moveForward(1); + redrawRegionSelectionTask.trigger(); + return true } - firstRange.detach(); - lastRange.detach(); - fillerRange.detach() + return false } - } - function rerender() { - addOverlays(); - if(cursor.getSelectionType() === ops.OdtCursor.RangeSelection) { - showOverlays(true); - repositionOverlays(cursor.getSelectedRange()) - }else { - showOverlays(false) + function updateShadowCursor() { + var selection = window.getSelection(), selectionRange = selection.rangeCount > 0 && selectionToRange(selection); + if(clickStartedWithinContainer && selectionRange) { + isMouseMoved = true; + imageSelector.clearSelection(); + shadowCursorIterator.setUnfilteredPosition((selection.focusNode), selection.focusOffset); + if(mouseDownRootFilter.acceptPosition(shadowCursorIterator) === FILTER_ACCEPT) { + if(clickCount === 2) { + expandToWordBoundaries(selectionRange.range) + }else { + if(clickCount >= 3) { + expandToParagraphBoundaries(selectionRange.range) + } + } + shadowCursor.setSelectedRange(selectionRange.range, selectionRange.hasForwardSelection); + odtDocument.emit(ops.OdtDocument.signalCursorMoved, shadowCursor) + } + } } - } - this.rerender = rerender; - this.show = rerender; - this.hide = function() { - showOverlays(false) - }; - this.visible = function() { - return isVisible - }; - function handleCursorMove(movedCursor) { - if(movedCursor === cursor) { - rerender() + function handleMouseDown(e) { + var target = getTarget(e), cursor = odtDocument.getCursor(inputMemberId); + clickStartedWithinContainer = target && domUtils.containsNode(odtDocument.getOdfCanvas().getElement(), target); + if(clickStartedWithinContainer) { + isMouseMoved = false; + mouseDownRootFilter = odtDocument.createRootFilter(target); + clickCount = e.detail; + if(cursor && e.shiftKey) { + window.getSelection().collapse(cursor.getAnchorNode(), 0) + } + if(clickCount > 1) { + updateShadowCursor() + } + } } - } - this.destroy = function(callback) { - root.removeChild(overlayTop); - root.removeChild(overlayMiddle); - root.removeChild(overlayBottom); - cursor.getOdtDocument().unsubscribe(ops.OdtDocument.signalCursorMoved, handleCursorMove); - callback() - }; - function init() { - var editinfons = "urn:webodf:names:editinfo", memberid = cursor.getMemberId(); - addOverlays(); - overlayTop.setAttributeNS(editinfons, "editinfo:memberid", memberid); - overlayMiddle.setAttributeNS(editinfons, "editinfo:memberid", memberid); - overlayBottom.setAttributeNS(editinfons, "editinfo:memberid", memberid); - overlayTop.className = overlayMiddle.className = overlayBottom.className = "selectionOverlay"; - cursor.getOdtDocument().subscribe(ops.OdtDocument.signalCursorMoved, handleCursorMove) - } - init() -}; -/* - - Copyright (C) 2013 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("gui.SelectionView"); -gui.SelectionViewManager = function SelectionViewManager() { - var selectionViews = {}; - function getSelectionView(memberId) { - return selectionViews.hasOwnProperty(memberId) ? selectionViews[memberId] : null - } - this.getSelectionView = getSelectionView; - function getSelectionViews() { - return Object.keys(selectionViews).map(function(memberid) { - return selectionViews[memberid] - }) - } - this.getSelectionViews = getSelectionViews; - function removeSelectionView(memberId) { - if(selectionViews.hasOwnProperty(memberId)) { - selectionViews[memberId].destroy(function() { - }); - delete selectionViews[memberId] + function mutableSelection(selection) { + if(selection) { + return{anchorNode:selection.anchorNode, anchorOffset:selection.anchorOffset, focusNode:selection.focusNode, focusOffset:selection.focusOffset} + } + return null } - } - this.removeSelectionView = removeSelectionView; - function hideSelectionView(memberId) { - if(selectionViews.hasOwnProperty(memberId)) { - selectionViews[memberId].hide() + function handleMouseClickEvent(event) { + var target = getTarget(event), eventDetails = {detail:event.detail, clientX:event.clientX, clientY:event.clientY, target:target}; + drawShadowCursorTask.processRequests(); + if(odfUtils.isImage(target) && odfUtils.isCharacterFrame(target.parentNode)) { + selectImage(target.parentNode) + }else { + if(clickStartedWithinContainer && !imageSelector.isSelectorElement(target)) { + if(isMouseMoved) { + selectRange(shadowCursor.getSelectedRange(), shadowCursor.hasForwardSelection(), event.detail) + }else { + runtime.setTimeout(function() { + var selection = mutableSelection(window.getSelection()), selectionRange, caretPos; + if(!selection.anchorNode && !selection.focusNode) { + caretPos = caretPositionFromPoint(eventDetails.clientX, eventDetails.clientY); + if(!caretPos) { + return + } + selection.anchorNode = (caretPos.container); + selection.anchorOffset = caretPos.offset; + selection.focusNode = selection.anchorNode; + selection.focusOffset = selection.anchorOffset + } + selectionRange = selectionToRange(selection); + selectRange(selectionRange.range, selectionRange.hasForwardSelection, eventDetails.detail) + }, 0) + } + } + } + clickCount = 0; + clickStartedWithinContainer = false; + isMouseMoved = false } - } - this.hideSelectionView = hideSelectionView; - function showSelectionView(memberId) { - if(selectionViews.hasOwnProperty(memberId)) { - selectionViews[memberId].show() + function handleContextMenu(e) { + handleMouseClickEvent(e) } - } - this.showSelectionView = showSelectionView; - this.rerenderSelectionViews = function() { - Object.keys(selectionViews).forEach(function(memberId) { - if(selectionViews[memberId].visible()) { - selectionViews[memberId].rerender() + function handleMouseUp(event) { + var target = getTarget(event), annotationNode = null; + if(target.className === "annotationRemoveButton") { + annotationNode = domUtils.getElementsByTagNameNS(target.parentNode, odf.Namespaces.officens, "annotation")[0]; + annotationController.removeAnnotation(annotationNode) + }else { + handleMouseClickEvent(event) + } + } + this.startEditing = function() { + var op; + odtDocument.getOdfCanvas().getElement().classList.add("virtualSelections"); + eventManager.subscribe("keydown", keyDownHandler.handleEvent); + eventManager.subscribe("keypress", keyPressHandler.handleEvent); + eventManager.subscribe("keyup", dummyHandler); + eventManager.subscribe("beforecut", handleBeforeCut); + eventManager.subscribe("cut", handleCut); + eventManager.subscribe("copy", handleCopy); + eventManager.subscribe("beforepaste", handleBeforePaste); + eventManager.subscribe("paste", handlePaste); + eventManager.subscribe("mousedown", handleMouseDown); + eventManager.subscribe("mousemove", drawShadowCursorTask.trigger); + eventManager.subscribe("mouseup", handleMouseUp); + eventManager.subscribe("contextmenu", handleContextMenu); + eventManager.subscribe("focus", delayedMaintainCursor); + odtDocument.subscribe(ops.OdtDocument.signalOperationExecuted, redrawRegionSelectionTask.trigger); + odtDocument.subscribe(ops.OdtDocument.signalOperationExecuted, updateUndoStack); + op = new ops.OpAddCursor; + op.init({memberid:inputMemberId}); + session.enqueue([op]); + if(undoManager) { + undoManager.saveInitialState() + } + }; + this.endEditing = function() { + var op; + op = new ops.OpRemoveCursor; + op.init({memberid:inputMemberId}); + session.enqueue([op]); + if(undoManager) { + undoManager.resetInitialState() + } + odtDocument.unsubscribe(ops.OdtDocument.signalOperationExecuted, updateUndoStack); + odtDocument.unsubscribe(ops.OdtDocument.signalOperationExecuted, redrawRegionSelectionTask.trigger); + eventManager.unsubscribe("keydown", keyDownHandler.handleEvent); + eventManager.unsubscribe("keypress", keyPressHandler.handleEvent); + eventManager.unsubscribe("keyup", dummyHandler); + eventManager.unsubscribe("cut", handleCut); + eventManager.unsubscribe("beforecut", handleBeforeCut); + eventManager.unsubscribe("copy", handleCopy); + eventManager.unsubscribe("paste", handlePaste); + eventManager.unsubscribe("beforepaste", handleBeforePaste); + eventManager.unsubscribe("mousemove", drawShadowCursorTask.trigger); + eventManager.unsubscribe("mousedown", handleMouseDown); + eventManager.unsubscribe("mouseup", handleMouseUp); + eventManager.unsubscribe("contextmenu", handleContextMenu); + eventManager.unsubscribe("focus", delayedMaintainCursor); + odtDocument.getOdfCanvas().getElement().classList.remove("virtualSelections") + }; + this.getInputMemberId = function() { + return inputMemberId + }; + this.getSession = function() { + return session + }; + this.setUndoManager = function(manager) { + if(undoManager) { + undoManager.unsubscribe(gui.UndoManager.signalUndoStackChanged, forwardUndoStackChange) + } + undoManager = manager; + if(undoManager) { + undoManager.setOdtDocument(odtDocument); + undoManager.setPlaybackFunction(function(op) { + op.execute(odtDocument) + }); + undoManager.subscribe(gui.UndoManager.signalUndoStackChanged, forwardUndoStackChange) + } + }; + this.getUndoManager = function() { + return undoManager + }; + this.getAnnotationController = function() { + return annotationController + }; + this.getDirectTextStyler = function() { + return directTextStyler + }; + this.getDirectParagraphStyler = function() { + return directParagraphStyler + }; + this.getImageManager = function() { + return imageManager + }; + this.getTextManipulator = function() { + return textManipulator + }; + this.getEventManager = function() { + return eventManager + }; + this.getKeyboardHandlers = function() { + return{keydown:keyDownHandler, keypress:keyPressHandler} + }; + this.destroy = function(callback) { + var destroyCallbacks = [drawShadowCursorTask.destroy, directTextStyler.destroy]; + if(directParagraphStyler) { + destroyCallbacks.push(directParagraphStyler.destroy) + } + async.destroyAll(destroyCallbacks, callback) + }; + function returnTrue(fn) { + return function() { + fn(); + return true + } + } + function rangeSelectionOnly(fn) { + return function(e) { + var selectionType = odtDocument.getCursor(inputMemberId).getSelectionType(); + if(selectionType === ops.OdtCursor.RangeSelection) { + return fn(e) + } + return true } - }) - }; - this.registerCursor = function(cursor, virtualSelectionsInitiallyVisible) { - var memberId = cursor.getMemberId(), selectionView = new gui.SelectionView(cursor); - if(virtualSelectionsInitiallyVisible) { - selectionView.show() - }else { - selectionView.hide() } - selectionViews[memberId] = selectionView; - return selectionView - }; - this.destroy = function(callback) { - var selectionViewArray = getSelectionViews(); - (function destroySelectionView(i, err) { - if(err) { - callback(err) + function init() { + var isMacOS = window.navigator.appVersion.toLowerCase().indexOf("mac") !== -1, modifier = gui.KeyboardHandler.Modifier, keyCode = gui.KeyboardHandler.KeyCode; + drawShadowCursorTask = new core.ScheduledTask(updateShadowCursor, 0); + redrawRegionSelectionTask = new core.ScheduledTask(maintainCursorSelection, 0); + keyDownHandler.bind(keyCode.Tab, modifier.None, rangeSelectionOnly(function() { + textManipulator.insertText("\t"); + return true + })); + keyDownHandler.bind(keyCode.Left, modifier.None, rangeSelectionOnly(moveCursorToLeft)); + keyDownHandler.bind(keyCode.Right, modifier.None, rangeSelectionOnly(moveCursorToRight)); + keyDownHandler.bind(keyCode.Up, modifier.None, rangeSelectionOnly(moveCursorUp)); + keyDownHandler.bind(keyCode.Down, modifier.None, rangeSelectionOnly(moveCursorDown)); + keyDownHandler.bind(keyCode.Backspace, modifier.None, returnTrue(textManipulator.removeTextByBackspaceKey)); + keyDownHandler.bind(keyCode.Delete, modifier.None, textManipulator.removeTextByDeleteKey); + keyDownHandler.bind(keyCode.Left, modifier.Shift, rangeSelectionOnly(extendSelectionToLeft)); + keyDownHandler.bind(keyCode.Right, modifier.Shift, rangeSelectionOnly(extendSelectionToRight)); + keyDownHandler.bind(keyCode.Up, modifier.Shift, rangeSelectionOnly(extendSelectionUp)); + keyDownHandler.bind(keyCode.Down, modifier.Shift, rangeSelectionOnly(extendSelectionDown)); + keyDownHandler.bind(keyCode.Home, modifier.None, rangeSelectionOnly(moveCursorToLineStart)); + keyDownHandler.bind(keyCode.End, modifier.None, rangeSelectionOnly(moveCursorToLineEnd)); + keyDownHandler.bind(keyCode.Home, modifier.Ctrl, rangeSelectionOnly(moveCursorToDocumentStart)); + keyDownHandler.bind(keyCode.End, modifier.Ctrl, rangeSelectionOnly(moveCursorToDocumentEnd)); + keyDownHandler.bind(keyCode.Home, modifier.Shift, rangeSelectionOnly(extendSelectionToLineStart)); + keyDownHandler.bind(keyCode.End, modifier.Shift, rangeSelectionOnly(extendSelectionToLineEnd)); + keyDownHandler.bind(keyCode.Up, modifier.CtrlShift, rangeSelectionOnly(extendSelectionToParagraphStart)); + keyDownHandler.bind(keyCode.Down, modifier.CtrlShift, rangeSelectionOnly(extendSelectionToParagraphEnd)); + keyDownHandler.bind(keyCode.Home, modifier.CtrlShift, rangeSelectionOnly(extendSelectionToDocumentStart)); + keyDownHandler.bind(keyCode.End, modifier.CtrlShift, rangeSelectionOnly(extendSelectionToDocumentEnd)); + if(isMacOS) { + keyDownHandler.bind(keyCode.Clear, modifier.None, textManipulator.removeCurrentSelection); + keyDownHandler.bind(keyCode.Left, modifier.Meta, rangeSelectionOnly(moveCursorToLineStart)); + keyDownHandler.bind(keyCode.Right, modifier.Meta, rangeSelectionOnly(moveCursorToLineEnd)); + keyDownHandler.bind(keyCode.Home, modifier.Meta, rangeSelectionOnly(moveCursorToDocumentStart)); + keyDownHandler.bind(keyCode.End, modifier.Meta, rangeSelectionOnly(moveCursorToDocumentEnd)); + keyDownHandler.bind(keyCode.Left, modifier.MetaShift, rangeSelectionOnly(extendSelectionToLineStart)); + keyDownHandler.bind(keyCode.Right, modifier.MetaShift, rangeSelectionOnly(extendSelectionToLineEnd)); + keyDownHandler.bind(keyCode.Up, modifier.AltShift, rangeSelectionOnly(extendSelectionToParagraphStart)); + keyDownHandler.bind(keyCode.Down, modifier.AltShift, rangeSelectionOnly(extendSelectionToParagraphEnd)); + keyDownHandler.bind(keyCode.Up, modifier.MetaShift, rangeSelectionOnly(extendSelectionToDocumentStart)); + keyDownHandler.bind(keyCode.Down, modifier.MetaShift, rangeSelectionOnly(extendSelectionToDocumentEnd)); + keyDownHandler.bind(keyCode.A, modifier.Meta, rangeSelectionOnly(extendSelectionToEntireDocument)); + keyDownHandler.bind(keyCode.B, modifier.Meta, rangeSelectionOnly(directTextStyler.toggleBold)); + keyDownHandler.bind(keyCode.I, modifier.Meta, rangeSelectionOnly(directTextStyler.toggleItalic)); + keyDownHandler.bind(keyCode.U, modifier.Meta, rangeSelectionOnly(directTextStyler.toggleUnderline)); + if(directParagraphStyler) { + keyDownHandler.bind(keyCode.L, modifier.MetaShift, rangeSelectionOnly(directParagraphStyler.alignParagraphLeft)); + keyDownHandler.bind(keyCode.E, modifier.MetaShift, rangeSelectionOnly(directParagraphStyler.alignParagraphCenter)); + keyDownHandler.bind(keyCode.R, modifier.MetaShift, rangeSelectionOnly(directParagraphStyler.alignParagraphRight)); + keyDownHandler.bind(keyCode.J, modifier.MetaShift, rangeSelectionOnly(directParagraphStyler.alignParagraphJustified)) + } + if(annotationController) { + keyDownHandler.bind(keyCode.C, modifier.MetaShift, annotationController.addAnnotation) + } + keyDownHandler.bind(keyCode.Z, modifier.Meta, undo); + keyDownHandler.bind(keyCode.Z, modifier.MetaShift, redo) }else { - if(i < selectionViewArray.length) { - selectionViewArray[i].destroy(function(err) { - destroySelectionView(i + 1, err) - }) - }else { - callback() + keyDownHandler.bind(keyCode.A, modifier.Ctrl, rangeSelectionOnly(extendSelectionToEntireDocument)); + keyDownHandler.bind(keyCode.B, modifier.Ctrl, rangeSelectionOnly(directTextStyler.toggleBold)); + keyDownHandler.bind(keyCode.I, modifier.Ctrl, rangeSelectionOnly(directTextStyler.toggleItalic)); + keyDownHandler.bind(keyCode.U, modifier.Ctrl, rangeSelectionOnly(directTextStyler.toggleUnderline)); + if(directParagraphStyler) { + keyDownHandler.bind(keyCode.L, modifier.CtrlShift, rangeSelectionOnly(directParagraphStyler.alignParagraphLeft)); + keyDownHandler.bind(keyCode.E, modifier.CtrlShift, rangeSelectionOnly(directParagraphStyler.alignParagraphCenter)); + keyDownHandler.bind(keyCode.R, modifier.CtrlShift, rangeSelectionOnly(directParagraphStyler.alignParagraphRight)); + keyDownHandler.bind(keyCode.J, modifier.CtrlShift, rangeSelectionOnly(directParagraphStyler.alignParagraphJustified)) + } + if(annotationController) { + keyDownHandler.bind(keyCode.C, modifier.CtrlAlt, annotationController.addAnnotation) } + keyDownHandler.bind(keyCode.Z, modifier.Ctrl, undo); + keyDownHandler.bind(keyCode.Z, modifier.CtrlShift, redo) } - })(0, undefined) - } -}; + keyPressHandler.setDefault(rangeSelectionOnly(function(e) { + var text = stringFromKeyPress(e); + if(text && !(e.altKey || (e.ctrlKey || e.metaKey))) { + textManipulator.insertText(text); + return true + } + return false + })); + keyPressHandler.bind(keyCode.Enter, modifier.None, rangeSelectionOnly(textManipulator.enqueueParagraphSplittingOps)) + } + init() + }; + return gui.SessionController +}(); /* Copyright (C) 2012-2013 KO GmbH @@ -17570,354 +17772,132 @@ gui.SelectionViewManager = function SelectionViewManager() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.EventNotifier"); -runtime.loadClass("core.DomUtils"); -runtime.loadClass("odf.OdfUtils"); -runtime.loadClass("odf.Namespaces"); -runtime.loadClass("gui.SelectionMover"); -runtime.loadClass("core.PositionFilterChain"); -runtime.loadClass("ops.StepsTranslator"); -runtime.loadClass("ops.TextPositionFilter"); -runtime.loadClass("ops.Member"); -ops.OdtDocument = function OdtDocument(odfCanvas) { - var self = this, odfUtils, domUtils, cursors = {}, members = {}, eventNotifier = new core.EventNotifier([ops.OdtDocument.signalMemberAdded, ops.OdtDocument.signalMemberUpdated, ops.OdtDocument.signalMemberRemoved, ops.OdtDocument.signalCursorAdded, ops.OdtDocument.signalCursorRemoved, ops.OdtDocument.signalCursorMoved, ops.OdtDocument.signalParagraphChanged, ops.OdtDocument.signalParagraphStyleModified, ops.OdtDocument.signalCommonStyleCreated, ops.OdtDocument.signalCommonStyleDeleted, ops.OdtDocument.signalTableAdded, - ops.OdtDocument.signalOperationExecuted, ops.OdtDocument.signalUndoStackChanged, ops.OdtDocument.signalStepsInserted, ops.OdtDocument.signalStepsRemoved]), FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, FILTER_REJECT = core.PositionFilter.FilterResult.FILTER_REJECT, filter, stepsTranslator, lastEditingOp, unsupportedMetadataRemoved = false; - function getRootNode() { - var element = odfCanvas.odfContainer().getContentElement(), localName = element && element.localName; - runtime.assert(localName === "text", "Unsupported content element type '" + localName + "' for OdtDocument"); - return element - } - function getDOM() { - return(getRootNode().ownerDocument) +runtime.loadClass("gui.Caret"); +gui.CaretManager = function CaretManager(sessionController) { + var carets = {}, window = runtime.getWindow(), scrollIntoViewScheduled = false; + function getCaret(memberId) { + return carets.hasOwnProperty(memberId) ? carets[memberId] : null } - this.getDOM = getDOM; - function isRoot(node) { - if(node.namespaceURI === odf.Namespaces.officens && node.localName === "text" || node.namespaceURI === odf.Namespaces.officens && node.localName === "annotation") { - return true - } - return false + function getCarets() { + return Object.keys(carets).map(function(memberid) { + return carets[memberid] + }) } - function getRoot(node) { - while(node && !isRoot(node)) { - node = (node.parentNode) - } - return node + function getCanvasElement() { + return sessionController.getSession().getOdtDocument().getOdfCanvas().getElement() } - this.getRootElement = getRoot; - function RootFilter(anchor) { - this.acceptPosition = function(iterator) { - var node = iterator.container(), anchorNode; - if(typeof anchor === "string") { - anchorNode = cursors[anchor].getNode() - }else { - anchorNode = anchor - } - if(getRoot(node) === getRoot(anchorNode)) { - return FILTER_ACCEPT - } - return FILTER_REJECT + function removeCaret(memberId) { + if(memberId === sessionController.getInputMemberId()) { + getCanvasElement().removeAttribute("tabindex") } + delete carets[memberId] } - function getIteratorAtPosition(position) { - var iterator = gui.SelectionMover.createPositionIterator(getRootNode()), point = stepsTranslator.convertStepsToDomPoint(position); - iterator.setUnfilteredPosition(point.node, point.offset); - return iterator - } - this.getIteratorAtPosition = getIteratorAtPosition; - this.convertDomPointToCursorStep = function(node, offset) { - return stepsTranslator.convertDomPointToSteps(node, offset) - }; - this.convertDomToCursorRange = function(anchorNode, anchorOffset, focusNode, focusOffset) { - var point1, point2; - point1 = stepsTranslator.convertDomPointToSteps(anchorNode, anchorOffset); - if(anchorNode === focusNode && anchorOffset === focusOffset) { - point2 = point1 - }else { - point2 = stepsTranslator.convertDomPointToSteps(focusNode, focusOffset) - } - return{position:point1, length:point2 - point1} - }; - this.convertCursorToDomRange = function(position, length) { - var range = getDOM().createRange(), point1, point2; - point1 = stepsTranslator.convertStepsToDomPoint(position); - if(length) { - point2 = stepsTranslator.convertStepsToDomPoint(position + length); - if(length > 0) { - range.setStart(point1.node, point1.offset); - range.setEnd(point2.node, point2.offset) - }else { - range.setStart(point2.node, point2.offset); - range.setEnd(point1.node, point1.offset) - } - }else { - range.setStart(point1.node, point1.offset) - } - return range - }; - function getTextNodeAtStep(steps, memberid) { - var iterator = getIteratorAtPosition(steps), node = iterator.container(), lastTextNode, nodeOffset = 0, cursorNode = null; - if(node.nodeType === Node.TEXT_NODE) { - lastTextNode = (node); - nodeOffset = iterator.unfilteredDomOffset() - }else { - lastTextNode = getDOM().createTextNode(""); - nodeOffset = 0; - node.insertBefore(lastTextNode, iterator.rightNode()) - } - if(memberid && (cursors[memberid] && self.getCursorPosition(memberid) === steps)) { - cursorNode = cursors[memberid].getNode(); - while(cursorNode.nextSibling && cursorNode.nextSibling.localName === "cursor") { - cursorNode.parentNode.insertBefore(cursorNode.nextSibling, cursorNode) - } - if(lastTextNode.length > 0 && lastTextNode.nextSibling !== cursorNode) { - lastTextNode = getDOM().createTextNode(""); - nodeOffset = 0 + function refreshLocalCaretBlinking(cursor) { + var caret, memberId = cursor.getMemberId(); + if(memberId === sessionController.getInputMemberId()) { + caret = getCaret(memberId); + if(caret) { + caret.refreshCursorBlinking() } - cursorNode.parentNode.insertBefore(lastTextNode, cursorNode) - } - while(lastTextNode.previousSibling && lastTextNode.previousSibling.nodeType === Node.TEXT_NODE) { - lastTextNode.previousSibling.appendData(lastTextNode.data); - nodeOffset = lastTextNode.previousSibling.length; - lastTextNode = (lastTextNode.previousSibling); - lastTextNode.parentNode.removeChild(lastTextNode.nextSibling) } - return{textNode:lastTextNode, offset:nodeOffset} - } - function getParagraphElement(node) { - return odfUtils.getParagraphElement(node) - } - function getStyleElement(styleName, styleFamily) { - return odfCanvas.getFormatting().getStyleElement(styleName, styleFamily) - } - this.getStyleElement = getStyleElement; - function getParagraphStyleElement(styleName) { - return getStyleElement(styleName, "paragraph") } - function getParagraphStyleAttributes(styleName) { - var node = getParagraphStyleElement(styleName); - if(node) { - return odfCanvas.getFormatting().getInheritedStyleAttributes(node) + function executeEnsureCaretVisible() { + var caret = getCaret(sessionController.getInputMemberId()); + scrollIntoViewScheduled = false; + if(caret) { + caret.ensureVisible() } - return null } - function handleOperationExecuted(op) { - var spec = op.spec(), memberId = spec.memberid, date = (new Date(spec.timestamp)).toISOString(), metadataManager = odfCanvas.odfContainer().getMetadataManager(), fullName; - if(op.isEdit) { - fullName = self.getMember(memberId).getProperties().fullName; - metadataManager.setMetadata({"dc:creator":fullName, "dc:date":date}, null); - if(!lastEditingOp) { - metadataManager.incrementEditingCycles(); - if(!unsupportedMetadataRemoved) { - metadataManager.setMetadata(null, ["meta:editing-duration", "meta:document-statistic"]) - } + function scheduleCaretVisibilityCheck() { + var caret = getCaret(sessionController.getInputMemberId()); + if(caret) { + caret.handleUpdate(); + if(!scrollIntoViewScheduled) { + scrollIntoViewScheduled = true; + runtime.setTimeout(executeEnsureCaretVisible, 50) } - lastEditingOp = op } } - function upgradeWhitespaceToElement(textNode, offset) { - runtime.assert(textNode.data[offset] === " ", "upgradeWhitespaceToElement: textNode.data[offset] should be a literal space"); - var space = textNode.ownerDocument.createElementNS(odf.Namespaces.textns, "text:s"); - space.appendChild(textNode.ownerDocument.createTextNode(" ")); - textNode.deleteData(offset, 1); - if(offset > 0) { - textNode = (textNode.splitText(offset)) + function ensureLocalCaretVisible(info) { + if(info.memberId === sessionController.getInputMemberId()) { + scheduleCaretVisibilityCheck() } - textNode.parentNode.insertBefore(space, textNode); - return space } - function upgradeWhitespacesAtPosition(position) { - var iterator = getIteratorAtPosition(position), container, offset, i; - iterator.previousPosition(); - iterator.previousPosition(); - for(i = -1;i <= 1;i += 1) { - container = iterator.container(); - offset = iterator.unfilteredDomOffset(); - if(container.nodeType === Node.TEXT_NODE && (container.data[offset] === " " && odfUtils.isSignificantWhitespace(container, offset))) { - container = upgradeWhitespaceToElement((container), offset); - iterator.moveToEndOfNode(container) - } - iterator.nextPosition() + function focusLocalCaret() { + var caret = getCaret(sessionController.getInputMemberId()); + if(caret) { + caret.setFocus() } } - this.upgradeWhitespacesAtPosition = upgradeWhitespacesAtPosition; - this.downgradeWhitespacesAtPosition = function(position) { - var iterator = getIteratorAtPosition(position), container, offset, firstSpaceElementChild, lastSpaceElementChild; - container = iterator.container(); - offset = iterator.unfilteredDomOffset(); - while(!odfUtils.isCharacterElement(container) && container.childNodes[offset]) { - container = container.childNodes[offset]; - offset = 0 + function blurLocalCaret() { + var caret = getCaret(sessionController.getInputMemberId()); + if(caret) { + caret.removeFocus() } - if(container.nodeType === Node.TEXT_NODE) { - container = container.parentNode + } + function showLocalCaret() { + var caret = getCaret(sessionController.getInputMemberId()); + if(caret) { + caret.show() } - if(odfUtils.isDowngradableSpaceElement(container)) { - firstSpaceElementChild = container.firstChild; - lastSpaceElementChild = container.lastChild; - domUtils.mergeIntoParent(container); - if(lastSpaceElementChild !== firstSpaceElementChild) { - domUtils.normalizeTextNodes(lastSpaceElementChild) - } - domUtils.normalizeTextNodes(firstSpaceElementChild) + } + function hideLocalCaret() { + var caret = getCaret(sessionController.getInputMemberId()); + if(caret) { + caret.hide() } + } + this.registerCursor = function(cursor, caretAvatarInitiallyVisible, blinkOnRangeSelect) { + var memberid = cursor.getMemberId(), caret = new gui.Caret(cursor, caretAvatarInitiallyVisible, blinkOnRangeSelect); + carets[memberid] = caret; + if(memberid === sessionController.getInputMemberId()) { + runtime.log("Starting to track input on new cursor of " + memberid); + cursor.handleUpdate = scheduleCaretVisibilityCheck; + getCanvasElement().setAttribute("tabindex", -1); + sessionController.getEventManager().focus() + }else { + cursor.handleUpdate = caret.handleUpdate + } + return caret }; - this.getParagraphStyleElement = getParagraphStyleElement; - this.getParagraphElement = getParagraphElement; - this.getParagraphStyleAttributes = getParagraphStyleAttributes; - this.getTextNodeAtStep = getTextNodeAtStep; - this.fixCursorPositions = function() { - var rootConstrainedFilter = new core.PositionFilterChain; - rootConstrainedFilter.addFilter("BaseFilter", filter); - Object.keys(cursors).forEach(function(memberId) { - var cursor = cursors[memberId], stepCounter = cursor.getStepCounter(), stepsSelectionLength, positionsToAdjustFocus, positionsToAdjustAnchor, positionsToAnchor, cursorMoved = false; - rootConstrainedFilter.addFilter("RootFilter", self.createRootFilter(memberId)); - stepsSelectionLength = stepCounter.countStepsToPosition(cursor.getAnchorNode(), 0, rootConstrainedFilter); - if(!stepCounter.isPositionWalkable(rootConstrainedFilter)) { - cursorMoved = true; - positionsToAdjustFocus = stepCounter.countPositionsToNearestStep(cursor.getNode(), 0, rootConstrainedFilter); - positionsToAdjustAnchor = stepCounter.countPositionsToNearestStep(cursor.getAnchorNode(), 0, rootConstrainedFilter); - cursor.move(positionsToAdjustFocus); - if(stepsSelectionLength !== 0) { - if(positionsToAdjustAnchor > 0) { - stepsSelectionLength += 1 - } - if(positionsToAdjustFocus > 0) { - stepsSelectionLength -= 1 - } - positionsToAnchor = stepCounter.countSteps(stepsSelectionLength, rootConstrainedFilter); - cursor.move(positionsToAnchor); - cursor.move(-positionsToAnchor, true) - } + this.getCaret = getCaret; + this.getCarets = getCarets; + this.destroy = function(callback) { + var odtDocument = sessionController.getSession().getOdtDocument(), eventManager = sessionController.getEventManager(), caretArray = getCarets(); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, ensureLocalCaretVisible); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, refreshLocalCaretBlinking); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, removeCaret); + eventManager.unsubscribe("focus", focusLocalCaret); + eventManager.unsubscribe("blur", blurLocalCaret); + window.removeEventListener("focus", showLocalCaret, false); + window.removeEventListener("blur", hideLocalCaret, false); + (function destroyCaret(i, err) { + if(err) { + callback(err) }else { - if(stepsSelectionLength === 0) { - cursorMoved = true; - cursor.move(0) + if(i < caretArray.length) { + caretArray[i].destroy(function(err) { + destroyCaret(i + 1, err) + }) + }else { + callback() } } - if(cursorMoved) { - self.emit(ops.OdtDocument.signalCursorMoved, cursor) - } - rootConstrainedFilter.removeFilter("RootFilter") - }) - }; - this.getDistanceFromCursor = function(memberid, node, offset) { - var cursor = cursors[memberid], focusPosition, targetPosition; - runtime.assert(node !== null && node !== undefined, "OdtDocument.getDistanceFromCursor called without node"); - if(cursor) { - focusPosition = stepsTranslator.convertDomPointToSteps(cursor.getNode(), 0); - targetPosition = stepsTranslator.convertDomPointToSteps(node, offset) - } - return targetPosition - focusPosition - }; - this.getCursorPosition = function(memberid) { - var cursor = cursors[memberid]; - return cursor ? stepsTranslator.convertDomPointToSteps(cursor.getNode(), 0) : 0 - }; - this.getCursorSelection = function(memberid) { - var cursor = cursors[memberid], focusPosition = 0, anchorPosition = 0; - if(cursor) { - focusPosition = stepsTranslator.convertDomPointToSteps(cursor.getNode(), 0); - anchorPosition = stepsTranslator.convertDomPointToSteps(cursor.getAnchorNode(), 0) - } - return{position:anchorPosition, length:focusPosition - anchorPosition} - }; - this.getPositionFilter = function() { - return filter - }; - this.getOdfCanvas = function() { - return odfCanvas - }; - this.getRootNode = getRootNode; - this.addMember = function(member) { - runtime.assert(members[member.getMemberId()] === undefined, "This member already exists"); - members[member.getMemberId()] = member - }; - this.getMember = function(memberId) { - return members.hasOwnProperty(memberId) ? members[memberId] : null - }; - this.removeMember = function(memberId) { - delete members[memberId] - }; - this.getCursor = function(memberid) { - return cursors[memberid] - }; - this.getCursors = function() { - var list = [], i; - for(i in cursors) { - if(cursors.hasOwnProperty(i)) { - list.push(cursors[i]) - } - } - return list - }; - this.addCursor = function(cursor) { - runtime.assert(Boolean(cursor), "OdtDocument::addCursor without cursor"); - var distanceToFirstTextNode = cursor.getStepCounter().countSteps(1, filter), memberid = cursor.getMemberId(); - runtime.assert(typeof memberid === "string", "OdtDocument::addCursor has cursor without memberid"); - runtime.assert(!cursors[memberid], "OdtDocument::addCursor is adding a duplicate cursor with memberid " + memberid); - cursor.move(distanceToFirstTextNode); - cursors[memberid] = cursor - }; - this.removeCursor = function(memberid) { - var cursor = cursors[memberid]; - if(cursor) { - cursor.removeFromOdtDocument(); - delete cursors[memberid]; - self.emit(ops.OdtDocument.signalCursorRemoved, memberid); - return true - } - return false - }; - this.getFormatting = function() { - return odfCanvas.getFormatting() - }; - this.emit = function(eventid, args) { - eventNotifier.emit(eventid, args) - }; - this.subscribe = function(eventid, cb) { - eventNotifier.subscribe(eventid, cb) - }; - this.unsubscribe = function(eventid, cb) { - eventNotifier.unsubscribe(eventid, cb) - }; - this.createRootFilter = function(inputMemberId) { - return new RootFilter(inputMemberId) - }; - this.close = function(callback) { - callback() - }; - this.destroy = function(callback) { - callback() + })(0, undefined); + carets = {} }; function init() { - filter = new ops.TextPositionFilter(getRootNode); - odfUtils = new odf.OdfUtils; - domUtils = new core.DomUtils; - stepsTranslator = new ops.StepsTranslator(getRootNode, gui.SelectionMover.createPositionIterator, filter, 500); - eventNotifier.subscribe(ops.OdtDocument.signalStepsInserted, stepsTranslator.handleStepsInserted); - eventNotifier.subscribe(ops.OdtDocument.signalStepsRemoved, stepsTranslator.handleStepsRemoved); - eventNotifier.subscribe(ops.OdtDocument.signalOperationExecuted, handleOperationExecuted) + var odtDocument = sessionController.getSession().getOdtDocument(), eventManager = sessionController.getEventManager(); + odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, ensureLocalCaretVisible); + odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, refreshLocalCaretBlinking); + odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, removeCaret); + eventManager.subscribe("focus", focusLocalCaret); + eventManager.subscribe("blur", blurLocalCaret); + window.addEventListener("focus", showLocalCaret, false); + window.addEventListener("blur", hideLocalCaret, false) } init() }; -ops.OdtDocument.signalMemberAdded = "member/added"; -ops.OdtDocument.signalMemberUpdated = "member/updated"; -ops.OdtDocument.signalMemberRemoved = "member/removed"; -ops.OdtDocument.signalCursorAdded = "cursor/added"; -ops.OdtDocument.signalCursorRemoved = "cursor/removed"; -ops.OdtDocument.signalCursorMoved = "cursor/moved"; -ops.OdtDocument.signalParagraphChanged = "paragraph/changed"; -ops.OdtDocument.signalTableAdded = "table/added"; -ops.OdtDocument.signalCommonStyleCreated = "style/created"; -ops.OdtDocument.signalCommonStyleDeleted = "style/deleted"; -ops.OdtDocument.signalParagraphStyleModified = "paragraphstyle/modified"; -ops.OdtDocument.signalOperationExecuted = "operation/executed"; -ops.OdtDocument.signalUndoStackChanged = "undo/changed"; -ops.OdtDocument.signalStepsInserted = "steps/inserted"; -ops.OdtDocument.signalStepsRemoved = "steps/removed"; -(function() { - return ops.OdtDocument -})(); /* Copyright (C) 2012-2013 KO GmbH @@ -17955,51 +17935,230 @@ ops.OdtDocument.signalStepsRemoved = "steps/removed"; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("ops.TrivialOperationRouter"); -runtime.loadClass("ops.OperationFactory"); -runtime.loadClass("ops.OdtDocument"); -ops.Session = function Session(odfCanvas) { - var self = this, operationFactory = new ops.OperationFactory, odtDocument = new ops.OdtDocument(odfCanvas), operationRouter = null; - this.setOperationFactory = function(opFactory) { - operationFactory = opFactory; - if(operationRouter) { - operationRouter.setOperationFactory(operationFactory) +runtime.loadClass("gui.Caret"); +runtime.loadClass("ops.EditInfo"); +runtime.loadClass("gui.EditInfoMarker"); +gui.SessionViewOptions = function() { + this.editInfoMarkersInitiallyVisible = true; + this.caretAvatarsInitiallyVisible = true; + this.caretBlinksOnRangeSelect = true +}; +gui.SessionView = function() { + function configOption(userValue, defaultValue) { + return userValue !== undefined ? Boolean(userValue) : defaultValue + } + function SessionView(viewOptions, localMemberId, session, caretManager, selectionViewManager) { + var avatarInfoStyles, editInfons = "urn:webodf:names:editinfo", editInfoMap = {}, showEditInfoMarkers = configOption(viewOptions.editInfoMarkersInitiallyVisible, true), showCaretAvatars = configOption(viewOptions.caretAvatarsInitiallyVisible, true), blinkOnRangeSelect = configOption(viewOptions.caretBlinksOnRangeSelect, true), rerenderIntervalId, rerenderSelectionViews = false, RERENDER_INTERVAL = 200; + function createAvatarInfoNodeMatch(nodeName, memberId, pseudoClass) { + return nodeName + '[editinfo|memberid="' + memberId + '"]' + pseudoClass } - }; - this.setOperationRouter = function(opRouter) { - operationRouter = opRouter; - opRouter.setPlaybackFunction(function(op) { - op.execute(odtDocument); - odtDocument.emit(ops.OdtDocument.signalOperationExecuted, op) - }); - opRouter.setOperationFactory(operationFactory) - }; - this.getOperationFactory = function() { - return operationFactory - }; - this.getOdtDocument = function() { - return odtDocument - }; - this.enqueue = function(ops) { - operationRouter.push(ops) - }; - this.close = function(callback) { - operationRouter.close(function(err) { - if(err) { - callback(err) + function getAvatarInfoStyle(nodeName, memberId, pseudoClass) { + var node = avatarInfoStyles.firstChild, nodeMatch = createAvatarInfoNodeMatch(nodeName, memberId, pseudoClass) + "{"; + while(node) { + if(node.nodeType === Node.TEXT_NODE && node.data.indexOf(nodeMatch) === 0) { + return node + } + node = node.nextSibling + } + return null + } + function setAvatarInfoStyle(memberId, name, color) { + function setStyle(nodeName, rule, pseudoClass) { + var styleRule = createAvatarInfoNodeMatch(nodeName, memberId, pseudoClass) + rule, styleNode = getAvatarInfoStyle(nodeName, memberId, pseudoClass); + if(styleNode) { + styleNode.data = styleRule + }else { + avatarInfoStyles.appendChild(document.createTextNode(styleRule)) + } + } + setStyle("div.editInfoMarker", "{ background-color: " + color + "; }", ""); + setStyle("span.editInfoColor", "{ background-color: " + color + "; }", ""); + setStyle("span.editInfoAuthor", '{ content: "' + name + '"; }', ":before"); + setStyle("dc|creator", "{ background-color: " + color + "; }", ""); + setStyle("div.selectionOverlay", "{ background-color: " + color + ";}", "") + } + function highlightEdit(element, memberId, timestamp) { + var editInfo, editInfoMarker, id = "", editInfoNode = element.getElementsByTagNameNS(editInfons, "editinfo")[0]; + if(editInfoNode) { + id = editInfoNode.getAttributeNS(editInfons, "id"); + editInfoMarker = editInfoMap[id] }else { - odtDocument.close(callback) + id = Math.random().toString(); + editInfo = new ops.EditInfo(element, session.getOdtDocument()); + editInfoMarker = new gui.EditInfoMarker(editInfo, showEditInfoMarkers); + editInfoNode = element.getElementsByTagNameNS(editInfons, "editinfo")[0]; + editInfoNode.setAttributeNS(editInfons, "id", id); + editInfoMap[id] = editInfoMarker } - }) - }; - this.destroy = function(callback) { - odtDocument.destroy(callback) - }; - function init() { - self.setOperationRouter(new ops.TrivialOperationRouter) + editInfoMarker.addEdit(memberId, new Date(timestamp)) + } + function setEditInfoMarkerVisibility(visible) { + var editInfoMarker, keyname; + for(keyname in editInfoMap) { + if(editInfoMap.hasOwnProperty(keyname)) { + editInfoMarker = editInfoMap[keyname]; + if(visible) { + editInfoMarker.show() + }else { + editInfoMarker.hide() + } + } + } + } + function setCaretAvatarVisibility(visible) { + caretManager.getCarets().forEach(function(caret) { + if(visible) { + caret.showHandle() + }else { + caret.hideHandle() + } + }) + } + this.showEditInfoMarkers = function() { + if(showEditInfoMarkers) { + return + } + showEditInfoMarkers = true; + setEditInfoMarkerVisibility(showEditInfoMarkers) + }; + this.hideEditInfoMarkers = function() { + if(!showEditInfoMarkers) { + return + } + showEditInfoMarkers = false; + setEditInfoMarkerVisibility(showEditInfoMarkers) + }; + this.showCaretAvatars = function() { + if(showCaretAvatars) { + return + } + showCaretAvatars = true; + setCaretAvatarVisibility(showCaretAvatars) + }; + this.hideCaretAvatars = function() { + if(!showCaretAvatars) { + return + } + showCaretAvatars = false; + setCaretAvatarVisibility(showCaretAvatars) + }; + this.getSession = function() { + return session + }; + this.getCaret = function(memberid) { + return caretManager.getCaret(memberid) + }; + function renderMemberData(member) { + var memberId = member.getMemberId(), properties = member.getProperties(); + setAvatarInfoStyle(memberId, properties.fullName, properties.color); + if(localMemberId === memberId) { + setAvatarInfoStyle("", "", properties.color) + } + } + function onCursorAdded(cursor) { + var memberId = cursor.getMemberId(), properties = session.getOdtDocument().getMember(memberId).getProperties(), caret; + caretManager.registerCursor(cursor, showCaretAvatars, blinkOnRangeSelect); + selectionViewManager.registerCursor(cursor, true); + caret = caretManager.getCaret(memberId); + if(caret) { + caret.setAvatarImageUrl(properties.imageUrl); + caret.setColor(properties.color) + } + runtime.log("+++ View here +++ eagerly created an Caret for '" + memberId + "'! +++") + } + function onCursorMoved(cursor) { + var memberId = cursor.getMemberId(), localSelectionView = selectionViewManager.getSelectionView(localMemberId), shadowSelectionView = selectionViewManager.getSelectionView(gui.ShadowCursor.ShadowCursorMemberId), localCaret = caretManager.getCaret(localMemberId); + if(memberId === localMemberId) { + shadowSelectionView.hide(); + if(localSelectionView) { + localSelectionView.show() + } + if(localCaret) { + localCaret.show() + } + }else { + if(memberId === gui.ShadowCursor.ShadowCursorMemberId) { + shadowSelectionView.show(); + if(localSelectionView) { + localSelectionView.hide() + } + if(localCaret) { + localCaret.hide() + } + } + } + } + function onCursorRemoved(memberid) { + selectionViewManager.removeSelectionView(memberid) + } + function onParagraphChanged(info) { + highlightEdit(info.paragraphElement, info.memberId, info.timeStamp) + } + function requestRerenderOfSelectionViews() { + rerenderSelectionViews = true + } + function startRerenderLoop() { + rerenderIntervalId = runtime.getWindow().setInterval(function() { + if(rerenderSelectionViews) { + selectionViewManager.rerenderSelectionViews(); + rerenderSelectionViews = false + } + }, RERENDER_INTERVAL) + } + function stopRerenderLoop() { + runtime.getWindow().clearInterval(rerenderIntervalId) + } + this.destroy = function(callback) { + var odtDocument = session.getOdtDocument(), editInfoArray = Object.keys(editInfoMap).map(function(keyname) { + return editInfoMap[keyname] + }); + odtDocument.unsubscribe(ops.OdtDocument.signalMemberAdded, renderMemberData); + odtDocument.unsubscribe(ops.OdtDocument.signalMemberUpdated, renderMemberData); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); + odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, requestRerenderOfSelectionViews); + odtDocument.unsubscribe(ops.OdtDocument.signalTableAdded, requestRerenderOfSelectionViews); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, requestRerenderOfSelectionViews); + stopRerenderLoop(); + avatarInfoStyles.parentNode.removeChild(avatarInfoStyles); + (function destroyEditInfo(i, err) { + if(err) { + callback(err) + }else { + if(i < editInfoArray.length) { + editInfoArray[i].destroy(function(err) { + destroyEditInfo(i + 1, err) + }) + }else { + callback() + } + } + })(0, undefined) + }; + function init() { + var odtDocument = session.getOdtDocument(), head = document.getElementsByTagName("head")[0]; + odtDocument.subscribe(ops.OdtDocument.signalMemberAdded, renderMemberData); + odtDocument.subscribe(ops.OdtDocument.signalMemberUpdated, renderMemberData); + odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); + odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); + odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); + odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); + startRerenderLoop(); + odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, requestRerenderOfSelectionViews); + odtDocument.subscribe(ops.OdtDocument.signalTableAdded, requestRerenderOfSelectionViews); + odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, requestRerenderOfSelectionViews); + avatarInfoStyles = document.createElementNS(head.namespaceURI, "style"); + avatarInfoStyles.type = "text/css"; + avatarInfoStyles.media = "screen, print, handheld, projection"; + avatarInfoStyles.appendChild(document.createTextNode("@namespace editinfo url(urn:webodf:names:editinfo);")); + avatarInfoStyles.appendChild(document.createTextNode("@namespace dc url(http://purl.org/dc/elements/1.1/);")); + head.appendChild(avatarInfoStyles) + } + init() } - init() -}; + return SessionView +}(); var webodf_css = "@namespace draw url(urn:oasis:names:tc:opendocument:xmlns:drawing:1.0);\n@namespace fo url(urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0);\n@namespace office url(urn:oasis:names:tc:opendocument:xmlns:office:1.0);\n@namespace presentation url(urn:oasis:names:tc:opendocument:xmlns:presentation:1.0);\n@namespace style url(urn:oasis:names:tc:opendocument:xmlns:style:1.0);\n@namespace svg url(urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0);\n@namespace table url(urn:oasis:names:tc:opendocument:xmlns:table:1.0);\n@namespace text url(urn:oasis:names:tc:opendocument:xmlns:text:1.0);\n@namespace webodfhelper url(urn:webodf:names:helper);\n@namespace cursor url(urn:webodf:names:cursor);\n@namespace editinfo url(urn:webodf:names:editinfo);\n@namespace annotation url(urn:webodf:names:annotation);\n@namespace dc url(http://purl.org/dc/elements/1.1/);\n\noffice|document > *, office|document-content > * {\n display: none;\n}\noffice|body, office|document {\n display: inline-block;\n position: relative;\n}\n\ntext|p, text|h {\n display: block;\n padding: 0;\n margin: 0;\n line-height: normal;\n position: relative;\n min-height: 1.3em; /* prevent empty paragraphs and headings from collapsing if they are empty */\n}\n*[webodfhelper|containsparagraphanchor] {\n position: relative;\n}\ntext|s {\n white-space: pre;\n}\ntext|tab {\n display: inline;\n white-space: pre;\n}\ntext|tracked-changes {\n /*Consumers that do not support change tracking, should ignore changes.*/\n display: none;\n}\noffice|binary-data {\n display: none;\n}\noffice|text {\n display: block;\n text-align: left;\n overflow: visible;\n word-wrap: break-word;\n}\n\noffice|text::selection {\n /** Let's not draw selection highlight that overflows into the office|text\n * node when selecting content across several paragraphs\n */\n background: transparent;\n}\n\n.virtualSelections office|document *::selection {\n background: transparent;\n}\n.virtualSelections office|document *::-moz-selection {\n background: transparent;\n}\n\noffice|text * draw|text-box {\n/** only for text documents */\n display: block;\n border: 1px solid #d3d3d3;\n}\noffice|spreadsheet {\n display: block;\n border-collapse: collapse;\n empty-cells: show;\n font-family: sans-serif;\n font-size: 10pt;\n text-align: left;\n page-break-inside: avoid;\n overflow: hidden;\n}\noffice|presentation {\n display: inline-block;\n text-align: left;\n}\n#shadowContent {\n display: inline-block;\n text-align: left;\n}\ndraw|page {\n display: block;\n position: relative;\n overflow: hidden;\n}\npresentation|notes, presentation|footer-decl, presentation|date-time-decl {\n display: none;\n}\n@media print {\n draw|page {\n border: 1pt solid black;\n page-break-inside: avoid;\n }\n presentation|notes {\n /*TODO*/\n }\n}\noffice|spreadsheet text|p {\n border: 0px;\n padding: 1px;\n margin: 0px;\n}\noffice|spreadsheet table|table {\n margin: 3px;\n}\noffice|spreadsheet table|table:after {\n /* show sheet name the end of the sheet */\n /*content: attr(table|name);*/ /* gives parsing error in opera */\n}\noffice|spreadsheet table|table-row {\n counter-increment: row;\n}\noffice|spreadsheet table|table-row:before {\n width: 3em;\n background: #cccccc;\n border: 1px solid black;\n text-align: center;\n content: counter(row);\n display: table-cell;\n}\noffice|spreadsheet table|table-cell {\n border: 1px solid #cccccc;\n}\ntable|table {\n display: table;\n}\ndraw|frame table|table {\n width: 100%;\n height: 100%;\n background: white;\n}\ntable|table-header-rows {\n display: table-header-group;\n}\ntable|table-row {\n display: table-row;\n}\ntable|table-column {\n display: table-column;\n}\ntable|table-cell {\n width: 0.889in;\n display: table-cell;\n word-break: break-all; /* prevent long words from extending out the table cell */\n}\ndraw|frame {\n display: block;\n}\ndraw|image {\n display: block;\n width: 100%;\n height: 100%;\n top: 0px;\n left: 0px;\n background-repeat: no-repeat;\n background-size: 100% 100%;\n -moz-background-size: 100% 100%;\n}\n/* only show the first image in frame */\ndraw|frame > draw|image:nth-of-type(n+2) {\n display: none;\n}\ntext|list:before {\n display: none;\n content:\"\";\n}\ntext|list {\n counter-reset: list;\n}\ntext|list-item {\n display: block;\n}\ntext|number {\n display:none;\n}\n\ntext|a {\n color: blue;\n text-decoration: underline;\n cursor: pointer;\n}\ntext|note-citation {\n vertical-align: super;\n font-size: smaller;\n}\ntext|note-body {\n display: none;\n}\ntext|note:hover text|note-citation {\n background: #dddddd;\n}\ntext|note:hover text|note-body {\n display: block;\n left:1em;\n max-width: 80%;\n position: absolute;\n background: #ffffaa;\n}\nsvg|title, svg|desc {\n display: none;\n}\nvideo {\n width: 100%;\n height: 100%\n}\n\n/* below set up the cursor */\ncursor|cursor {\n display: inline;\n width: 0px;\n height: 1em;\n /* making the position relative enables the avatar to use\n the cursor as reference for its absolute position */\n position: relative;\n z-index: 1;\n}\ncursor|cursor > span {\n /* IMPORTANT: when changing these values ensure DEFAULT_CARET_TOP and DEFAULT_CARET_HEIGHT\n in Caret.js remain in sync */\n display: inline;\n position: absolute;\n top: 5%; /* push down the caret; 0px can do the job, 5% looks better, 10% is a bit over */\n height: 1em;\n border-left: 2px solid black;\n outline: none;\n}\n\ncursor|cursor > div {\n padding: 3px;\n box-shadow: 0px 0px 5px rgba(50, 50, 50, 0.75);\n border: none !important;\n border-radius: 5px;\n opacity: 0.3;\n}\n\ncursor|cursor > div > img {\n border-radius: 5px;\n}\n\ncursor|cursor > div.active {\n opacity: 0.8;\n}\n\ncursor|cursor > div:after {\n content: ' ';\n position: absolute;\n width: 0px;\n height: 0px;\n border-style: solid;\n border-width: 8.7px 5px 0 5px;\n border-color: black transparent transparent transparent;\n\n top: 100%;\n left: 43%;\n}\n\n\n.editInfoMarker {\n position: absolute;\n width: 10px;\n height: 100%;\n left: -20px;\n opacity: 0.8;\n top: 0;\n border-radius: 5px;\n background-color: transparent;\n box-shadow: 0px 0px 5px rgba(50, 50, 50, 0.75);\n}\n.editInfoMarker:hover {\n box-shadow: 0px 0px 8px rgba(0, 0, 0, 1);\n}\n\n.editInfoHandle {\n position: absolute;\n background-color: black;\n padding: 5px;\n border-radius: 5px;\n opacity: 0.8;\n box-shadow: 0px 0px 5px rgba(50, 50, 50, 0.75);\n bottom: 100%;\n margin-bottom: 10px;\n z-index: 3;\n left: -25px;\n}\n.editInfoHandle:after {\n content: ' ';\n position: absolute;\n width: 0px;\n height: 0px;\n border-style: solid;\n border-width: 8.7px 5px 0 5px;\n border-color: black transparent transparent transparent;\n\n top: 100%;\n left: 5px;\n}\n.editInfo {\n font-family: sans-serif;\n font-weight: normal;\n font-style: normal;\n text-decoration: none;\n color: white;\n width: 100%;\n height: 12pt;\n}\n.editInfoColor {\n float: left;\n width: 10pt;\n height: 10pt;\n border: 1px solid white;\n}\n.editInfoAuthor {\n float: left;\n margin-left: 5pt;\n font-size: 10pt;\n text-align: left;\n height: 12pt;\n line-height: 12pt;\n}\n.editInfoTime {\n float: right;\n margin-left: 30pt;\n font-size: 8pt;\n font-style: italic;\n color: yellow;\n height: 12pt;\n line-height: 12pt;\n}\n\n.annotationWrapper {\n display: inline;\n position: relative;\n}\n\n.annotationRemoveButton:before {\n content: '\u00d7';\n color: white;\n padding: 5px;\n line-height: 1em;\n}\n\n.annotationRemoveButton {\n width: 20px;\n height: 20px;\n border-radius: 10px;\n background-color: black;\n box-shadow: 0px 0px 5px rgba(50, 50, 50, 0.75);\n position: absolute;\n top: -10px;\n left: -10px;\n z-index: 3;\n text-align: center;\n font-family: sans-serif;\n font-style: normal;\n font-weight: normal;\n text-decoration: none;\n font-size: 15px;\n}\n.annotationRemoveButton:hover {\n cursor: pointer;\n box-shadow: 0px 0px 5px rgba(0, 0, 0, 1);\n}\n\n.annotationNote {\n width: 4cm;\n position: absolute;\n display: inline;\n z-index: 10;\n}\n.annotationNote > office|annotation {\n display: block;\n text-align: left;\n}\n\n.annotationConnector {\n position: absolute;\n display: inline;\n z-index: 2;\n border-top: 1px dashed brown;\n}\n.annotationConnector.angular {\n -moz-transform-origin: left top;\n -webkit-transform-origin: left top;\n -ms-transform-origin: left top;\n transform-origin: left top;\n}\n.annotationConnector.horizontal {\n left: 0;\n}\n.annotationConnector.horizontal:before {\n content: '';\n display: inline;\n position: absolute;\n width: 0px;\n height: 0px;\n border-style: solid;\n border-width: 8.7px 5px 0 5px;\n border-color: brown transparent transparent transparent;\n top: -1px;\n left: -5px;\n}\n\noffice|annotation {\n width: 100%;\n height: 100%;\n display: none;\n background: rgb(198, 238, 184);\n background: -moz-linear-gradient(90deg, rgb(198, 238, 184) 30%, rgb(180, 196, 159) 100%);\n background: -webkit-linear-gradient(90deg, rgb(198, 238, 184) 30%, rgb(180, 196, 159) 100%);\n background: -o-linear-gradient(90deg, rgb(198, 238, 184) 30%, rgb(180, 196, 159) 100%);\n background: -ms-linear-gradient(90deg, rgb(198, 238, 184) 30%, rgb(180, 196, 159) 100%);\n background: linear-gradient(180deg, rgb(198, 238, 184) 30%, rgb(180, 196, 159) 100%);\n box-shadow: 0 3px 4px -3px #ccc;\n}\n\noffice|annotation > dc|creator {\n display: block;\n font-size: 10pt;\n font-weight: normal;\n font-style: normal;\n font-family: sans-serif;\n color: white;\n background-color: brown;\n padding: 4px;\n}\noffice|annotation > dc|date {\n display: block;\n font-size: 10pt;\n font-weight: normal;\n font-style: normal;\n font-family: sans-serif;\n border: 4px solid transparent;\n}\noffice|annotation > text|list {\n display: block;\n padding: 5px;\n}\n\n/* This is very temporary CSS. This must go once\n * we start bundling webodf-default ODF styles for annotations.\n */\noffice|annotation text|p {\n font-size: 10pt;\n color: black;\n font-weight: normal;\n font-style: normal;\n text-decoration: none;\n font-family: sans-serif;\n}\n\ndc|*::selection {\n background: transparent;\n}\ndc|*::-moz-selection {\n background: transparent;\n}\n\n#annotationsPane {\n background-color: #EAEAEA;\n width: 4cm;\n height: 100%;\n display: none;\n position: absolute;\n outline: 1px solid #ccc;\n}\n\n.annotationHighlight {\n background-color: yellow;\n position: relative;\n}\n\n.selectionOverlay {\n position: absolute;\n z-index: 15;\n opacity: 0.2;\n pointer-events: none;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n}\n\n#imageSelector {\n display: none;\n position: absolute;\n border-style: solid;\n border-color: black;\n}\n\n#imageSelector > div {\n width: 5px;\n height: 5px;\n display: block;\n position: absolute;\n border: 1px solid black;\n background-color: #ffffff;\n}\n\n#imageSelector > .topLeft {\n top: -4px;\n left: -4px;\n}\n\n#imageSelector > .topRight {\n top: -4px;\n right: -4px;\n}\n\n#imageSelector > .bottomRight {\n right: -4px;\n bottom: -4px;\n}\n\n#imageSelector > .bottomLeft {\n bottom: -4px;\n left: -4px;\n}\n\n#imageSelector > .topMiddle {\n top: -4px;\n left: 50%;\n margin-left: -2.5px; /* half of the width defined in #imageSelector > div */\n}\n\n#imageSelector > .rightMiddle {\n top: 50%;\n right: -4px;\n margin-top: -2.5px; /* half of the height defined in #imageSelector > div */\n}\n\n#imageSelector > .bottomMiddle {\n bottom: -4px;\n left: 50%;\n margin-left: -2.5px; /* half of the width defined in #imageSelector > div */\n}\n\n#imageSelector > .leftMiddle {\n top: 50%;\n left: -4px;\n margin-top: -2.5px; /* half of the height defined in #imageSelector > div */\n}\n"; -var webodf_version = "0.4.2-1451-gc9cf878"; diff --git a/js/3rdparty/webodf/webodf.js b/js/3rdparty/webodf/webodf.js index 306f4d94..f483b7d2 100644 --- a/js/3rdparty/webodf/webodf.js +++ b/js/3rdparty/webodf/webodf.js @@ -1,121 +1,59 @@ // Input 0 -/* - - - Copyright (C) 2012 KO GmbH - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -var core={},gui={},xmldom={},odf={},ops={}; +var webodf_version="0.4.2-1556-gf8a94ee"; // Input 1 -function Runtime(){}Runtime.prototype.getVariable=function(g){};Runtime.prototype.toJson=function(g){};Runtime.prototype.fromJson=function(g){};Runtime.prototype.byteArrayFromString=function(g,l){};Runtime.prototype.byteArrayToString=function(g,l){};Runtime.prototype.read=function(g,l,c,m){};Runtime.prototype.readFile=function(g,l,c){};Runtime.prototype.readFileSync=function(g,l){};Runtime.prototype.loadXML=function(g,l){};Runtime.prototype.writeFile=function(g,l,c){}; -Runtime.prototype.isFile=function(g,l){};Runtime.prototype.getFileSize=function(g,l){};Runtime.prototype.deleteFile=function(g,l){};Runtime.prototype.log=function(g,l){};Runtime.prototype.setTimeout=function(g,l){};Runtime.prototype.clearTimeout=function(g){};Runtime.prototype.libraryPaths=function(){};Runtime.prototype.type=function(){};Runtime.prototype.getDOMImplementation=function(){};Runtime.prototype.parseXML=function(g){};Runtime.prototype.getWindow=function(){}; -Runtime.prototype.assert=function(g,l,c){};var IS_COMPILED_CODE=!0; -Runtime.byteArrayToString=function(g,l){function c(c){var b="",p,r=c.length;for(p=0;pk?d.push(k):(p+=1,a=c[p],194<=k&&224>k?d.push((k&31)<<6|a&63):(p+=1,e=c[p],224<=k&&240>k?d.push((k&15)<<12|(a&63)<<6|e&63):(p+=1,h=c[p],240<=k&&245>k&&(k=(k&7)<<18|(a&63)<<12|(e&63)<<6|h&63,k-=65536,d.push((k>>10)+55296,(k&1023)+56320))))),1E3===d.length&&(b+=String.fromCharCode.apply(null, -d),d.length=0);return b+String.fromCharCode.apply(null,d)}var f;"utf8"===l?f=m(g):("binary"!==l&&this.log("Unsupported encoding: "+l),f=c(g));return f};Runtime.getVariable=function(g){try{return eval(g)}catch(l){}};Runtime.toJson=function(g){return JSON.stringify(g)};Runtime.fromJson=function(g){return JSON.parse(g)};Runtime.getFunctionName=function(g){return void 0===g.name?(g=/function\s+(\w+)/.exec(g))&&g[1]:g.name}; -function BrowserRuntime(g){function l(b,r){var d,k,a;void 0!==r?a=b:r=b;g?(k=g.ownerDocument,a&&(d=k.createElement("span"),d.className=a,d.appendChild(k.createTextNode(a)),g.appendChild(d),g.appendChild(k.createTextNode(" "))),d=k.createElement("span"),0e?(k[h]=e,h+=1):2048>e?(k[h]=192|e>>>6,k[h+1]=128|e&63,h+=2):(k[h]=224|e>>>12&15,k[h+1]=128|e>>>6&63,k[h+2]=128|e&63,h+=3)}else for("binary"!==c&&n.log("unknown encoding: "+c),d=b.length,k=new Uint8Array(new ArrayBuffer(d)),a=0;ak.status||0===k.status?d(null):d("Status "+String(k.status)+": "+k.responseText||k.statusText):d("File "+c+" is empty."))};a=r.buffer&&!k.sendAsBinary?r.buffer:n.byteArrayToString(r,"binary");try{k.sendAsBinary?k.sendAsBinary(a):k.send(a)}catch(e){n.log("HUH? "+e+" "+r),d(e.message)}};this.deleteFile=function(c,r){delete b[c];var d=new XMLHttpRequest;d.open("DELETE",c,!0);d.onreadystatechange=function(){4===d.readyState&& -(200>d.status&&300<=d.status?r(d.responseText):r(null))};d.send(null)};this.loadXML=function(b,c){var d=new XMLHttpRequest;d.open("GET",b,!0);d.overrideMimeType&&d.overrideMimeType("text/xml");d.onreadystatechange=function(){4===d.readyState&&(0!==d.status||d.responseText?200===d.status||0===d.status?c(null,d.responseXML):c(d.responseText,null):c("File "+b+" is empty.",null))};try{d.send(null)}catch(k){c(k.message,null)}};this.isFile=function(b,c){n.getFileSize(b,function(d){c(-1!==d)})};this.getFileSize= -function(c,r){if(b.hasOwnProperty(c)&&"string"!==typeof b[c])r(b[c].length);else{var d=new XMLHttpRequest;d.open("HEAD",c,!0);d.onreadystatechange=function(){if(4===d.readyState){var b=d.getResponseHeader("Content-Length");b?r(parseInt(b,10)):f(c,"binary",function(a,e){a?r(-1):r(e.length)})}};d.send(null)}};this.log=l;this.assert=function(b,c,d){if(!b)throw l("alert","ASSERTION FAILED:\n"+c),d&&d(),c;};this.setTimeout=function(b,c){return setTimeout(function(){b()},c)};this.clearTimeout=function(b){clearTimeout(b)}; -this.libraryPaths=function(){return["lib"]};this.setCurrentDirectory=function(){};this.type=function(){return"BrowserRuntime"};this.getDOMImplementation=function(){return window.document.implementation};this.parseXML=function(b){return(new DOMParser).parseFromString(b,"text/xml")};this.exit=function(b){l("Calling exit with code "+String(b)+", but exit() is not implemented.")};this.getWindow=function(){return window}} -function NodeJSRuntime(){function g(b){var d=b.length,k,a=new Uint8Array(new ArrayBuffer(d));for(k=0;k").implementation} -function RhinoRuntime(){function g(b,c){var f;void 0!==c?f=b:c=b;"alert"===f&&print("\n!!!!! ALERT !!!!!");print(c);"alert"===f&&print("!!!!! ALERT !!!!!")}var l=this,c=Packages.javax.xml.parsers.DocumentBuilderFactory.newInstance(),m,f,n="";c.setValidating(!1);c.setNamespaceAware(!0);c.setExpandEntityReferences(!1);c.setSchema(null);f=Packages.org.xml.sax.EntityResolver({resolveEntity:function(b,c){var f=new Packages.java.io.FileReader(c);return new Packages.org.xml.sax.InputSource(f)}});m=c.newDocumentBuilder(); -m.setEntityResolver(f);this.byteArrayFromString=function(b,c){var f=[],d,k=b.length;for(d=0;df?d.push(f):(h+=1,a=e[h],194<=f&&224>f?d.push((f&31)<<6|a&63):(h+=1,c=e[h],224<=f&&240>f?d.push((f&15)<<12|(a&63)<<6|c&63):(h+=1,p=e[h],240<=f&&245>f&&(f=(f&7)<<18|(a&63)<<12|(c&63)<<6|p&63,f-=65536,d.push((f>>10)+55296,(f&1023)+56320))))),1E3===d.length&&(b+=String.fromCharCode.apply(null, +d),d.length=0);return b+String.fromCharCode.apply(null,d)}var m;"utf8"===k?m=n(g):("binary"!==k&&this.log("Unsupported encoding: "+k),m=e(g));return m};Runtime.getVariable=function(g){try{return eval(g)}catch(k){}};Runtime.toJson=function(g){return JSON.stringify(g)};Runtime.fromJson=function(g){return JSON.parse(g)};Runtime.getFunctionName=function(g){return void 0===g.name?(g=/function\s+(\w+)/.exec(g))&&g[1]:g.name}; +function BrowserRuntime(g){function k(h,b){var d,f,a;void 0!==b?a=h:b=h;g?(f=g.ownerDocument,a&&(d=f.createElement("span"),d.className=a,d.appendChild(f.createTextNode(a)),g.appendChild(d),g.appendChild(f.createTextNode(" "))),d=f.createElement("span"),0c?(f[p]=c,p+=1):2048>c?(f[p]=192|c>>>6,f[p+1]=128|c&63,p+=2):(f[p]=224|c>>>12&15,f[p+1]=128|c>>>6&63,f[p+2]=128|c&63,p+=3)}else for("binary"!==e&&q.log("unknown encoding: "+e),d=b.length,f=new Uint8Array(new ArrayBuffer(d)),a=0;af.status||0===f.status?d(null):d("Status "+String(f.status)+": "+f.responseText||f.statusText):d("File "+h+" is empty."))};a=e.buffer&&!f.sendAsBinary?e.buffer:q.byteArrayToString(e,"binary");try{f.sendAsBinary?f.sendAsBinary(a):f.send(a)}catch(c){q.log("HUH? "+c+" "+e),d(c.message)}};this.deleteFile=function(h,e){delete b[h];var d=new XMLHttpRequest;d.open("DELETE",h,!0);d.onreadystatechange=function(){4===d.readyState&& +(200>d.status&&300<=d.status?e(d.responseText):e(null))};d.send(null)};this.loadXML=function(b,e){var d=new XMLHttpRequest;d.open("GET",b,!0);d.overrideMimeType&&d.overrideMimeType("text/xml");d.onreadystatechange=function(){4===d.readyState&&(0!==d.status||d.responseText?200===d.status||0===d.status?e(null,d.responseXML):e(d.responseText,null):e("File "+b+" is empty.",null))};try{d.send(null)}catch(f){e(f.message,null)}};this.isFile=function(b,e){q.getFileSize(b,function(d){e(-1!==d)})};this.getFileSize= +function(h,e){if(b.hasOwnProperty(h)&&"string"!==typeof b[h])e(b[h].length);else{var d=new XMLHttpRequest;d.open("HEAD",h,!0);d.onreadystatechange=function(){if(4===d.readyState){var f=d.getResponseHeader("Content-Length");f?e(parseInt(f,10)):m(h,"binary",function(a,c){a?e(-1):e(c.length)})}};d.send(null)}};this.log=k;this.assert=function(b,e,d){if(!b)throw k("alert","ASSERTION FAILED:\n"+e),d&&d(),e;};this.setTimeout=function(b,e){return setTimeout(function(){b()},e)};this.clearTimeout=function(b){clearTimeout(b)}; +this.libraryPaths=function(){return["lib"]};this.setCurrentDirectory=function(){};this.currentDirectory=function(){return""};this.type=function(){return"BrowserRuntime"};this.getDOMImplementation=function(){return window.document.implementation};this.parseXML=function(b){return(new DOMParser).parseFromString(b,"text/xml")};this.exit=function(b){k("Calling exit with code "+String(b)+", but exit() is not implemented.")};this.getWindow=function(){return window}} +function NodeJSRuntime(){function g(b){var d=b.length,f,a=new Uint8Array(new ArrayBuffer(d));for(f=0;f").implementation} +function RhinoRuntime(){function g(b,e){var d;void 0!==e?d=b:e=b;"alert"===d&&print("\n!!!!! ALERT !!!!!");print(e);"alert"===d&&print("!!!!! ALERT !!!!!")}var k=this,e={},n=e.javax.xml.parsers.DocumentBuilderFactory.newInstance(),m,q,b="";n.setValidating(!1);n.setNamespaceAware(!0);n.setExpandEntityReferences(!1);n.setSchema(null);q=e.org.xml.sax.EntityResolver({resolveEntity:function(b,m){var d=new e.java.io.FileReader(m);return new e.org.xml.sax.InputSource(d)}});m=n.newDocumentBuilder();m.setEntityResolver(q); +this.byteArrayFromString=function(b,e){var d,f=b.length,a=new Uint8Array(new ArrayBuffer(f));for(d=0;d>>18],h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e>>>12&63],h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e>>>6&63],h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e& -63];b===d+1?(e=a[b]<<4,h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e>>>6],h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e&63],h+="=="):b===d&&(e=a[b]<<10|a[b+1]<<2,h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e>>>12],h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e>>>6&63],h+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e&63],h+="=");return h}function c(a){a=a.replace(/[^A-Za-z0-9+\/]+/g, -"");var e=a.length,h=new Uint8Array(new ArrayBuffer(3*e)),b=a.length%4,d=0,c,k;for(c=0;c>16,h[d+1]=k>>8&255,h[d+2]=k&255,d+=3;e=3*e-[0,0,2,1][b];return h.subarray(0,e)}function m(a){var e,h,b=a.length,d=0,c=new Uint8Array(new ArrayBuffer(3*b));for(e=0;eh?c[d++]=h:(2048>h?c[d++]=192|h>>>6:(c[d++]=224|h>>>12&15,c[d++]=128|h>>>6&63),c[d++]=128|h&63);return c.subarray(0, -d)}function f(a){var e,h,b,d,c=a.length,k=new Uint8Array(new ArrayBuffer(c)),q=0;for(e=0;eh?k[q++]=h:(e+=1,b=a[e],224>h?k[q++]=(h&31)<<6|b&63:(e+=1,d=a[e],k[q++]=(h&15)<<12|(b&63)<<6|d&63));return k.subarray(0,q)}function n(a){return l(g(a))}function b(a){return String.fromCharCode.apply(String,c(a))}function p(a){return f(g(a))}function r(a){a=f(a);for(var e="",h=0;he?k+=String.fromCharCode(e):(c+=1,b=a.charCodeAt(c)&255,224>e?k+=String.fromCharCode((e&31)<<6|b&63):(c+=1,d=a.charCodeAt(c)&255,k+=String.fromCharCode((e&15)<<12|(b&63)<<6|d&63)));return k}function k(a,e){function h(){var k=c+1E5;k>a.length&&(k=a.length);b+=d(a,c,k);c=k;k=c===a.length;e(b,k)&&!k&&runtime.setTimeout(h,0)}var b="",c=0;1E5>a.length?e(d(a,0,a.length),!0):("string"!==typeof a&&(a=a.slice()),h())}function a(a){return m(g(a))}function e(a){return String.fromCharCode.apply(String, -m(a))}function h(a){return String.fromCharCode.apply(String,m(g(a)))}var q=function(a){var e={},h,b;h=0;for(b=a.length;h>>8):(da(e&255),da(e>>>8))},qa=function(){t=(t<<5^h[z+3-1]&255)&8191;s=x[32768+t];x[z&32767]=s;x[32768+t]=z},aa=function(a,e){y>16-e?(v|=a<>16-y,y+=e-16):(v|=a<a;a++)h[a]=h[a+32768];K-=32768;z-=32768;w-=32768;for(a=0;8192>a;a++)e=x[32768+a],x[32768+a]=32768<=e?e-32768:0;for(a=0;32768>a;a++)e=x[a],x[a]=32768<=e?e-32768:0;b+=32768}F||(a=Aa(h,z+Q,b),0>=a?F=!0:Q+=a)},ya=function(a){var e=$,b=z,d,c=M,k=32506=ma&&(e>>=2);do if(d=a,h[d+c]===s&&h[d+c-1]===f&&h[d]===h[b]&&h[++d]===h[b+1]){b+= -2;d++;do++b;while(h[b]===h[++d]&&h[++b]===h[++d]&&h[++b]===h[++d]&&h[++b]===h[++d]&&h[++b]===h[++d]&&h[++b]===h[++d]&&h[++b]===h[++d]&&h[++b]===h[++d]&&bc){K=a;c=d;if(258<=d)break;f=h[b+c-1];s=h[b+c]}a=x[a&32767]}while(a>k&&0!==--e);return c},ra=function(a,e){u[U++]=e;0===a?ba[e].fc++:(a--,ba[T[e]+256+1].fc++,ga[(256>a?P[a]:P[256+(a>>7)])&255].fc++,q[H++]=a,fa|=W);W<<=1;0===(U&7)&&(E[la++]=fa,fa=0,W=1);if(2d;d++)h+=ga[d].fc* -(5+oa[d]);h>>=3;if(H>=1,h<<=1;while(0<--e);return h>>1},Ea=function(a,e){var h=[];h.length=16;var b=0,d;for(d=1;15>=d;d++)b=b+N[d-1]<<1,h[d]=b;for(b=0;b<=e;b++)d=a[b].dl,0!==d&&(a[b].fc=Ba(h[d]++,d))},Da=function(a){var e=a.dyn_tree,h=a.static_tree,b=a.elems,d,c=-1, -k=b;Z=0;ka=573;for(d=0;dZ;)d=O[++Z]=2>c?++c:0,e[d].fc=1,ca[d]=0,ja--,null!==h&&(C-=h[d].dl);a.max_code=c;for(d=Z>>1;1<=d;d--)xa(e,d);do d=O[1],O[1]=O[Z--],xa(e,1),h=O[1],O[--ka]=d,O[--ka]=h,e[k].fc=e[d].fc+e[h].fc,ca[k]=ca[d]>ca[h]+1?ca[d]:ca[h]+1,e[d].dl=e[h].dl=k,O[1]=k++,xa(e,1);while(2<=Z);O[--ka]=O[1];k=a.dyn_tree;d=a.extra_bits;var b=a.extra_base,h=a.max_code,q=a.max_length,f=a.static_tree,s,g,l,p,B=0;for(g=0;15>=g;g++)N[g]=0;k[O[ka]].dl= -0;for(a=ka+1;573>a;a++)s=O[a],g=k[k[s].dl].dl+1,g>q&&(g=q,B++),k[s].dl=g,s>h||(N[g]++,l=0,s>=b&&(l=d[s-b]),p=k[s].fc,ja+=p*(g+l),null!==f&&(C+=p*(f[s].dl+l)));if(0!==B){do{for(g=q-1;0===N[g];)g--;N[g]--;N[g+1]+=2;N[q]--;B-=2}while(0h||(k[d].dl!==g&&(ja+=(g-k[d].dl)*k[d].fc,k[d].fc=g),s--)}Ea(e,c)},Fa=function(a,e){var h,b=-1,d,c=a[0].dl,k=0,q=7,f=4;0===c&&(q=138,f=3);a[e+1].dl=65535;for(h=0;h<=e;h++)d=c,c=a[h+1].dl,++k=k?V[17].fc++:V[18].fc++,k=0,b=d,0===c?(q=138,f=3):d===c?(q=6,f=3):(q=7,f=4))},Ga=function(){8h?P[h]:P[256+(h>>7)])&255,J(f,e),g=oa[f],0!==g&&(h-=ea[f],aa(h,g))),k>>=1;while(b=k?(J(17,V),aa(k-3,3)):(J(18,V),aa(k-11,7));k=0;b=d;0===c?(q=138,f=3):d===c?(q=6,f=3):(q=7,f=4)}},Ja=function(){var a;for(a=0;286>a;a++)ba[a].fc=0;for(a=0;30>a;a++)ga[a].fc=0;for(a=0;19>a;a++)V[a].fc=0;ba[256].fc=1;fa=U=H=la=ja=C=0;W=1},Ca=function(a){var e,b,d,c;c=z-w;E[la]=fa;Da(G);Da(I);Fa(ba,G.max_code);Fa(ga,I.max_code);Da(ha);for(d=18;3<=d&&0===V[va[d]].dl;d--); -ja+=3*(d+1)+14;e=ja+3+7>>3;b=C+3+7>>3;b<=e&&(e=b);if(c+4<=e&&0<=w)for(aa(0+a,3),Ga(),X(c),X(~c),d=0;db.len&&(f=b.len);for(g=0;gk-a&&(f=k-a);for(g=0;g -l;l++)for(L[l]=g,f=0;f<1<l;l++)for(ea[l]=g,f=0;f<1<>=7;30>l;l++)for(ea[l]=g<<7,f=0;f<1<=f;f++)N[f]=0;for(f=0;143>=f;)S[f++].dl=8,N[8]++;for(;255>=f;)S[f++].dl=9,N[9]++;for(;279>=f;)S[f++].dl=7,N[7]++;for(;287>=f;)S[f++].dl=8,N[8]++;Ea(S,287);for(f=0;30>f;f++)Y[f].dl=5,Y[f].fc=Ba(f,5);Ja()}for(f=0;8192>f;f++)x[32768+f]=0;ia=wa[R].max_lazy;ma=wa[R].good_length;$=wa[R].max_chain;w=z=0;Q=Aa(h,0,65536); -if(0>=Q)F=!0,Q=0;else{for(F=!1;262>Q&&!F;)ua();for(f=t=0;2>f;f++)t=(t<<5^h[f]&255)&8191}b=null;a=k=0;3>=R?(M=2,B=0):(B=2,A=0);e=!1}r=!0;if(0===Q)return e=!0,0}f=Ka(d,c,q);if(f===q)return q;if(e)return f;if(3>=R)for(;0!==Q&&null===b;){qa();0!==s&&32506>=z-s&&(B=ya(s),B>Q&&(B=Q));if(3<=B)if(l=ra(z-K,B-3),Q-=B,B<=ia){B--;do z++,qa();while(0!==--B);z++}else z+=B,B=0,t=h[z]&255,t=(t<<5^h[z+1]&255)&8191;else l=ra(0,h[z]&255),Q--,z++;l&&(Ca(0),w=z);for(;262>Q&&!F;)ua()}else for(;0!==Q&&null===b;){qa();M= -B;D=K;B=2;0!==s&&M=z-s&&(B=ya(s),B>Q&&(B=Q),3===B&&4096Q&&!F;)ua()}0===Q&&(0!==A&&ra(0,h[z-1]&255),Ca(1),e=!0);return f+Ka(d,f+c,q-f)};this.deflate=function(a,e){var c,k;sa=a;na=0;"undefined"===String(typeof e)&&(e=6);(c=e)?1>c?c=1:9c;c++)ba[c]=new g;ga=[];ga.length=61;for(c=0;61>c;c++)ga[c]=new g;S=[];S.length=288;for(c=0;288>c;c++)S[c]=new g;Y=[];Y.length=30;for(c=0;30>c;c++)Y[c]=new g;V=[];V.length=39;for(c=0;39>c;c++)V[c]=new g;G=new l;I=new l;ha=new l;N=[];N.length=16;O=[];O.length=573;ca=[];ca.length=573;T=[];T.length=256;P=[];P.length=512;L=[];L.length=29;ea=[];ea.length=30;E=[];E.length=1024}var s=Array(1024),B=[],m=[];for(c=La(s, -0,s.length);0>>18],d+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c>>>12&63],d+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c>>>6&63],d+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c& +63];b===f+1?(c=a[b]<<4,d+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c>>>6],d+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c&63],d+="=="):b===f&&(c=a[b]<<10|a[b+1]<<2,d+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c>>>12],d+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c>>>6&63],d+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c&63],d+="=");return d}function e(a){a=a.replace(/[^A-Za-z0-9+\/]+/g, +"");var c=a.length,d=new Uint8Array(new ArrayBuffer(3*c)),b=a.length%4,f=0,p,e;for(p=0;p>16,d[f+1]=e>>8&255,d[f+2]=e&255,f+=3;c=3*c-[0,0,2,1][b];return d.subarray(0,c)}function n(a){var c,d,b=a.length,f=0,p=new Uint8Array(new ArrayBuffer(3*b));for(c=0;cd?p[f++]=d:(2048>d?p[f++]=192|d>>>6:(p[f++]=224|d>>>12&15,p[f++]=128|d>>>6&63),p[f++]=128|d&63);return p.subarray(0, +f)}function m(a){var c,d,b,f,p=a.length,l=new Uint8Array(new ArrayBuffer(p)),e=0;for(c=0;cd?l[e++]=d:(c+=1,b=a[c],224>d?l[e++]=(d&31)<<6|b&63:(c+=1,f=a[c],l[e++]=(d&15)<<12|(b&63)<<6|f&63));return l.subarray(0,e)}function q(a){return k(g(a))}function b(a){return String.fromCharCode.apply(String,e(a))}function h(a){return m(g(a))}function r(a){a=m(a);for(var c="",d=0;dc?l+=String.fromCharCode(c):(p+=1,b=a.charCodeAt(p)&255,224>c?l+=String.fromCharCode((c&31)<<6|b&63):(p+=1,f=a.charCodeAt(p)&255,l+=String.fromCharCode((c&15)<<12|(b&63)<<6|f&63)));return l}function f(a,c){function b(){var l=p+1E5;l>a.length&&(l=a.length);f+=d(a,p,l);p=l;l=p===a.length;c(f,l)&&!l&&runtime.setTimeout(b,0)}var f="",p=0;1E5>a.length?c(d(a,0,a.length),!0):("string"!==typeof a&&(a=a.slice()),b())}function a(a){return n(g(a))}function c(a){return String.fromCharCode.apply(String, +n(a))}function p(a){return String.fromCharCode.apply(String,n(g(a)))}var l=function(a){var c={},d,b;d=0;for(b=a.length;df-m&&(f=Math.max(2*f,m+b),b=new Uint8Array(new ArrayBuffer(f)),b.set(n),n=b)}var c=this,m=0,f=1024,n=new Uint8Array(new ArrayBuffer(f));this.appendByteArrayWriter=function(b){c.appendByteArray(b.getByteArray())};this.appendByteArray=function(b){var c=b.length;l(c);n.set(b,m);m+=c};this.appendArray=function(b){var c=b.length;l(c);n.set(b,m);m+=c};this.appendUInt16LE=function(b){c.appendArray([b&255,b>>8&255])};this.appendUInt32LE=function(b){c.appendArray([b& -255,b>>8&255,b>>16&255,b>>24&255])};this.appendString=function(b){c.appendByteArray(runtime.byteArrayFromString(b,g))};this.getLength=function(){return m};this.getByteArray=function(){var b=new Uint8Array(new ArrayBuffer(m));b.set(n.subarray(0,m));return b}}; +core.ByteArrayWriter=function(g){function k(b){b>m-n&&(m=Math.max(2*m,n+b),b=new Uint8Array(new ArrayBuffer(m)),b.set(q),q=b)}var e=this,n=0,m=1024,q=new Uint8Array(new ArrayBuffer(m));this.appendByteArrayWriter=function(b){e.appendByteArray(b.getByteArray())};this.appendByteArray=function(b){var e=b.length;k(e);q.set(b,n);n+=e};this.appendArray=function(b){var e=b.length;k(e);q.set(b,n);n+=e};this.appendUInt16LE=function(b){e.appendArray([b&255,b>>8&255])};this.appendUInt32LE=function(b){e.appendArray([b& +255,b>>8&255,b>>16&255,b>>24&255])};this.appendString=function(b){e.appendByteArray(runtime.byteArrayFromString(b,g))};this.getLength=function(){return n};this.getByteArray=function(){var b=new Uint8Array(new ArrayBuffer(n));b.set(q.subarray(0,n));return b}}; // Input 6 -core.RawInflate=function(){var g,l,c=null,m,f,n,b,p,r,d,k,a,e,h,q,u,x,v=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535],y=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],w=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,99,99],t=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],s=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],D=[16,17,18, -0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],A=function(){this.list=this.next=null},B=function(){this.n=this.b=this.e=0;this.t=null},M=function(a,e,h,b,d,c){this.BMAX=16;this.N_MAX=288;this.status=0;this.root=null;this.m=0;var k=Array(this.BMAX+1),q,f,g,s,l,p,m,r=Array(this.BMAX+1),n,w,u,t=new B,z=Array(this.BMAX);s=Array(this.N_MAX);var K,x=Array(this.BMAX+1),v,F,M;M=this.root=null;for(l=0;ll&&(c=l);for(v=1<(v-=k[p])){this.status=2;this.m=c;return}if(0>(v-=k[l]))this.status=2,this.m=c;else{k[l]+=v;x[1]=p=0;n=k;w=1;for(u=2;0<--l;)x[u++]=p+=n[w++];n=a;l=w=0;do 0!=(p=n[w++])&&(s[x[p]++]=l);while(++lK+r[1+s];){K+=r[1+s];s++;F=(F=g-K)>c?c:F;if((f=1<<(p=m-K))>a+1)for(f-=a+1,u=m;++pq&&K>K-r[s],z[s-1][p].e=t.e,z[s-1][p].b=t.b,z[s-1][p].n=t.n,z[s-1][p].t=t.t)}t.b=m-K;w>=e?t.e=99:n[w]n[w]?16:15,t.n=n[w++]): -(t.e=d[n[w]-h],t.n=b[n[w++]-h]);f=1<>K;p>=1)l^=p;for(l^=p;(l&(1<>=a;b-=a},Q=function(b,c,f){var s,B,m;if(0==f)return 0;for(m=0;;){z(h);B=a.list[K(h)];for(s=B.e;16 -f;f++)r[D[f]]=0;h=7;f=new M(r,19,19,null,null,h);if(0!=f.status)return-1;a=f.root;h=f.m;l=B+m;for(k=g=0;kf)r[k++]=g=f;else if(16==f){z(2);f=3+K(2);F(2);if(k+f>l)return-1;for(;0l)return-1;for(;0I;I++)G[I]=8;for(;256>I;I++)G[I]=9;for(;280>I;I++)G[I]=7;for(;288>I;I++)G[I]=8;f=7;I=new M(G,288,257,y,w,f);if(0!=I.status){alert("HufBuild error: "+I.status);S=-1;break b}c=I.root;f=I.m;for(I=0;30>I;I++)G[I]=5;$=5;I=new M(G,30,0,t,s,$); -if(1 + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -150,13 +88,21 @@ if(1g))throw runtime.log("alert","watchdog timeout"),"timeout!";if(0l))throw runtime.log("alert","watchdog loop overflow"),"loop overflow";}}; +(function(){function g(){var e,g,m,q,b;void 0===k&&(b=(e=runtime.getWindow())&&e.document,k={rangeBCRIgnoresElementBCR:!1,unscaledRangeClientRects:!1},b&&(q=b.createElement("div"),q.style.position="absolute",q.style.left="-99999px",q.style.transform="scale(2)",q.style["-webkit-transform"]="scale(2)",g=b.createElement("div"),q.appendChild(g),b.body.appendChild(q),e=b.createRange(),e.selectNode(g),k.rangeBCRIgnoresElementBCR=0===e.getClientRects().length,g.appendChild(b.createTextNode("Rect transform test")), +g=g.getBoundingClientRect(),m=e.getBoundingClientRect(),k.unscaledRangeClientRects=2=a.compareBoundaryPoints(Range.START_TO_START,c)&&0<=a.compareBoundaryPoints(Range.END_TO_END,c)}function k(a,c){return 0>=a.compareBoundaryPoints(Range.END_TO_START,c)&&0<= +a.compareBoundaryPoints(Range.START_TO_END,c)}function m(a,c){var d=null;a.nodeType===Node.TEXT_NODE&&(0===a.length?(a.parentNode.removeChild(a),c.nodeType===Node.TEXT_NODE&&(d=c)):(c.nodeType===Node.TEXT_NODE&&(a.appendData(c.data),c.parentNode.removeChild(c)),d=a));return d}function q(a){for(var c=a.parentNode;a.firstChild;)c.insertBefore(a.firstChild,a);c.removeChild(a);return c}function b(a,c){for(var d=a.parentNode,f=a.firstChild,e;f;)e=f.nextSibling,b(f,c),f=e;c(a)&&(d=q(a));return d}function h(a, +c){return a===c||Boolean(a.compareDocumentPosition(c)&Node.DOCUMENT_POSITION_CONTAINED_BY)}function r(a,c){for(var d=0,b;a.parentNode!==c;)runtime.assert(null!==a.parentNode,"parent is null"),a=a.parentNode;for(b=c.firstChild;b!==a;)d+=1,b=b.nextSibling;return d}function d(a,c,b){Object.keys(c).forEach(function(f){var e=f.split(":"),h=e[1],m=b(e[0]),e=c[f];"object"===typeof e&&Object.keys(e).length?(f=m?a.getElementsByTagNameNS(m,h)[0]||a.ownerDocument.createElementNS(m,f):a.getElementsByTagName(h)[0]|| +a.ownerDocument.createElement(f),a.appendChild(f),d(f,e,b)):m&&a.setAttributeNS(m,f,String(e))})}var f=null;this.splitBoundaries=function(a){var c=[],d,b;if(a.startContainer.nodeType===Node.TEXT_NODE||a.endContainer.nodeType===Node.TEXT_NODE){if(d=a.endContainer){d=a.endOffset;b=a.endContainer;if(d + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -191,26 +137,12 @@ core.Utils=function(){function g(l,c){c&&Array.isArray(c)?l=(l||[]).concat(c.map @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -(function(){function g(){var c,g,f,n,b;void 0===l&&(b=(c=runtime.getWindow())&&c.document,l={rangeBCRIgnoresElementBCR:!1,unscaledRangeClientRects:!1},b&&(n=b.createElement("div"),n.style.position="absolute",n.style.left="-99999px",n.style.transform="scale(2)",n.style["-webkit-transform"]="scale(2)",g=b.createElement("div"),n.appendChild(g),b.body.appendChild(n),c=b.createRange(),c.selectNode(g),l.rangeBCRIgnoresElementBCR=0===c.getClientRects().length,g.appendChild(b.createTextNode("Rect transform test")), -g=g.getBoundingClientRect(),f=c.getBoundingClientRect(),l.unscaledRangeClientRects=2=a.compareBoundaryPoints(Range.START_TO_START,e)&&0<=a.compareBoundaryPoints(Range.END_TO_END,e)}function l(a,e){return 0>=a.compareBoundaryPoints(Range.END_TO_START,e)&&0<= -a.compareBoundaryPoints(Range.START_TO_END,e)}function f(a,e){var h=null;a.nodeType===Node.TEXT_NODE&&(0===a.length?(a.parentNode.removeChild(a),e.nodeType===Node.TEXT_NODE&&(h=e)):(e.nodeType===Node.TEXT_NODE&&(a.appendData(e.data),e.parentNode.removeChild(e)),h=a));return h}function n(a){for(var e=a.parentNode;a.firstChild;)e.insertBefore(a.firstChild,a);e.removeChild(a);return e}function b(a,e){for(var h=a.parentNode,d=a.firstChild,c;d;)c=d.nextSibling,b(d,e),d=c;e(a)&&(h=n(a));return h}function p(a, -e){return a===e||Boolean(a.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_CONTAINED_BY)}function r(a,e){for(var h=0,b;a.parentNode!==e;)runtime.assert(null!==a.parentNode,"parent is null"),a=a.parentNode;for(b=e.firstChild;b!==a;)h+=1,b=b.nextSibling;return h}function d(a,e,h){Object.keys(e).forEach(function(b){var c=b.split(":"),k=c[1],f=h(c[0]),c=e[b];"object"===typeof c&&Object.keys(c).length?(b=a.getElementsByTagNameNS(f,k)[0]||a.ownerDocument.createElementNS(f,b),a.appendChild(b),d(b,c,h)): -f&&a.setAttributeNS(f,b,c)})}var k=null;this.splitBoundaries=function(a){var e=[],h,b;if(a.startContainer.nodeType===Node.TEXT_NODE||a.endContainer.nodeType===Node.TEXT_NODE){if(h=a.endContainer){h=a.endOffset;b=a.endContainer;if(h + Copyright (C) 2012 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -245,38 +177,46 @@ this.setSelectedRange=function(a,e){r&&r!==a&&r.detach();r=a;b=!1!==e;(d=a.colla @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -core.EventNotifier=function(g){var l={};this.emit=function(c,g){var f,n;runtime.assert(l.hasOwnProperty(c),'unknown event fired "'+c+'"');n=l[c];for(f=0;fg))throw runtime.log("alert","watchdog timeout"),"timeout!";if(0k))throw runtime.log("alert","watchdog loop overflow"),"loop overflow";}}; +// Input 10 +core.PositionIterator=function(g,k,e,n){function m(){this.acceptNode=function(a){return!a||a.nodeType===c&&0===a.length?u:l}}function q(a){this.acceptNode=function(d){return!d||d.nodeType===c&&0===d.length?u:a.acceptNode(d)}}function b(){var a=d.currentNode,b=a.nodeType;f=b===c?a.length-1:b===p?1:0}function h(){if(null===d.previousSibling()){if(!d.parentNode()||d.currentNode===g)return d.firstChild(),!1;f=0}else b();return!0}var r=this,d,f,a,c=Node.TEXT_NODE,p=Node.ELEMENT_NODE,l=NodeFilter.FILTER_ACCEPT, +u=NodeFilter.FILTER_REJECT;this.nextPosition=function(){var a=d.currentNode,b=a.nodeType;if(a===g)return!1;if(0===f&&b===p)null===d.firstChild()&&(f=1);else if(b===c&&f+1 "+b.length),runtime.assert(0<=e,"Error in setPosition: "+e+" < 0"),e===b.length&&(d.nextSibling()?f=0:d.parentNode()?f=1:runtime.assert(!1,"Error in setUnfilteredPosition: position not valid.")),!0;p=a(b);for(h=b.parentNode;h&&h!==g&&p===l;)p=a(h),p!==l&&(d.currentNode=h),h=h.parentNode;ex&&(e=x);for(A=1<A){this.status=2;this.m=e;return}A-=p[x];if(0>A)this.status=2,this.m=e;else{p[x]+=A;z[1]=k=0;v=p;q=1;for(s=2;0<--x;)k+=v[q++],z[s++]=k;v=a;x=q=0;do k=v[q++],0!==k&&(g[z[k]++]=x);while(++xt+r[1+g];){t+= +r[1+g];g++;U=m-t;U=U>e?e:U;k=n-t;h=1<a+1)for(h-=a+1,s=n;++kl&&t>t-r[g],u[g-1][k].e=F.e,u[g-1][k].b=F.b,u[g-1][k].n=F.n,u[g-1][k].t=F.t)}F.b=n-t;q>=c?F.e=99:v[q]v[q]?16:15,F.n=v[q++]):(F.e=f[v[q]-d],F.n=b[v[q++]- +d]);h=1<>t;k>=1)x^=k;for(x^=k;(x&(1<>=d;c-=d}function m(a,c,d){var f,l,m;if(0===d)return 0;for(m=0;;){k(v);l=w.list[e(v)];for(f=l.e;16f;f++)s[P[f]]=0;v= +7;f=new g(s,19,19,null,null,v);if(0!==f.status)return-1;w=f.root;v=f.m;l=r+q;for(b=p=0;bf)s[b++]=p=f;else if(16===f){k(2);f=3+e(2);n(2);if(b+f>l)return-1;for(;0l)return-1;for(;0C;C++)I[C]=8;for(C=144;256>C;C++)I[C]=9;for(C=256;280>C;C++)I[C]=7;for(C=280;288>C;C++)I[C]=8;f=7;C=new g(I,288,257,x,R,f);if(0!==C.status){alert("HufBuild error: "+C.status);O=-1;break b}r=C.root;f=C.m;for(C=0;30>C;C++)I[C]= +5;A=5;C=new g(I,30,0,F,U,A);if(1";return runtime.parseXML(c)}; -core.UnitTestRunner=function(){function g(c){b+=1;runtime.log("fail",c)}function l(b,d){var c;try{if(b.length!==d.length)return g("array of length "+b.length+" should be "+d.length+" long"),!1;for(c=0;c1/e?"-0":String(e),g(d+" should be "+b+". Was "+c+".")):g(d+" should be "+b+" (of type "+typeof b+"). Was "+e+" (of type "+typeof e+").")}var b=0,p;p=function(b,d){var c=Object.keys(b),a=Object.keys(d);c.sort();a.sort();return l(c,a)&&Object.keys(b).every(function(a){var h=b[a],c=d[a];return f(h,c)?!0:(g(h+" should be "+c+" for key "+a),!1)})};this.areNodesEqual=m;this.shouldBeNull=function(b,d){n(b,d,"null")}; -this.shouldBeNonNull=function(b,d){var c,a;try{a=eval(d)}catch(e){c=e}c?g(d+" should be non-null. Threw exception "+c):null!==a?runtime.log("pass",d+" is non-null."):g(d+" should be non-null. Was "+a)};this.shouldBe=n;this.countFailedTests=function(){return b}}; -core.UnitTester=function(){function g(c,f){return""+c+""}var l=0,c={};this.runTests=function(m,f,n){function b(a){if(0===a.length)c[p]=k,l+=r.countFailedTests(),f();else{e=a[0];var h=Runtime.getFunctionName(e);runtime.log("Running "+h);q=r.countFailedTests();d.setUp();e(function(){d.tearDown();k[h]=q===r.countFailedTests();b(a.slice(1))})}}var p=Runtime.getFunctionName(m)||"",r=new core.UnitTestRunner,d=new m(r),k={},a,e,h,q,u="BrowserRuntime"=== -runtime.type();if(c.hasOwnProperty(p))runtime.log("Test "+p+" has already run.");else{u?runtime.log("Running "+g(p,'runSuite("'+p+'");')+": "+d.description()+""):runtime.log("Running "+p+": "+d.description);h=d.tests();for(a=0;aRunning "+g(m,'runTest("'+p+'","'+m+'")')+""):runtime.log("Running "+m),q=r.countFailedTests(),d.setUp(),e(),d.tearDown(),k[m]=q===r.countFailedTests()); -b(d.asyncTests())}};this.countFailedTests=function(){return l};this.results=function(){return c}}; +core.ScheduledTask=function(g,k){function e(){q&&(runtime.clearTimeout(m),q=!1)}function n(){e();g.apply(void 0,b);b=null}var m,q=!1,b=[];this.trigger=function(){b=Array.prototype.slice.call(arguments);q||(q=!0,m=runtime.setTimeout(n,k))};this.triggerImmediate=function(){b=Array.prototype.slice.call(arguments);n()};this.processRequests=function(){q&&n()};this.cancel=e;this.destroy=function(b){e();b()}}; // Input 13 -core.PositionIterator=function(g,l,c,m){function f(){this.acceptNode=function(a){return!a||a.nodeType===e&&0===a.length?u:q}}function n(a){this.acceptNode=function(b){return!b||b.nodeType===e&&0===b.length?u:a.acceptNode(b)}}function b(){var a=d.currentNode,b=a.nodeType;k=b===e?a.length-1:b===h?1:0}function p(){if(null===d.previousSibling()){if(!d.parentNode()||d.currentNode===g)return d.firstChild(),!1;k=0}else b();return!0}var r=this,d,k,a,e=Node.TEXT_NODE,h=Node.ELEMENT_NODE,q=NodeFilter.FILTER_ACCEPT, -u=NodeFilter.FILTER_REJECT;this.nextPosition=function(){var a=d.currentNode,b=a.nodeType;if(a===g)return!1;if(0===k&&b===h)null===d.firstChild()&&(k=1);else if(b===e&&k+1 "+b.length),runtime.assert(0<=h,"Error in setPosition: "+h+" < 0"),h===b.length&&(d.nextSibling()?k=0:d.parentNode()?k=1:runtime.assert(!1,"Error in setUnfilteredPosition: position not valid.")),!0;c=a(b);for(f=b.parentNode;f&&f!==g&&c===q;)c=a(f),c!==q&&(d.currentNode=f),f=f.parentNode;h";return runtime.parseXML(e)}; +core.UnitTestRunner=function(){function g(e){b+=1;runtime.log("fail",e)}function k(b,d){var f;try{if(b.length!==d.length)return g("array of length "+b.length+" should be "+d.length+" long"),!1;for(f=0;f1/c?"-0":String(c),g(d+" should be "+b+". Was "+f+".")):g(d+" should be "+b+" (of type "+typeof b+"). Was "+c+" (of type "+typeof c+").")}var b=0,h;h=function(b,d){var f=Object.keys(b),a=Object.keys(d);f.sort();a.sort();return k(f,a)&&Object.keys(b).every(function(a){var f=b[a],e=d[a];return m(f,e)?!0:(g(f+" should be "+e+" for key "+a),!1)})};this.areNodesEqual=n;this.shouldBeNull=function(b,d){q(b,d,"null")};this.shouldBeNonNull=function(b,d){var f,a;try{a=eval(d)}catch(c){f= +c}f?g(d+" should be non-null. Threw exception "+f):null!==a?runtime.log("pass",d+" is non-null."):g(d+" should be non-null. Was "+a)};this.shouldBe=q;this.countFailedTests=function(){return b};this.name=function(b){var d,f,a=[],c=b.length;a.length=c;for(d=0;d"+e+""}var k=0,e={};this.runTests=function(n,m,q){function b(a){if(0===a.length)e[h]=f,k+=r.countFailedTests(),m();else{c=a[0].f;var p=a[0].name;runtime.log("Running "+p);l=r.countFailedTests();d.setUp();c(function(){d.tearDown();f[p]=l===r.countFailedTests();b(a.slice(1))})}}var h=Runtime.getFunctionName(n)||"",r=new core.UnitTestRunner,d=new n(r),f={},a,c,p,l,u="BrowserRuntime"===runtime.type(); +if(e.hasOwnProperty(h))runtime.log("Test "+h+" has already run.");else{u?runtime.log("Running "+g(h,'runSuite("'+h+'");')+": "+d.description()+""):runtime.log("Running "+h+": "+d.description);p=d.tests();for(a=0;aRunning "+g(n,'runTest("'+h+'","'+n+'")')+""):runtime.log("Running "+n),l=r.countFailedTests(),d.setUp(),c(),d.tearDown(),f[n]=l===r.countFailedTests());b(d.asyncTests())}};this.countFailedTests= +function(){return k};this.results=function(){return e}}; // Input 14 -runtime.loadClass("core.PositionIterator");core.PositionFilter=function(){};core.PositionFilter.FilterResult={FILTER_ACCEPT:1,FILTER_REJECT:2,FILTER_SKIP:3};core.PositionFilter.prototype.acceptPosition=function(g){};(function(){return core.PositionFilter})(); +core.Utils=function(){function g(k,e){if(e&&Array.isArray(e)){k=k||[];if(!Array.isArray(k))throw"Destination is not an array.";k=k.concat(e.map(function(e){return g(null,e)}))}else if(e&&"object"===typeof e){k=k||{};if("object"!==typeof k)throw"Destination is not an object.";Object.keys(e).forEach(function(n){k[n]=g(k[n],e[n])})}else k=e;return k}this.hashString=function(g){var e=0,n,m;n=0;for(m=g.length;n>>8^c;return e^-1}function m(a){return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&15,a>>5&63,(a&31)<<1)}function f(a){var b=a.getFullYear();return 1980>b?0:b-1980<< -25|a.getMonth()+1<<21|a.getDate()<<16|a.getHours()<<11|a.getMinutes()<<5|a.getSeconds()>>1}function n(a,b){var e,h,d,c,k,f,g,l=this;this.load=function(b){if(void 0!==l.data)b(null,l.data);else{var e=k+34+h+d+256;e+g>q&&(e=q-g);runtime.read(a,g,e,function(e,h){if(e||null===h)b(e,h);else a:{var d=h,g=new core.ByteArray(d),q=g.readUInt32LE(),s;if(67324752!==q)b("File entry signature is wrong."+q.toString()+" "+d.length.toString(),null);else{g.pos+=22;q=g.readUInt16LE();s=g.readUInt16LE();g.pos+=q+s; -if(c){d=d.subarray(g.pos,g.pos+k);if(k!==d.length){b("The amount of compressed bytes read was "+d.length.toString()+" instead of "+k.toString()+" for "+l.filename+" in "+a+".",null);break a}d=x(d,f)}else d=d.subarray(g.pos,g.pos+f);f!==d.length?b("The amount of bytes read was "+d.length.toString()+" instead of "+f.toString()+" for "+l.filename+" in "+a+".",null):(l.data=d,b(null,d))}}})}};this.set=function(a,b,e,d){l.filename=a;l.data=b;l.compressed=e;l.date=d};this.error=null;b&&(e=b.readUInt32LE(), -33639248!==e?this.error="Central directory entry has wrong signature at position "+(b.pos-4).toString()+' for file "'+a+'": '+b.data.length.toString():(b.pos+=6,c=b.readUInt16LE(),this.date=m(b.readUInt32LE()),b.readUInt32LE(),k=b.readUInt32LE(),f=b.readUInt32LE(),h=b.readUInt16LE(),d=b.readUInt16LE(),e=b.readUInt16LE(),b.pos+=8,g=b.readUInt32LE(),this.filename=runtime.byteArrayToString(b.data.subarray(b.pos,b.pos+h),"utf8"),b.pos+=h+d+e))}function b(a,b){if(22!==a.length)b("Central directory length should be 22.", -v);else{var e=new core.ByteArray(a),d;d=e.readUInt32LE();101010256!==d?b("Central directory signature is wrong: "+d.toString(),v):(d=e.readUInt16LE(),0!==d?b("Zip files with non-zero disk numbers are not supported.",v):(d=e.readUInt16LE(),0!==d?b("Zip files with non-zero disk numbers are not supported.",v):(d=e.readUInt16LE(),u=e.readUInt16LE(),d!==u?b("Number of entries is inconsistent.",v):(d=e.readUInt32LE(),e=e.readUInt16LE(),e=q-22-d,runtime.read(g,e,q-e,function(a,e){if(a||null===e)b(a,v);else a:{var d= -new core.ByteArray(e),c,k;h=[];for(c=0;cq?l("File '"+g+"' cannot be read.",v):runtime.read(g,q-22,22,function(a,e){a||null===l||null===e?l(a,v):b(e,l)})})}; -// Input 19 -core.CSSUnits=function(){var g=this,l={"in":1,cm:2.54,mm:25.4,pt:72,pc:12};this.convert=function(c,g,f){return c*l[f]/l[g]};this.convertMeasure=function(c,l){var f,n;c&&l?(f=parseFloat(c),n=c.replace(f.toString(),""),f=g.convert(f,n,l).toString()):f="";return f};this.getUnits=function(c){return c.substr(c.length-2,c.length)}}; -// Input 20 -xmldom.LSSerializerFilter=function(){}; -// Input 21 -"function"!==typeof Object.create&&(Object.create=function(g){var l=function(){};l.prototype=g;return new l}); -xmldom.LSSerializer=function(){function g(c){var g=c||{},b=function(b){var a={},e;for(e in b)b.hasOwnProperty(e)&&(a[b[e]]=e);return a}(c),l=[g],m=[b],d=0;this.push=function(){d+=1;g=l[d]=Object.create(g);b=m[d]=Object.create(b)};this.pop=function(){l[d]=void 0;m[d]=void 0;d-=1;g=l[d];b=m[d]};this.getLocalNamespaceDefinitions=function(){return b};this.getQName=function(d){var a=d.namespaceURI,e=0,h;if(!a)return d.localName;if(h=b[a])return h+":"+d.localName;do{h||!d.prefix?(h="ns"+e,e+=1):h=d.prefix; -if(g[h]===a)break;if(!g[h]){g[h]=a;b[a]=h;break}h=null}while(null===h);return h+":"+d.localName}}function l(c){return c.replace(/&/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")}function c(f,g){var b="",p=m.filter?m.filter.acceptNode(g):NodeFilter.FILTER_ACCEPT,r;if(p===NodeFilter.FILTER_ACCEPT&&g.nodeType===Node.ELEMENT_NODE){f.push();r=f.getQName(g);var d,k=g.attributes,a,e,h,q="",u;d="<"+r;a=k.length;for(e=0;e")}if(p===NodeFilter.FILTER_ACCEPT||p===NodeFilter.FILTER_SKIP){for(p=g.firstChild;p;)b+=c(f,p),p=p.nextSibling;g.nodeValue&&(b+=l(g.nodeValue))}r&&(b+="",f.pop());return b}var m=this;this.filter=null;this.writeToString=function(f,l){if(!f)return"";var b=new g(l);return c(b,f)}}; -// Input 22 -xmldom.RelaxNGParser=function(){function g(b,c){this.message=function(){c&&(b+=1===c.nodeType?" Element ":" Node ",b+=c.nodeName,c.nodeValue&&(b+=" with value '"+c.nodeValue+"'"),b+=".");return b}}function l(b){if(2>=b.e.length)return b;var c={name:b.name,e:b.e.slice(0,2)};return l({name:b.name,e:[c].concat(b.e.slice(2))})}function c(b){b=b.split(":",2);var c="",a;1===b.length?b=["",b[0]]:c=b[0];for(a in p)p[a]===c&&(b[0]=a);return b}function m(b,k){for(var a=0,e,h,f=b.name;b.e&&a=e.length)return b;0===c&&(c=0);for(var h=e.item(c);h.namespaceURI===d;){c+=1;if(c>=e.length)return b;h=e.item(c)}return h=p(a,b.attDeriv(a,e.item(c)),e,c+1)}function r(a,b,e){e.e[0].a?(a.push(e.e[0].text),b.push(e.e[0].a.ns)):r(a,b,e.e[0]);e.e[1].a?(a.push(e.e[1].text),b.push(e.e[1].a.ns)): -r(a,b,e.e[1])}var d="http://www.w3.org/2000/xmlns/",k,a,e,h,q,u,x,v,y,w,t={type:"notAllowed",nullable:!1,hash:"notAllowed",textDeriv:function(){return t},startTagOpenDeriv:function(){return t},attDeriv:function(){return t},startTagCloseDeriv:function(){return t},endTagDeriv:function(){return t}},s={type:"empty",nullable:!0,hash:"empty",textDeriv:function(){return t},startTagOpenDeriv:function(){return t},attDeriv:function(){return t},startTagCloseDeriv:function(){return s},endTagDeriv:function(){return t}}, -D={type:"text",nullable:!0,hash:"text",textDeriv:function(){return D},startTagOpenDeriv:function(){return t},attDeriv:function(){return t},startTagCloseDeriv:function(){return D},endTagDeriv:function(){return t}},A,B,M;k=m("choice",function(a,b){if(a===t)return b;if(b===t||a===b)return a},function(a,b){var e={},h;f(e,{p1:a,p2:b});b=a=void 0;for(h in e)e.hasOwnProperty(h)&&(void 0===a?a=e[h]:b=void 0===b?e[h]:k(b,e[h]));return function(a,b){return{type:"choice",p1:a,p2:b,nullable:a.nullable||b.nullable, -textDeriv:function(e,c){return k(a.textDeriv(e,c),b.textDeriv(e,c))},startTagOpenDeriv:c(function(e){return k(a.startTagOpenDeriv(e),b.startTagOpenDeriv(e))}),attDeriv:function(e,c){return k(a.attDeriv(e,c),b.attDeriv(e,c))},startTagCloseDeriv:g(function(){return k(a.startTagCloseDeriv(),b.startTagCloseDeriv())}),endTagDeriv:g(function(){return k(a.endTagDeriv(),b.endTagDeriv())})}}(a,b)});a=function(a,b,e){return function(){var c={},h=0;return function(d,k){var f=b&&b(d,k),g,l;if(void 0!==f)return f; -f=d.hash||d.toString();g=k.hash||k.toString();fNode.ELEMENT_NODE;){if(d!==Node.COMMENT_NODE&&(d!==Node.TEXT_NODE||!/^\s+$/.test(c.currentNode.nodeValue)))return[new g("Not allowed node of type "+ -d+".")];d=(f=c.nextSibling())?f.nodeType:0}if(!f)return[new g("Missing element "+b.names)];if(b.names&&-1===b.names.indexOf(n[f.namespaceURI]+":"+f.localName))return[new g("Found "+f.nodeName+" instead of "+b.names+".",f)];if(c.firstChild()){for(k=l(b.e[1],c,f);c.nextSibling();)if(d=c.currentNode.nodeType,!(c.currentNode&&c.currentNode.nodeType===Node.TEXT_NODE&&/^\s+$/.test(c.currentNode.nodeValue)||d===Node.COMMENT_NODE))return[new g("Spurious content.",c.currentNode)];if(c.parentNode()!==f)return[new g("Implementation error.")]}else k= -l(b.e[1],c,f);c.nextSibling();return k}var m,f,n;f=function(b,p,m,d){var k=b.name,a=null;if("text"===k)a:{for(var e=(b=p.currentNode)?b.nodeType:0;b!==m&&3!==e;){if(1===e){a=[new g("Element not allowed here.",b)];break a}e=(b=p.nextSibling())?b.nodeType:0}p.nextSibling();a=null}else if("data"===k)a=null;else if("value"===k)d!==b.text&&(a=[new g("Wrong value, should be '"+b.text+"', not '"+d+"'",m)]);else if("list"===k)a=null;else if("attribute"===k)a:{if(2!==b.e.length)throw"Attribute with wrong # of elements: "+ -b.e.length;k=b.localnames.length;for(a=0;a=k&&c.push(l(a.substring(b,d)))):"["===a[d]&&(0>=k&&(b=d+1),k+=1),d+=1;return d};xmldom.XPathIterator.prototype.next=function(){};xmldom.XPathIterator.prototype.reset=function(){};d=function(a,e,c){var d,k,g,l;for(d=0;d - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -gui.AnnotationViewManager=function(g,l,c){function m(a){var b=a.node,c=a.end;a=r.createRange();c&&(a.setStart(b,b.childNodes.length),a.setEnd(c,0),c=d.getTextNodes(a,!1),c.forEach(function(a){var c=r.createElement("span");c.className="annotationHighlight";c.setAttribute("annotation",b.getAttributeNS(odf.Namespaces.officens,"name"));a.parentNode.insertBefore(c,a);c.appendChild(a)}));a.detach()}function f(a){var b=g.getSizer();a?(c.style.display="inline-block",b.style.paddingRight=k.getComputedStyle(c).width): -(c.style.display="none",b.style.paddingRight=0);g.refreshSize()}function n(){p.sort(function(a,b){return a.node.compareDocumentPosition(b.node)===Node.DOCUMENT_POSITION_FOLLOWING?-1:1})}function b(){var a;for(a=0;a=(l.getBoundingClientRect().top-n.bottom)/b?d.style.top=Math.abs(l.getBoundingClientRect().top-n.bottom)/b+20+"px":d.style.top="0px");k.style.left=f.getBoundingClientRect().width/b+"px";var f=k.style,l=k.getBoundingClientRect().left/b,m=k.getBoundingClientRect().top/b,n=d.getBoundingClientRect().left/b,r=d.getBoundingClientRect().top/b,t=0,s=0,t=n-l,t=t*t,s=r-m,s=s*s,l=Math.sqrt(t+s);f.width=l+"px";m=Math.asin((d.getBoundingClientRect().top- -k.getBoundingClientRect().top)/(b*parseFloat(k.style.width)));k.style.transform="rotate("+m+"rad)";k.style.MozTransform="rotate("+m+"rad)";k.style.WebkitTransform="rotate("+m+"rad)";k.style.msTransform="rotate("+m+"rad)"}}var p=[],r=l.ownerDocument,d=new odf.OdfUtils,k=runtime.getWindow();runtime.assert(Boolean(k),"Expected to be run in an environment which has a global window, like a browser.");this.rerenderAnnotations=b;this.addAnnotation=function(a){f(!0);p.push({node:a.node,end:a.end});n();var e= -r.createElement("div"),c=r.createElement("div"),d=r.createElement("div"),k=r.createElement("div"),g=r.createElement("div"),l=a.node;e.className="annotationWrapper";l.parentNode.insertBefore(e,l);c.className="annotationNote";c.appendChild(l);g.className="annotationRemoveButton";c.appendChild(g);d.className="annotationConnector horizontal";k.className="annotationConnector angular";e.appendChild(c);e.appendChild(d);e.appendChild(k);a.end&&m(a);b()};this.forgetAnnotations=function(){for(;p.length;){var a= -p[0],b=p.indexOf(a),c=a.node,d=c.parentNode.parentNode;"div"===d.localName&&(d.parentNode.insertBefore(c,d),d.parentNode.removeChild(d));a=a.node.getAttributeNS(odf.Namespaces.officens,"name");a=r.querySelectorAll('span.annotationHighlight[annotation="'+a+'"]');d=c=void 0;for(c=0;c - - @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. - - You should have received a copy of the GNU Affero General Public License - along with this code. If not, see . - - 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: https://github.com/kogmbh/WebODF/ -*/ -odf.OdfNodeFilter=function(){this.acceptNode=function(g){return"http://www.w3.org/1999/xhtml"===g.namespaceURI?NodeFilter.FILTER_SKIP:g.namespaceURI&&g.namespaceURI.match(/^urn:webodf:/)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}}; -// Input 28 +2932959818,3654703836,1088359270,936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117],b,d,f=a.length,e=0,e=0;b=-1;for(d=0;d>>8^e;return b^-1}function n(a){return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&15,a>>5&63,(a&31)<<1)}function m(a){var c=a.getFullYear();return 1980>c?0:c-1980<< +25|a.getMonth()+1<<21|a.getDate()<<16|a.getHours()<<11|a.getMinutes()<<5|a.getSeconds()>>1}function q(a,c){var b,d,f,e,p,h,m,g=this;this.load=function(c){if(null!==g.data)c(null,g.data);else{var b=p+34+d+f+256;b+m>l&&(b=l-m);runtime.read(a,m,b,function(b,d){if(b||null===d)c(b,d);else a:{var f=d,l=new core.ByteArray(f),m=l.readUInt32LE(),k;if(67324752!==m)c("File entry signature is wrong."+m.toString()+" "+f.length.toString(),null);else{l.pos+=22;m=l.readUInt16LE();k=l.readUInt16LE();l.pos+=m+k;if(e){f= +f.subarray(l.pos,l.pos+p);if(p!==f.length){c("The amount of compressed bytes read was "+f.length.toString()+" instead of "+p.toString()+" for "+g.filename+" in "+a+".",null);break a}f=B(f,h)}else f=f.subarray(l.pos,l.pos+h);h!==f.length?c("The amount of bytes read was "+f.length.toString()+" instead of "+h.toString()+" for "+g.filename+" in "+a+".",null):(g.data=f,c(null,f))}}})}};this.set=function(a,c,b,d){g.filename=a;g.data=c;g.compressed=b;g.date=d};this.error=null;c&&(b=c.readUInt32LE(),33639248!== +b?this.error="Central directory entry has wrong signature at position "+(c.pos-4).toString()+' for file "'+a+'": '+c.data.length.toString():(c.pos+=6,e=c.readUInt16LE(),this.date=n(c.readUInt32LE()),c.readUInt32LE(),p=c.readUInt32LE(),h=c.readUInt32LE(),d=c.readUInt16LE(),f=c.readUInt16LE(),b=c.readUInt16LE(),c.pos+=8,m=c.readUInt32LE(),this.filename=runtime.byteArrayToString(c.data.subarray(c.pos,c.pos+d),"utf8"),this.data=null,c.pos+=d+f+b))}function b(a,c){if(22!==a.length)c("Central directory length should be 22.", +w);else{var b=new core.ByteArray(a),d;d=b.readUInt32LE();101010256!==d?c("Central directory signature is wrong: "+d.toString(),w):(d=b.readUInt16LE(),0!==d?c("Zip files with non-zero disk numbers are not supported.",w):(d=b.readUInt16LE(),0!==d?c("Zip files with non-zero disk numbers are not supported.",w):(d=b.readUInt16LE(),u=b.readUInt16LE(),d!==u?c("Number of entries is inconsistent.",w):(d=b.readUInt32LE(),b=b.readUInt16LE(),b=l-22-d,runtime.read(g,b,l-b,function(a,b){if(a||null===b)c(a,w);else a:{var d= +new core.ByteArray(b),f,e;p=[];for(f=0;fl?k("File '"+g+"' cannot be read.",w):runtime.read(g,l-22,22,function(a,c){a||null===k||null===c?k(a,w): +b(c,k)})})}; +// Input 16 +gui.Avatar=function(g,k){var e=this,n,m,q;this.setColor=function(b){m.style.borderColor=b};this.setImageUrl=function(b){e.isVisible()?m.src=b:q=b};this.isVisible=function(){return"block"===n.style.display};this.show=function(){q&&(m.src=q,q=void 0);n.style.display="block"};this.hide=function(){n.style.display="none"};this.markAsFocussed=function(b){n.className=b?"active":""};this.destroy=function(b){g.removeChild(n);b()};(function(){var b=g.ownerDocument,e=b.documentElement.namespaceURI;n=b.createElementNS(e, +"div");m=b.createElementNS(e,"img");m.width=64;m.height=64;n.appendChild(m);n.style.width="64px";n.style.height="70px";n.style.position="absolute";n.style.top="-80px";n.style.left="-34px";n.style.display=k?"block":"none";g.appendChild(n)})()}; +// Input 17 +gui.EditInfoHandle=function(g){var k=[],e,n=g.ownerDocument,m=n.documentElement.namespaceURI;this.setEdits=function(g){k=g;var b,h,r,d;e.innerHTML="";for(g=0;g @@ -478,12 +287,9 @@ odf.OdfNodeFilter=function(){this.acceptNode=function(g){return"http://www.w3.or @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -odf.Namespaces=function(){function g(c){return l[c]||null}var l={db:"urn:oasis:names:tc:opendocument:xmlns:database:1.0",dc:"http://purl.org/dc/elements/1.1/",meta:"urn:oasis:names:tc:opendocument:xmlns:meta:1.0",dr3d:"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0",draw:"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0",chart:"urn:oasis:names:tc:opendocument:xmlns:chart:1.0",fo:"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",form:"urn:oasis:names:tc:opendocument:xmlns:form:1.0",numberns:"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0", -office:"urn:oasis:names:tc:opendocument:xmlns:office:1.0",presentation:"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0",style:"urn:oasis:names:tc:opendocument:xmlns:style:1.0",svg:"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",table:"urn:oasis:names:tc:opendocument:xmlns:table:1.0",text:"urn:oasis:names:tc:opendocument:xmlns:text:1.0",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace"},c;g.lookupNamespaceURI=g;c=function(){};c.forEachPrefix=function(c){for(var f in l)l.hasOwnProperty(f)&& -c(f,l[f])};c.resolvePrefix=g;c.lookupPrefix=function(c){var f,g;for(g in l)if(l.hasOwnProperty(g)&&l[g]===c){f=g;break}return f};c.namespaceMap=l;c.dbns="urn:oasis:names:tc:opendocument:xmlns:database:1.0";c.dcns="http://purl.org/dc/elements/1.1/";c.metans="urn:oasis:names:tc:opendocument:xmlns:meta:1.0";c.dr3dns="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0";c.drawns="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0";c.chartns="urn:oasis:names:tc:opendocument:xmlns:chart:1.0";c.fons="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"; -c.formns="urn:oasis:names:tc:opendocument:xmlns:form:1.0";c.numberns="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0";c.officens="urn:oasis:names:tc:opendocument:xmlns:office:1.0";c.presentationns="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0";c.stylens="urn:oasis:names:tc:opendocument:xmlns:style:1.0";c.svgns="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0";c.tablens="urn:oasis:names:tc:opendocument:xmlns:table:1.0";c.textns="urn:oasis:names:tc:opendocument:xmlns:text:1.0"; -c.xlinkns="http://www.w3.org/1999/xlink";c.xmlns="http://www.w3.org/XML/1998/namespace";return c}(); -// Input 29 +gui.KeyboardHandler=function(){function g(e,g){g||(g=k.None);return e+":"+g}var k=gui.KeyboardHandler.Modifier,e=null,n={};this.setDefault=function(g){e=g};this.bind=function(e,k,b){e=g(e,k);runtime.assert(!1===n.hasOwnProperty(e),"tried to overwrite the callback handler of key combo: "+e);n[e]=b};this.unbind=function(e,k){var b=g(e,k);delete n[b]};this.reset=function(){e=null;n={}};this.handleEvent=function(m){var q=m.keyCode,b=k.None;m.metaKey&&(b|=k.Meta);m.ctrlKey&&(b|=k.Ctrl);m.altKey&&(b|=k.Alt); +m.shiftKey&&(b|=k.Shift);q=g(q,b);q=n[q];b=!1;q?b=q():null!==e&&(b=e(m));b&&(m.preventDefault?m.preventDefault():m.returnValue=!1)}};gui.KeyboardHandler.Modifier={None:0,Meta:1,Ctrl:2,Alt:4,CtrlAlt:6,Shift:8,MetaShift:9,CtrlShift:10,AltShift:12};gui.KeyboardHandler.KeyCode={Backspace:8,Tab:9,Clear:12,Enter:13,End:35,Home:36,Left:37,Up:38,Right:39,Down:40,Delete:46,A:65,B:66,C:67,D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90};(function(){return gui.KeyboardHandler})(); +// Input 19 /* Copyright (C) 2012-2013 KO GmbH @@ -521,36 +327,12 @@ c.xlinkns="http://www.w3.org/1999/xlink";c.xmlns="http://www.w3.org/XML/1998/nam @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("xmldom.XPath");runtime.loadClass("odf.Namespaces"); -odf.StyleInfo=function(){function g(a,b){for(var c=A[a.localName],e=c&&c[a.namespaceURI],d=e?e.length:0,h,c=0;c @@ -589,19 +371,19 @@ Node.ELEMENT_NODE){var d=b,h=e,f=d.getAttributeNS(q,"name"),k=void 0;f?k=q:(f=d. @source: https://github.com/kogmbh/WebODF/ */ runtime.loadClass("core.DomUtils");runtime.loadClass("odf.Namespaces"); -odf.OdfUtils=function(){function g(a){return"image"===(a&&a.localName)&&a.namespaceURI===s}function l(a){return"frame"===(a&&a.localName)&&a.namespaceURI===s&&"as-char"===a.getAttributeNS(t,"anchor-type")}function c(a){var b=a&&a.localName;return("p"===b||"h"===b)&&a.namespaceURI===t}function m(a){for(;a&&!c(a);)a=a.parentNode;return a}function f(a){return/^[ \t\r\n]+$/.test(a)}function n(a){var b=a&&a.localName;return/^(span|p|h|a|meta)$/.test(b)&&a.namespaceURI===t||"span"===b&&"annotationHighlight"=== -a.className?!0:!1}function b(a){var b=a&&a.localName,c;c=!1;b&&(c=a.namespaceURI,c=c===t?"s"===b||"tab"===b||"line-break"===b:l(a));return c}function p(a){var b=a&&a.localName,c=!1;b&&(a=a.namespaceURI,a===t&&(c="s"===b));return c}function r(a){for(;null!==a.firstChild&&n(a);)a=a.firstChild;return a}function d(a){for(;null!==a.lastChild&&n(a);)a=a.lastChild;return a}function k(a){for(;!c(a)&&null===a.previousSibling;)a=a.parentNode;return c(a)?null:d(a.previousSibling)}function a(a){for(;!c(a)&&null=== -a.nextSibling;)a=a.parentNode;return c(a)?null:r(a.nextSibling)}function e(a){for(var c=!1;a;)if(a.nodeType===Node.TEXT_NODE)if(0===a.length)a=k(a);else return!f(a.data.substr(a.length-1,1));else b(a)?(c=!1===p(a),a=null):a=k(a);return c}function h(c){var e=!1;for(c=c&&r(c);c;){if(c.nodeType===Node.TEXT_NODE&&0a.value||"%"===a.unit)?null:a}function y(a){return(a=x(a))&&"%"!==a.unit?null:a}function w(a){switch(a.namespaceURI){case odf.Namespaces.drawns:case odf.Namespaces.svgns:case odf.Namespaces.dr3dns:return!1;case odf.Namespaces.textns:switch(a.localName){case "note-body":case "ruby-text":return!1}break; -case odf.Namespaces.officens:switch(a.localName){case "annotation":case "binary-data":case "event-listeners":return!1}break;default:switch(a.localName){case "editinfo":return!1}}return!0}var t=odf.Namespaces.textns,s=odf.Namespaces.drawns,D=/^\s*$/,A=new core.DomUtils;this.isImage=g;this.isCharacterFrame=l;this.isTextSpan=function(a){return"span"===(a&&a.localName)&&a.namespaceURI===t};this.isParagraph=c;this.getParagraphElement=m;this.isWithinTrackedChanges=function(a,b){for(;a&&a!==b;){if(a.namespaceURI=== -t&&"tracked-changes"===a.localName)return!0;a=a.parentNode}return!1};this.isListItem=function(a){return"list-item"===(a&&a.localName)&&a.namespaceURI===t};this.isLineBreak=function(a){return"line-break"===(a&&a.localName)&&a.namespaceURI===t};this.isODFWhitespace=f;this.isGroupingElement=n;this.isCharacterElement=b;this.isSpaceElement=p;this.firstChild=r;this.lastChild=d;this.previousNode=k;this.nextNode=a;this.scanLeftForNonSpace=e;this.lookLeftForCharacter=function(a){var c;c=0;a.nodeType===Node.TEXT_NODE&& -0=b.value||"%"===b.unit)?null:b;return b||y(a)};this.parseFoLineHeight=function(a){return v(a)||y(a)};this.getImpactedParagraphs=function(a){var b= -a.commonAncestorContainer,e=[];for(b.nodeType===Node.ELEMENT_NODE&&(e=A.getElementsByTagNameNS(b,t,"p").concat(A.getElementsByTagNameNS(b,t,"h")));b&&!c(b);)b=b.parentNode;b&&e.push(b);return e.filter(function(b){return A.rangeIntersectsNode(a,b)})};this.getTextNodes=function(a,b){var c=a.startContainer.ownerDocument.createRange(),e;e=A.getNodesInRange(a,function(e){c.selectNodeContents(e);if(e.nodeType===Node.TEXT_NODE){if(b&&A.rangesIntersect(a,c)||A.containsRange(a,c))return Boolean(m(e)&&(!f(e.textContent)|| -u(e,0)))?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT}else if(A.rangesIntersect(a,c)&&w(e))return NodeFilter.FILTER_SKIP;return NodeFilter.FILTER_REJECT});c.detach();return e};this.getTextElements=function(a,c,e){var d=a.startContainer.ownerDocument.createRange(),h;h=A.getNodesInRange(a,function(h){d.selectNodeContents(h);if(b(h.parentNode))return NodeFilter.FILTER_REJECT;if(h.nodeType===Node.TEXT_NODE){if(c&&A.rangesIntersect(a,d)||A.containsRange(a,d))if(e||Boolean(m(h)&&(!f(h.textContent)|| -u(h,0))))return NodeFilter.FILTER_ACCEPT}else if(b(h)){if(c&&A.rangesIntersect(a,d)||A.containsRange(a,d))return NodeFilter.FILTER_ACCEPT}else if(w(h)||n(h))return NodeFilter.FILTER_SKIP;return NodeFilter.FILTER_REJECT});d.detach();return h};this.getParagraphElements=function(a){var b=a.startContainer.ownerDocument.createRange(),e;e=A.getNodesInRange(a,function(e){b.selectNodeContents(e);if(c(e)){if(A.rangesIntersect(a,b))return NodeFilter.FILTER_ACCEPT}else if(w(e)||n(e))return NodeFilter.FILTER_SKIP; -return NodeFilter.FILTER_REJECT});b.detach();return e};this.getImageElements=function(a){var b=a.startContainer.ownerDocument.createRange(),c;c=A.getNodesInRange(a,function(c){b.selectNodeContents(c);return g(c)&&A.containsRange(a,b)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP});b.detach();return c}}; -// Input 31 +odf.OdfUtils=function(){function g(a){return"image"===(a&&a.localName)&&a.namespaceURI===s}function k(a){return null!==a&&a.nodeType===Node.ELEMENT_NODE&&"frame"===a.localName&&a.namespaceURI===s&&"as-char"===a.getAttributeNS(t,"anchor-type")}function e(a){var c=a&&a.localName;return("p"===c||"h"===c)&&a.namespaceURI===t}function n(a){for(;a&&!e(a);)a=a.parentNode;return a}function m(a){return/^[ \t\r\n]+$/.test(a)}function q(a){if(null===a||a.nodeType!==Node.ELEMENT_NODE)return!1;var c=a.localName; +return/^(span|p|h|a|meta)$/.test(c)&&a.namespaceURI===t||"span"===c&&"annotationHighlight"===a.className}function b(a){var c=a&&a.localName,b;b=!1;c&&(b=a.namespaceURI,b=b===t?"s"===c||"tab"===c||"line-break"===c:k(a));return b}function h(a){var c=a&&a.localName,b=!1;c&&(a=a.namespaceURI,a===t&&(b="s"===c));return b}function r(a){for(;null!==a.firstChild&&q(a);)a=a.firstChild;return a}function d(a){for(;null!==a.lastChild&&q(a);)a=a.lastChild;return a}function f(a){for(;!e(a)&&null===a.previousSibling;)a= +a.parentNode;return e(a)?null:d(a.previousSibling)}function a(a){for(;!e(a)&&null===a.nextSibling;)a=a.parentNode;return e(a)?null:r(a.nextSibling)}function c(a){for(var c=!1;a;)if(a.nodeType===Node.TEXT_NODE)if(0===a.length)a=f(a);else return!m(a.data.substr(a.length-1,1));else b(a)?(c=!1===h(a),a=null):a=f(a);return c}function p(c){var d=!1,f;for(c=c&&r(c);c;){f=c.nodeType===Node.TEXT_NODE?c.length:0;if(0a.value||"%"===a.unit)?null:a}function y(a){return(a=B(a))&&"%"!==a.unit?null:a}function v(a){switch(a.namespaceURI){case odf.Namespaces.drawns:case odf.Namespaces.svgns:case odf.Namespaces.dr3dns:return!1; +case odf.Namespaces.textns:switch(a.localName){case "note-body":case "ruby-text":return!1}break;case odf.Namespaces.officens:switch(a.localName){case "annotation":case "binary-data":case "event-listeners":return!1}break;default:switch(a.localName){case "editinfo":return!1}}return!0}var t=odf.Namespaces.textns,s=odf.Namespaces.drawns,N=/^\s*$/,z=new core.DomUtils;this.isImage=g;this.isCharacterFrame=k;this.isTextSpan=function(a){return"span"===(a&&a.localName)&&a.namespaceURI===t};this.isParagraph= +e;this.getParagraphElement=n;this.isWithinTrackedChanges=function(a,c){for(;a&&a!==c;){if(a.namespaceURI===t&&"tracked-changes"===a.localName)return!0;a=a.parentNode}return!1};this.isListItem=function(a){return"list-item"===(a&&a.localName)&&a.namespaceURI===t};this.isLineBreak=function(a){return"line-break"===(a&&a.localName)&&a.namespaceURI===t};this.isODFWhitespace=m;this.isGroupingElement=q;this.isCharacterElement=b;this.isSpaceElement=h;this.firstChild=r;this.lastChild=d;this.previousNode=f; +this.nextNode=a;this.scanLeftForNonSpace=c;this.lookLeftForCharacter=function(a){var d,e=d=0;a.nodeType===Node.TEXT_NODE&&(e=a.length);0=c.value||"%"===c.unit)?null:c;return c||y(a)};this.parseFoLineHeight=function(a){return w(a)||y(a)};this.getImpactedParagraphs=function(a){var c,b,d;c=a.commonAncestorContainer;var f=[],p=[];for(c.nodeType===Node.ELEMENT_NODE&&(f=z.getElementsByTagNameNS(c,t,"p").concat(z.getElementsByTagNameNS(c,t,"h")));c&&!e(c);)c=c.parentNode;c&&f.push(c);b=f.length;for(c=0;c @@ -639,10 +421,28 @@ return NodeFilter.FILTER_REJECT});b.detach();return e};this.getImageElements=fun @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.OdfUtils"); -odf.TextSerializer=function(){function g(m){var f="",n=l.filter?l.filter.acceptNode(m):NodeFilter.FILTER_ACCEPT,b=m.nodeType,p;if(n===NodeFilter.FILTER_ACCEPT||n===NodeFilter.FILTER_SKIP)for(p=m.firstChild;p;)f+=g(p),p=p.nextSibling;n===NodeFilter.FILTER_ACCEPT&&(b===Node.ELEMENT_NODE&&c.isParagraph(m)?f+="\n":b===Node.TEXT_NODE&&m.textContent&&(f+=m.textContent));return f}var l=this,c=new odf.OdfUtils;this.filter=null;this.writeToString=function(c){if(!c)return"";c=g(c);"\n"===c[c.length-1]&&(c= -c.substr(0,c.length-1));return c}}; -// Input 32 +ops.Server=function(){};ops.Server.prototype.connect=function(g,k){};ops.Server.prototype.networkStatus=function(){};ops.Server.prototype.login=function(g,k,e,n){};ops.Server.prototype.joinSession=function(g,k,e,n){};ops.Server.prototype.leaveSession=function(g,k,e,n){};ops.Server.prototype.getGenesisUrl=function(g){}; +// Input 22 +xmldom.LSSerializerFilter=function(){};xmldom.LSSerializerFilter.prototype.acceptNode=function(g){}; +// Input 23 +xmldom.XPathIterator=function(){};xmldom.XPathIterator.prototype.next=function(){};xmldom.XPathIterator.prototype.reset=function(){}; +function createXPathSingleton(){function g(b,a,c){return-1!==b&&(b=h&&c.push(k(b.substring(a,d)))):"["===b[d]&&(0>=h&&(a=d+1),h+=1),d+=1;return d};r=function(d,a,c){var e,l,g,k;for(e=0;e @@ -680,12 +480,14 @@ c.substr(0,c.length-1));return c}}; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils");runtime.loadClass("core.LoopWatchDog");runtime.loadClass("odf.Namespaces"); -odf.TextStyleApplicator=function(g,l,c){function m(b){function c(a,b){return"object"===typeof a&&"object"===typeof b?Object.keys(a).every(function(d){return c(a[d],b[d])}):a===b}this.isStyleApplied=function(a){a=l.getAppliedStylesForElement(a);return c(b,a)}}function f(b){var f={};this.applyStyleToContainer=function(a){var e;e=a.getAttributeNS(p,"style-name");var h=a.ownerDocument;e=e||"";if(!f.hasOwnProperty(e)){var m=e,n;n=e?l.createDerivedStyleObject(e,"text",b):b;h=h.createElementNS(r,"style:style"); -l.updateStyle(h,n);h.setAttributeNS(r,"style:name",g.generateStyleName());h.setAttributeNS(r,"style:family","text");h.setAttributeNS("urn:webodf:names:scope","scope","document-content");c.appendChild(h);f[m]=h}e=f[e].getAttributeNS(r,"name");a.setAttributeNS(p,"text:style-name",e)}}function n(c,f){var a=c.ownerDocument,e=c.parentNode,h,g,l=new core.LoopWatchDog(1E4);g=[];"span"!==e.localName||e.namespaceURI!==p?(h=a.createElementNS(p,"text:span"),e.insertBefore(h,c),e=!1):(c.previousSibling&&!b.rangeContainsNode(f, -e.firstChild)?(h=e.cloneNode(!1),e.parentNode.insertBefore(h,e.nextSibling)):h=e,e=!0);g.push(c);for(a=c.nextSibling;a&&b.rangeContainsNode(f,a);)l.check(),g.push(a),a=a.nextSibling;g.forEach(function(a){a.parentNode!==h&&h.appendChild(a)});if(a&&e)for(g=h.cloneNode(!1),h.parentNode.insertBefore(g,h.nextSibling);a;)l.check(),e=a.nextSibling,g.appendChild(a),a=e;return h}var b=new core.DomUtils,p=odf.Namespaces.textns,r=odf.Namespaces.stylens;this.applyStyle=function(b,c,a){var e={},h,g,l,p;runtime.assert(a&& -a["style:text-properties"],"applyStyle without any text properties");e["style:text-properties"]=a["style:text-properties"];l=new f(e);p=new m(e);b.forEach(function(a){h=p.isStyleApplied(a);!1===h&&(g=n(a,c),l.applyStyleToContainer(g))})}}; -// Input 33 +gui.AnnotatableCanvas=function(){};gui.AnnotatableCanvas.prototype.refreshSize=function(){};gui.AnnotatableCanvas.prototype.getZoomLevel=function(){};gui.AnnotatableCanvas.prototype.getSizer=function(){}; +gui.AnnotationViewManager=function(g,k,e){function n(a){var c=a.node,b=a.end;a=r.createRange();b&&(a.setStart(c,c.childNodes.length),a.setEnd(b,0),b=d.getTextNodes(a,!1),b.forEach(function(a){var b=r.createElement("span"),d=c.getAttributeNS(odf.Namespaces.officens,"name");b.className="annotationHighlight";b.setAttribute("annotation",d);a.parentNode.insertBefore(b,a);b.appendChild(a)}));a.detach()}function m(a){var c=g.getSizer();a?(e.style.display="inline-block",c.style.paddingRight=f.getComputedStyle(e).width): +(e.style.display="none",c.style.paddingRight=0);g.refreshSize()}function q(){h.sort(function(a,c){return a.node.compareDocumentPosition(c.node)===Node.DOCUMENT_POSITION_FOLLOWING?-1:1})}function b(){var a;for(a=0;a=(m.getBoundingClientRect().top-n.bottom)/c?b.style.top=Math.abs(m.getBoundingClientRect().top-n.bottom)/c+20+"px":b.style.top="0px");f.style.left=d.getBoundingClientRect().width/c+"px";var d=f.style,m=f.getBoundingClientRect().left/c,k=f.getBoundingClientRect().top/c,n=b.getBoundingClientRect().left/c,q=b.getBoundingClientRect().top/c,r=0,s=0,r=n-m,r=r*r,s=q-k,s=s*s,m=Math.sqrt(r+s);d.width= +m+"px";k=Math.asin((b.getBoundingClientRect().top-f.getBoundingClientRect().top)/(c*parseFloat(f.style.width)));f.style.transform="rotate("+k+"rad)";f.style.MozTransform="rotate("+k+"rad)";f.style.WebkitTransform="rotate("+k+"rad)";f.style.msTransform="rotate("+k+"rad)"}}var h=[],r=k.ownerDocument,d=new odf.OdfUtils,f=runtime.getWindow();runtime.assert(Boolean(f),"Expected to be run in an environment which has a global window, like a browser.");this.rerenderAnnotations=b;this.addAnnotation=function(a){m(!0); +h.push({node:a.node,end:a.end});q();var c=r.createElement("div"),d=r.createElement("div"),f=r.createElement("div"),e=r.createElement("div"),g=r.createElement("div"),k=a.node;c.className="annotationWrapper";k.parentNode.insertBefore(c,k);d.className="annotationNote";d.appendChild(k);g.className="annotationRemoveButton";d.appendChild(g);f.className="annotationConnector horizontal";e.className="annotationConnector angular";c.appendChild(d);c.appendChild(f);c.appendChild(e);a.end&&n(a);b()};this.forgetAnnotations= +function(){for(;h.length;){var a=h[0],c=h.indexOf(a),b=a.node,d=b.parentNode.parentNode;"div"===d.localName&&(d.parentNode.insertBefore(b,d),d.parentNode.removeChild(d));a=a.node.getAttributeNS(odf.Namespaces.officens,"name");a=r.querySelectorAll('span.annotationHighlight[annotation="'+a+'"]');d=b=void 0;for(b=0;b @@ -723,31 +525,19 @@ a["style:text-properties"],"applyStyle without any text properties");e["style:te @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.OdfUtils");runtime.loadClass("xmldom.XPath");runtime.loadClass("core.CSSUnits"); -odf.Style2CSS=function(){function g(a){var b={},c,e;if(!a)return b;for(a=a.firstChild;a;){if(e=a.namespaceURI!==q||"style"!==a.localName&&"default-style"!==a.localName?a.namespaceURI===v&&"list-style"===a.localName?"list":a.namespaceURI!==q||"page-layout"!==a.localName&&"default-page-layout"!==a.localName?void 0:"page":a.getAttributeNS(q,"family"))(c=a.getAttributeNS&&a.getAttributeNS(q,"name"))||(c=""),e=b[e]=b[e]||{},e[c]=a;a=a.nextSibling}return b}function l(a,b){if(!b||!a)return null;if(a[b])return a[b]; -var c,e;for(c in a)if(a.hasOwnProperty(c)&&(e=l(a[c].derivedStyles,b)))return e;return null}function c(a,b,e){var d=b[a],h,f;d&&(h=d.getAttributeNS(q,"parent-style-name"),f=null,h&&(f=l(e,h),!f&&b[h]&&(c(h,b,e),f=b[h],b[h]=null)),f?(f.derivedStyles||(f.derivedStyles={}),f.derivedStyles[a]=d):e[a]=d)}function m(a,b){for(var e in a)a.hasOwnProperty(e)&&(c(e,a,b),a[e]=null)}function f(a,b){var c=t[a],e;if(null===c)return null;e=b?"["+c+'|style-name="'+b+'"]':"";"presentation"===c&&(c="draw",e=b?'[presentation|style-name="'+ -b+'"]':"");return c+"|"+s[a].join(e+","+c+"|")+e}function n(a,b,c){var e=[],d,h;e.push(f(a,b));for(d in c.derivedStyles)if(c.derivedStyles.hasOwnProperty(d))for(h in b=n(a,d,c.derivedStyles[d]),b)b.hasOwnProperty(h)&&e.push(b[h]);return e}function b(a,b,c){if(!a)return null;for(a=a.firstChild;a;){if(a.namespaceURI===b&&a.localName===c)return b=a;a=a.nextSibling}return null}function p(a,b){var c="",e,d;for(e in b)if(b.hasOwnProperty(e)&&(e=b[e],d=a.getAttributeNS(e[0],e[1]))){d=d.trim();if(R.hasOwnProperty(e[1])){var h= -d.indexOf(" "),f=void 0,k=void 0;-1!==h?(f=d.substring(0,h),k=d.substring(h)):(f=d,k="");(f=ba.parseLength(f))&&"pt"===f.unit&&0.75>f.value&&(d="0.75pt"+k)}e[2]&&(c+=e[2]+":"+d+";")}return c}function r(a){return(a=b(a,q,"text-properties"))?ba.parseFoFontSize(a.getAttributeNS(h,"font-size")):null}function d(a){a=a.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,function(a,b,c,e){return b+b+c+c+e+e});return(a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(a))?{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3], -16)}:null}function k(a,b,c,e){b='text|list[text|style-name="'+b+'"]';var d=c.getAttributeNS(v,"level"),h;c=ba.getFirstNonWhitespaceChild(c);c=ba.getFirstNonWhitespaceChild(c);var f;c&&(h=c.attributes,f=h["fo:text-indent"]?h["fo:text-indent"].value:void 0,h=h["fo:margin-left"]?h["fo:margin-left"].value:void 0);f||(f="-0.6cm");c="-"===f.charAt(0)?f.substring(1):"-"+f;for(d=d&&parseInt(d,10);1 text|list-item > text|list",d-=1;d=b+" > text|list-item > *:not(text|list):first-child";void 0!== -h&&(h=d+"{margin-left:"+h+";}",a.insertRule(h,a.cssRules.length));e=b+" > text|list-item > *:not(text|list):first-child:before{"+e+";";e+="counter-increment:list;";e+="margin-left:"+f+";";e+="width:"+c+";";e+="display:inline-block}";try{a.insertRule(e,a.cssRules.length)}catch(k){throw k;}}function a(c,f,g,l){if("list"===f)for(var s=l.firstChild,m,t;s;){if(s.namespaceURI===v)if(m=s,"list-level-style-number"===s.localName){var u=m;t=u.getAttributeNS(q,"num-format");var P=u.getAttributeNS(q,"num-suffix"), -L={1:"decimal",a:"lower-latin",A:"upper-latin",i:"lower-roman",I:"upper-roman"},u=u.getAttributeNS(q,"num-prefix")||"",u=L.hasOwnProperty(t)?u+(" counter(list, "+L[t]+")"):t?u+("'"+t+"';"):u+" ''";P&&(u+=" '"+P+"'");t="content: "+u+";";k(c,g,m,t)}else"list-level-style-image"===s.localName?(t="content: none;",k(c,g,m,t)):"list-level-style-bullet"===s.localName&&(t="content: '"+m.getAttributeNS(v,"bullet-char")+"';",k(c,g,m,t));s=s.nextSibling}else if("page"===f)if(P=m=g="",s=l.getElementsByTagNameNS(q, -"page-layout-properties")[0],m=s.parentNode.parentNode.parentNode.masterStyles,P="",g+=p(s,$),t=s.getElementsByTagNameNS(q,"background-image"),0=c?d=m(a,c-1,b,!0):a.nodeType===Node.TEXT_NODE&& +0a?-1:1;for(a=Math.abs(a);0p?g.previousPosition():g.nextPosition());)if(X.check(),h.acceptPosition(g)===t&&(q+=1,n=g.container(),w=m(n,g.unfilteredDomOffset(),H),w.top!==I)){if(w.top!==Y&&Y!==I)break;Y=w.top;w=Math.abs(C-w.left);if(null===r||wa?(b=h.previousPosition,d=-1):(b=h.nextPosition,d=1);for(f=m(h.container(),h.unfilteredDomOffset(),n);b.call(h);)if(c.acceptPosition(h)===t){if(u.getParagraphElement(h.getCurrentNode())!==p)break;l=m(h.container(),h.unfilteredDomOffset(),n);if(l.bottom!==f.bottom&&(f=l.top>=f.top&&l.bottomf.bottom,!f))break;g+= +d;f=l}n.detach();return g}function l(a,c,b){runtime.assert(null!==a,"SelectionMover.countStepsToPosition called with element===null");var d=e(),f=d.container(),l=d.unfilteredDomOffset(),h=0,p=new core.LoopWatchDog(1E4);for(d.setUnfilteredPosition(a,c);b.acceptPosition(d)!==t&&d.previousPosition();)p.check();a=d.container();runtime.assert(Boolean(a),"SelectionMover.countStepsToPosition: positionIterator.container() returned null");c=d.unfilteredDomOffset();for(d.setUnfilteredPosition(f,l);b.acceptPosition(d)!== +t&&d.previousPosition();)p.check();f=B.comparePoints(a,c,d.container(),d.unfilteredDomOffset());if(0>f)for(;d.nextPosition()&&(p.check(),b.acceptPosition(d)===t&&(h+=1),d.container()!==a||d.unfilteredDomOffset()!==c););else if(0 @@ -773,11 +563,11 @@ m;if(m=b(l,q,"drawing-page-properties"))t=""+p(m,M),"true"===m.getAttributeNS(w, @source: https://github.com/kogmbh/WebODF/ */ runtime.loadClass("odf.Namespaces");runtime.loadClass("core.DomUtils"); -odf.MetadataManager=function(g){function l(f,l){f&&(Object.keys(f).forEach(function(b){m[b]=f[b]}),c.mapKeyValObjOntoNode(g,f,odf.Namespaces.resolvePrefix));l&&(l.forEach(function(b){delete m[b]}),c.removeKeyElementsFromNode(g,l,odf.Namespaces.resolvePrefix))}var c=new core.DomUtils,m={};this.setMetadata=l;this.incrementEditingCycles=function(){var c=parseInt(m["meta:editing-cycles"]||0,10)+1;l({"meta:editing-cycles":c},null)};m=c.getKeyValRepresentationOfNode(g,odf.Namespaces.lookupPrefix)}; -// Input 35 +odf.MetadataManager=function(g){function k(m,k){m&&(Object.keys(m).forEach(function(b){n[b]=m[b]}),e.mapKeyValObjOntoNode(g,m,odf.Namespaces.lookupNamespaceURI));k&&(k.forEach(function(b){delete n[b]}),e.removeKeyElementsFromNode(g,k,odf.Namespaces.lookupNamespaceURI))}var e=new core.DomUtils,n={};this.setMetadata=k;this.incrementEditingCycles=function(){var e=parseInt(n["meta:editing-cycles"]||0,10)+1;k({"meta:editing-cycles":e},null)};n=e.getKeyValRepresentationOfNode(g,odf.Namespaces.lookupPrefix)}; +// Input 30 /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -812,32 +602,8 @@ odf.MetadataManager=function(g){function l(f,l){f&&(Object.keys(f).forEach(funct @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.Base64");runtime.loadClass("core.Zip");runtime.loadClass("xmldom.LSSerializer");runtime.loadClass("odf.StyleInfo");runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.OdfNodeFilter");runtime.loadClass("odf.MetadataManager"); -odf.OdfContainer=function(){function g(a,b,c){for(a=a?a.firstChild:null;a;){if(a.localName===c&&a.namespaceURI===b)return a;a=a.nextSibling}return null}function l(a){var b,c=k.length;for(b=0;bc)break;d=d.nextSibling}a.insertBefore(b,d)}}}function n(a){this.OdfContainer= -a}function b(a,b,c,e){var d=this;this.size=0;this.type=null;this.name=a;this.container=c;this.onchange=this.onreadystatechange=this.document=this.mimetype=this.url=null;this.EMPTY=0;this.LOADING=1;this.DONE=2;this.state=this.EMPTY;this.load=function(){null!==e&&(this.mimetype=b,e.loadAsDataURL(a,b,function(a,b){a&&runtime.log(a);d.url=b;if(d.onchange)d.onchange(d);if(d.onstatereadychange)d.onstatereadychange(d)}))}}var p=new odf.StyleInfo,r,d=odf.Namespaces.stylens,k="meta settings scripts font-face-decls styles automatic-styles master-styles body".split(" "), -a=(new Date).getTime()+"_webodf_",e=new core.Base64;n.prototype=new function(){};n.prototype.constructor=n;n.namespaceURI="urn:oasis:names:tc:opendocument:xmlns:office:1.0";n.localName="document";b.prototype.load=function(){};b.prototype.getUrl=function(){return this.data?"data:;base64,"+e.toBase64(this.data):null};odf.OdfContainer=function q(k,l){function v(a){for(var b=a.firstChild,c;b;)c=b.nextSibling,b.nodeType===Node.ELEMENT_NODE?v(b):b.nodeType===Node.PROCESSING_INSTRUCTION_NODE&&a.removeChild(b), -b=c}function y(a,b){for(var c=a&&a.firstChild;c;)c.nodeType===Node.ELEMENT_NODE&&c.setAttributeNS("urn:webodf:names:scope","scope",b),c=c.nextSibling}function w(a,b){function c(a,b,e){var d=0,f;for(f=a=a.replace(/\d+$/,"");b.hasOwnProperty(f)||e.hasOwnProperty(f);)d+=1,f=a+d;return f}function e(a){var b={};for(a=a.firstChild;a;)a.nodeType===Node.ELEMENT_NODE&&a.namespaceURI===d&&"font-face"===a.localName&&(g=a.getAttributeNS(d,"name"),b[g]=a),a=a.nextSibling;return b}var f,k,g,l,m,s,p={};m=e(a);s= -e(b);for(f=b.firstChild;f;)k=f.nextSibling,f.nodeType===Node.ELEMENT_NODE&&f.namespaceURI===d&&"font-face"===f.localName&&(g=f.getAttributeNS(d,"name"),m.hasOwnProperty(g)?f.isEqualNode(m[g])||(l=c(g,m,s),f.setAttributeNS(d,"style:name",l),a.appendChild(f),m[l]=f,delete s[g],p[g]=l):(a.appendChild(f),m[g]=f,delete s[g])),f=k;return p}function t(a,b){var c=null,e,d,f;if(a)for(c=a.cloneNode(!0),e=c.firstChild;e;)d=e.nextSibling,e.nodeType===Node.ELEMENT_NODE&&(f=e.getAttributeNS("urn:webodf:names:scope", -"scope"))&&f!==b&&c.removeChild(e),e=d;return c}function s(a,b){var c=null,e,f,k,g={};if(a)for(b.forEach(function(a){p.collectUsedFontFaces(g,a)}),c=a.cloneNode(!0),e=c.firstChild;e;)f=e.nextSibling,e.nodeType===Node.ELEMENT_NODE&&(k=e.getAttributeNS(d,"name"),g[k]||c.removeChild(e)),e=f;return c}function D(a){var b=N.rootElement.ownerDocument,c;if(a){v(a.documentElement);try{c=b.importNode(a.documentElement,!0)}catch(e){}}return c}function A(a){N.state=a;if(N.onchange)N.onchange(N);if(N.onstatereadychange)N.onstatereadychange(N)} -function B(a){ka=null;N.rootElement=a;a.fontFaceDecls=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","font-face-decls");a.styles=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles");a.automaticStyles=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles");a.masterStyles=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles");a.body=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","body");a.meta=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0", -"meta")}function M(b){b=D(b);var c=N.rootElement;b&&"document-styles"===b.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===b.namespaceURI?(c.fontFaceDecls=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","font-face-decls"),f(c,c.fontFaceDecls),c.styles=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles"),f(c,c.styles),c.automaticStyles=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles"),y(c.automaticStyles,"document-styles"),f(c,c.automaticStyles), -c.masterStyles=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles"),f(c,c.masterStyles),p.prefixStyleNames(c.automaticStyles,a,c.masterStyles)):A(q.INVALID)}function z(a){a=D(a);var b,c,e;if(a&&"document-content"===a.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===a.namespaceURI){b=N.rootElement;c=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","font-face-decls");b.fontFaceDecls&&c?e=w(b.fontFaceDecls,c):c&&(b.fontFaceDecls=c,f(b,c));c=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0", -"automatic-styles");y(c,"document-content");e&&p.changeFontFaceNames(c,e);if(b.automaticStyles&&c)for(e=c.firstChild;e;)b.automaticStyles.appendChild(e),e=c.firstChild;else c&&(b.automaticStyles=c,f(b,c));b.body=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","body");f(b,b.body)}else A(q.INVALID)}function K(a){a=D(a);var b;a&&"document-meta"===a.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===a.namespaceURI&&(b=N.rootElement,b.meta=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0", -"meta"),f(b,b.meta),r=new odf.MetadataManager(b.meta))}function F(a){a=D(a);var b;a&&"document-settings"===a.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===a.namespaceURI&&(b=N.rootElement,b.settings=g(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","settings"),f(b,b.settings))}function Q(a){a=D(a);var b;if(a&&"manifest"===a.localName&&"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"===a.namespaceURI)for(b=N.rootElement,b.manifest=a,a=b.manifest.firstChild;a;)a.nodeType=== -Node.ELEMENT_NODE&&"file-entry"===a.localName&&"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"===a.namespaceURI&&(Z[a.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","full-path")]=a.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","media-type")),a=a.nextSibling}function $(a){var b=a.shift(),c,e;b?(c=b[0],e=b[1],O.loadAsDOM(c,function(b,c){e(c);b||N.state===q.INVALID||$(a)})):A(q.DONE)}function ia(a){var b="";odf.Namespaces.forEachPrefix(function(a,c){b+= -" xmlns:"+a+'="'+c+'"'});return''}function R(){var a=new xmldom.LSSerializer,b=ia("document-meta");a.filter=new odf.OdfNodeFilter;b+=a.writeToString(N.rootElement.meta,odf.Namespaces.namespaceMap);return b+""}function ma(a,b){var c=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest:file-entry");c.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0", -"manifest:full-path",a);c.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest:media-type",b);return c}function ba(){var a=runtime.parseXML(''),b=g(a,"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest"),c=new xmldom.LSSerializer,e;for(e in Z)Z.hasOwnProperty(e)&&b.appendChild(ma(e,Z[e]));c.filter=new odf.OdfNodeFilter;return'\n'+ -c.writeToString(a,odf.Namespaces.namespaceMap)}function ga(){var a=new xmldom.LSSerializer,b=ia("document-settings");a.filter=new odf.OdfNodeFilter;b+=a.writeToString(N.rootElement.settings,odf.Namespaces.namespaceMap);return b+""}function S(){var b=odf.Namespaces.namespaceMap,e=new xmldom.LSSerializer,d,f,k,g=ia("document-styles");f=t(N.rootElement.automaticStyles,"document-styles");k=N.rootElement.masterStyles&&N.rootElement.masterStyles.cloneNode(!0);d=s(N.rootElement.fontFaceDecls, -[k,N.rootElement.styles,f]);p.removePrefixFromStyleNames(f,a,k);e.filter=new c(k,f);g+=e.writeToString(d,b);g+=e.writeToString(N.rootElement.styles,b);g+=e.writeToString(f,b);g+=e.writeToString(k,b);return g+""}function Y(){var a=odf.Namespaces.namespaceMap,b=new xmldom.LSSerializer,c,e,d=ia("document-content");e=t(N.rootElement.automaticStyles,"document-content");c=s(N.rootElement.fontFaceDecls,[e]);b.filter=new m(N.rootElement.body,e);d+=b.writeToString(c,a);d+=b.writeToString(e, -a);d+=b.writeToString(N.rootElement.body,a);return d+""}function V(a,b){runtime.loadXML(a,function(a,c){if(a)b(a);else{var e=D(c);e&&"document"===e.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===e.namespaceURI?(B(e),A(q.DONE)):A(q.INVALID)}})}function G(){function a(b,c){var d;c||(c=b);d=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0",c);e[b]=d;e.appendChild(d)}var b=new core.Zip("",null),c=runtime.byteArrayFromString("application/vnd.oasis.opendocument.text", -"utf8"),e=N.rootElement,d=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","text");b.save("mimetype",c,!1,new Date);a("meta");a("settings");a("scripts");a("fontFaceDecls","font-face-decls");a("styles");a("automaticStyles","automatic-styles");a("masterStyles","master-styles");a("body");e.body.appendChild(d);r=new odf.MetadataManager(e.meta);A(q.DONE);return b}function I(){var a,b=new Date,c=runtime.getWindow();a="WebODF/"+("undefined"!==String(typeof webodf_version)?webodf_version: -"FromSource");c&&(a=a+" "+c.navigator.userAgent);r.setMetadata({"meta:generator":a});a=runtime.byteArrayFromString(ga(),"utf8");O.save("settings.xml",a,!0,b);a=runtime.byteArrayFromString(R(),"utf8");O.save("meta.xml",a,!0,b);a=runtime.byteArrayFromString(S(),"utf8");O.save("styles.xml",a,!0,b);a=runtime.byteArrayFromString(Y(),"utf8");O.save("content.xml",a,!0,b);a=runtime.byteArrayFromString(ba(),"utf8");O.save("META-INF/manifest.xml",a,!0,b)}function ha(a,b){I();O.writeAs(a,function(a){b(a)})} -var N=this,O,Z={},ka;this.onstatereadychange=l;this.rootElement=this.state=this.onchange=null;this.setRootElement=B;this.getContentElement=function(){var a;ka||(a=N.rootElement.body,ka=a.getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","text")[0]||a.getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","presentation")[0]||a.getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","spreadsheet")[0]);return ka};this.getDocumentType=function(){var a= -N.getContentElement();return a&&a.localName};this.getMetadataManager=function(){return r};this.getPart=function(a){return new b(a,Z[a],N,O)};this.getPartData=function(a,b){O.load(a,b)};this.createByteArray=function(a,b){I();O.createByteArray(a,b)};this.saveAs=ha;this.save=function(a){ha(k,a)};this.getUrl=function(){return k};this.setBlob=function(a,b,c){c=e.convertBase64ToByteArray(c);O.save(a,c,!1,new Date);Z.hasOwnProperty(a)&&runtime.log(a+" has been overwritten.");Z[a]=b};this.removeBlob=function(a){var b= -O.remove(a);runtime.assert(b,"file is not found: "+a);delete Z[a]};this.state=q.LOADING;this.rootElement=function(a){var b=document.createElementNS(a.namespaceURI,a.localName),c;a=new a;for(c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b}(n);O=k?new core.Zip(k,function(a,b){O=b;a?V(k,function(b){a&&(O.error=a+"\n"+b,A(q.INVALID))}):$([["styles.xml",M],["content.xml",z],["meta.xml",K],["settings.xml",F],["META-INF/manifest.xml",Q]])}):G()};odf.OdfContainer.EMPTY=0;odf.OdfContainer.LOADING=1;odf.OdfContainer.DONE= -2;odf.OdfContainer.INVALID=3;odf.OdfContainer.SAVING=4;odf.OdfContainer.MODIFIED=5;odf.OdfContainer.getContainer=function(a){return new odf.OdfContainer(a,null)};return odf.OdfContainer}(); -// Input 36 +odf.OdfNodeFilter=function(){this.acceptNode=function(g){return"http://www.w3.org/1999/xhtml"===g.namespaceURI?NodeFilter.FILTER_SKIP:g.namespaceURI&&g.namespaceURI.match(/^urn:webodf:/)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}}; +// Input 31 /* Copyright (C) 2012-2013 KO GmbH @@ -875,11 +641,30 @@ O.remove(a);runtime.assert(b,"file is not found: "+a);delete Z[a]};this.state=q. @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.Base64");runtime.loadClass("xmldom.XPath");runtime.loadClass("odf.OdfContainer"); -odf.FontLoader=function(){function g(l,f,n,b,p){var r,d=0,k;for(k in l)if(l.hasOwnProperty(k)){if(d===n){r=k;break}d+=1}r?f.getPartData(l[r].href,function(a,e){if(a)runtime.log(a);else if(e){var d="@font-face { font-family: '"+(l[r].family||r)+"'; src: url(data:application/x-font-ttf;charset=binary;base64,"+c.convertUTF8ArrayToBase64(e)+') format("truetype"); }';try{b.insertRule(d,b.cssRules.length)}catch(k){runtime.log("Problem inserting rule in CSS: "+runtime.toJson(k)+"\nRule: "+d)}}else runtime.log("missing font data for "+ -l[r].href);g(l,f,n+1,b,p)}):p&&p()}var l=new xmldom.XPath,c=new core.Base64;odf.FontLoader=function(){this.loadFonts=function(c,f){for(var n=c.rootElement.fontFaceDecls;f.cssRules.length;)f.deleteRule(f.cssRules.length-1);if(n){var b={},p,r,d,k;if(n)for(n=l.getODFElementsWithXPath(n,"style:font-face[svg:font-face-src]",odf.Namespaces.resolvePrefix),p=0;ph.value&&(e="0.75pt"+p)}f[2]&&(b+=f[2]+":"+e+";")}return b}function h(a){return(a=q(a,l,"text-properties"))?O.parseFoFontSize(a.getAttributeNS(c,"font-size")):null}function r(a,c,b,d){return c+c+b+b+d+d}function d(a,b,d,f){b='text|list[text|style-name="'+b+'"]';var e=d.getAttributeNS(w,"level");d=q(d,l,"list-level-properties");d=q(d,l,"list-level-label-alignment");var h,p;d&&(h=d.getAttributeNS(c,"text-indent"),p=d.getAttributeNS(c, +"margin-left"));h||(h="-0.6cm");d="-"===h.charAt(0)?h.substring(1):"-"+h;for(e=e&&parseInt(e,10);1 text|list-item > text|list",e-=1;if(p){e=b+" > text|list-item > *:not(text|list):first-child";e+="{";e=e+("margin-left:"+p+";")+"}";try{a.insertRule(e,a.cssRules.length)}catch(g){runtime.log("cannot load rule: "+e)}}f=b+" > text|list-item > *:not(text|list):first-child:before{"+f+";";f=f+"counter-increment:list;"+("margin-left:"+h+";");f+="width:"+d+";";f+="display:inline-block}";try{a.insertRule(f, +a.cssRules.length)}catch(m){runtime.log("cannot load rule: "+f)}}function f(e,g,k,n){if("list"===g)for(var u=n.element.firstChild,s,t;u;){if(u.namespaceURI===w)if(s=u,"list-level-style-number"===u.localName){var G=s;t=G.getAttributeNS(l,"num-format");var T=G.getAttributeNS(l,"num-suffix")||"",G=G.getAttributeNS(l,"num-prefix")||"",$={1:"decimal",a:"lower-latin",A:"upper-latin",i:"lower-roman",I:"upper-roman"},W="";G&&(W+=' "'+G+'"');W=$.hasOwnProperty(t)?W+(" counter(list, "+$[t]+")"):t?W+(' "'+t+ +'"'):W+" ''";t="content:"+W+' "'+T+'"';d(e,k,s,t)}else"list-level-style-image"===u.localName?(t="content: none;",d(e,k,s,t)):"list-level-style-bullet"===u.localName&&(t="content: '"+s.getAttributeNS(w,"bullet-char")+"';",d(e,k,s,t));u=u.nextSibling}else if("page"===g){if(t=n.element,G=T=k="",u=q(t,l,"page-layout-properties"))if(s=t.getAttributeNS(l,"name"),k+=b(u,ja),(T=q(u,l,"background-image"))&&(G=T.getAttributeNS(y,"href"))&&(k=k+("background-image: url('odfkit:"+G+"');")+b(T,z)),"presentation"=== +ba)for(t=(t=q(t.parentNode.parentElement,p,"master-styles"))&&t.firstElementChild;t;){if(t.namespaceURI===l&&"master-page"===t.localName&&t.getAttributeNS(l,"page-layout-name")===s){G=t.getAttributeNS(l,"name");T="draw|page[draw|master-page-name="+G+"] {"+k+"}";G="office|body, draw|page[draw|master-page-name="+G+"] {"+b(u,ka)+" }";try{e.insertRule(T,e.cssRules.length),e.insertRule(G,e.cssRules.length)}catch(da){throw da;}}t=t.nextElementSibling}else if("text"===ba){T="office|text {"+k+"}";G="office|body {width: "+ +u.getAttributeNS(c,"page-width")+";}";try{e.insertRule(T,e.cssRules.length),e.insertRule(G,e.cssRules.length)}catch(S){throw S;}}}else{k=m(g,k,n).join(",");u="";if(s=q(n.element,l,"text-properties")){G=s;t=W="";T=1;s=""+b(G,N);$=G.getAttributeNS(l,"text-underline-style");"solid"===$&&(W+=" underline");$=G.getAttributeNS(l,"text-line-through-style");"solid"===$&&(W+=" line-through");W.length&&(s+="text-decoration:"+W+";");if(W=G.getAttributeNS(l,"font-name")||G.getAttributeNS(c,"font-family"))$=Z[W], +s+="font-family: "+($||W)+";";$=G.parentElement;if(G=h($)){for(;$;){if(G=h($)){if("%"!==G.unit){t="font-size: "+G.value*T+G.unit+";";break}T*=G.value/100}G=$;W=$="";$=null;"default-style"===G.localName?$=null:($=G.getAttributeNS(l,"parent-style-name"),W=G.getAttributeNS(l,"family"),$=C.getODFElementsWithXPath(K,$?"//style:*[@style:name='"+$+"'][@style:family='"+W+"']":"//style:default-style[@style:family='"+W+"']",odf.Namespaces.lookupNamespaceURI)[0])}t||(t="font-size: "+parseFloat(I)*T+Y.getUnits(I)+ +";");s+=t}u+=s}if(s=q(n.element,l,"paragraph-properties"))t=s,s=""+b(t,x),(T=q(t,l,"background-image"))&&(G=T.getAttributeNS(y,"href"))&&(s=s+("background-image: url('odfkit:"+G+"');")+b(T,z)),(t=t.getAttributeNS(c,"line-height"))&&"normal"!==t&&(t=O.parseFoLineHeight(t),s="%"!==t.unit?s+("line-height: "+t.value+t.unit+";"):s+("line-height: "+t.value/100+";")),u+=s;if(s=q(n.element,l,"graphic-properties"))G=s,s=""+b(G,R),t=G.getAttributeNS(a,"opacity"),T=G.getAttributeNS(a,"fill"),G=G.getAttributeNS(a, +"fill-color"),"solid"===T||"hatch"===T?G&&"none"!==G?(t=isNaN(parseFloat(t))?1:parseFloat(t)/100,T=G.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,r),(G=(T=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(T))?{r:parseInt(T[1],16),g:parseInt(T[2],16),b:parseInt(T[3],16)}:null)&&(s+="background-color: rgba("+G.r+","+G.g+","+G.b+","+t+");")):s+="background: none;":"none"===T&&(s+="background: none;"),u+=s;if(s=q(n.element,l,"drawing-page-properties"))t=""+b(s,R),"true"===s.getAttributeNS(v,"background-visible")&& +(t+="background: none;"),u+=t;if(s=q(n.element,l,"table-cell-properties"))s=""+b(s,F),u+=s;if(s=q(n.element,l,"table-row-properties"))s=""+b(s,P),u+=s;if(s=q(n.element,l,"table-column-properties"))s=""+b(s,U),u+=s;if(s=q(n.element,l,"table-properties"))t=s,s=""+b(t,A),t=t.getAttributeNS(B,"border-model"),"collapsing"===t?s+="border-collapse:collapse;":"separating"===t&&(s+="border-collapse:separate;"),u+=s;if(0!==u.length)try{e.insertRule(k+"{"+u+"}",e.cssRules.length)}catch(ga){throw ga;}}for(var D in n.derivedStyles)n.derivedStyles.hasOwnProperty(D)&& +f(e,g,D,n.derivedStyles[D])}var a=odf.Namespaces.drawns,c=odf.Namespaces.fons,p=odf.Namespaces.officens,l=odf.Namespaces.stylens,u=odf.Namespaces.svgns,B=odf.Namespaces.tablens,w=odf.Namespaces.textns,y=odf.Namespaces.xlinkns,v=odf.Namespaces.presentationns,t={graphic:"draw","drawing-page":"draw",paragraph:"text",presentation:"presentation",ruby:"text",section:"text",table:"table","table-cell":"table","table-column":"table","table-row":"table",text:"text",list:"text",page:"office"},s={graphic:"circle connected control custom-shape ellipse frame g line measure page page-thumbnail path polygon polyline rect regular-polygon".split(" "), +paragraph:"alphabetical-index-entry-template h illustration-index-entry-template index-source-style object-index-entry-template p table-index-entry-template table-of-content-entry-template user-index-entry-template".split(" "),presentation:"caption circle connector control custom-shape ellipse frame g line measure page-thumbnail path polygon polyline rect regular-polygon".split(" "),"drawing-page":"caption circle connector control page custom-shape ellipse frame g line measure page-thumbnail path polygon polyline rect regular-polygon".split(" "), +ruby:["ruby","ruby-text"],section:"alphabetical-index bibliography illustration-index index-title object-index section table-of-content table-index user-index".split(" "),table:["background","table"],"table-cell":"body covered-table-cell even-columns even-rows first-column first-row last-column last-row odd-columns odd-rows table-cell".split(" "),"table-column":["table-column"],"table-row":["table-row"],text:"a index-entry-chapter index-entry-link-end index-entry-link-start index-entry-page-number index-entry-span index-entry-tab-stop index-entry-text index-title-template linenumbering-configuration list-level-style-number list-level-style-bullet outline-level-style span".split(" "), +list:["list-item"]},N=[[c,"color","color"],[c,"background-color","background-color"],[c,"font-weight","font-weight"],[c,"font-style","font-style"]],z=[[l,"repeat","background-repeat"]],x=[[c,"background-color","background-color"],[c,"text-align","text-align"],[c,"text-indent","text-indent"],[c,"padding","padding"],[c,"padding-left","padding-left"],[c,"padding-right","padding-right"],[c,"padding-top","padding-top"],[c,"padding-bottom","padding-bottom"],[c,"border-left","border-left"],[c,"border-right", +"border-right"],[c,"border-top","border-top"],[c,"border-bottom","border-bottom"],[c,"margin","margin"],[c,"margin-left","margin-left"],[c,"margin-right","margin-right"],[c,"margin-top","margin-top"],[c,"margin-bottom","margin-bottom"],[c,"border","border"]],R=[[c,"background-color","background-color"],[c,"min-height","min-height"],[a,"stroke","border"],[u,"stroke-color","border-color"],[u,"stroke-width","border-width"],[c,"border","border"],[c,"border-left","border-left"],[c,"border-right","border-right"], +[c,"border-top","border-top"],[c,"border-bottom","border-bottom"]],F=[[c,"background-color","background-color"],[c,"border-left","border-left"],[c,"border-right","border-right"],[c,"border-top","border-top"],[c,"border-bottom","border-bottom"],[c,"border","border"]],U=[[l,"column-width","width"]],P=[[l,"row-height","height"],[c,"keep-together",null]],A=[[l,"width","width"],[c,"margin-left","margin-left"],[c,"margin-right","margin-right"],[c,"margin-top","margin-top"],[c,"margin-bottom","margin-bottom"]], +ja=[[c,"background-color","background-color"],[c,"padding","padding"],[c,"padding-left","padding-left"],[c,"padding-right","padding-right"],[c,"padding-top","padding-top"],[c,"padding-bottom","padding-bottom"],[c,"border","border"],[c,"border-left","border-left"],[c,"border-right","border-right"],[c,"border-top","border-top"],[c,"border-bottom","border-bottom"],[c,"margin","margin"],[c,"margin-left","margin-left"],[c,"margin-right","margin-right"],[c,"margin-top","margin-top"],[c,"margin-bottom", +"margin-bottom"]],ka=[[c,"page-width","width"],[c,"page-height","height"]],G={border:!0,"border-left":!0,"border-right":!0,"border-top":!0,"border-bottom":!0,"stroke-width":!0},Z={},O=new odf.OdfUtils,ba,K,I,C=xmldom.XPath,Y=new core.CSSUnits;this.style2css=function(a,c,b,d,e){for(var l,h,p,k;c.cssRules.length;)c.deleteRule(c.cssRules.length-1);l=null;d&&(l=d.ownerDocument,K=d.parentNode);e&&(l=e.ownerDocument,K=e.parentNode);if(l)for(k in odf.Namespaces.forEachPrefix(function(a,b){h="@namespace "+ +a+" url("+b+");";try{c.insertRule(h,c.cssRules.length)}catch(d){}}),Z=b,ba=a,I=runtime.getWindow().getComputedStyle(document.body,null).getPropertyValue("font-size")||"12pt",a=g(d),d=g(e),e={},t)if(t.hasOwnProperty(k))for(p in b=e[k]={},n(a[k],b),n(d[k],b),b)b.hasOwnProperty(p)&&f(c,k,p,b[p])}}; +// Input 32 /* Copyright (C) 2012-2013 KO GmbH @@ -917,14 +702,39 @@ l[r].href);g(l,f,n+1,b,p)}):p&&p()}var l=new xmldom.XPath,c=new core.Base64;odf. @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils");runtime.loadClass("core.Utils"); -odf.ObjectNameGenerator=function(g,l){function c(a,b){var c={};this.generateName=function(){var e=b(),d=0,h;do h=a+d,d+=1;while(c[h]||e[h]);c[h]=!0;return h}}function m(){var a,b={};[g.rootElement.automaticStyles,g.rootElement.styles].forEach(function(c){for(a=c.firstChild;a;)a.nodeType===Node.ELEMENT_NODE&&a.namespaceURI===f&&"style"===a.localName&&(b[a.getAttributeNS(f,"name")]=!0),a=a.nextSibling});return b}var f=odf.Namespaces.stylens,n=odf.Namespaces.drawns,b=odf.Namespaces.xlinkns,p=new core.DomUtils, -r=(new core.Utils).hashString(l),d=null,k=null,a=null,e={},h={};this.generateStyleName=function(){null===d&&(d=new c("auto"+r+"_",function(){return m()}));return d.generateName()};this.generateFrameName=function(){null===k&&(p.getElementsByTagNameNS(g.rootElement.body,n,"frame").forEach(function(a){e[a.getAttributeNS(n,"name")]=!0}),k=new c("fr"+r+"_",function(){return e}));return k.generateName()};this.generateImageName=function(){null===a&&(p.getElementsByTagNameNS(g.rootElement.body,n,"image").forEach(function(a){a= -a.getAttributeNS(b,"href");a=a.substring(9,a.lastIndexOf("."));h[a]=!0}),a=new c("img"+r+"_",function(){return h}));return a.generateName()}}; -// Input 38 +runtime.loadClass("xmldom.XPath");runtime.loadClass("odf.Namespaces"); +odf.StyleInfo=function(){function g(a,c){var b,d,f,e,l,h=0;if(b=x[a.localName])if(f=b[a.namespaceURI])h=f.length;for(b=0;b + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -959,21 +769,10 @@ a.getAttributeNS(b,"href");a=a.substring(9,a.lastIndexOf("."));h[a]=!0}),a=new c @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.Utils");runtime.loadClass("odf.ObjectNameGenerator");runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.OdfContainer");runtime.loadClass("odf.StyleInfo");runtime.loadClass("odf.OdfUtils");runtime.loadClass("odf.TextStyleApplicator"); -odf.Formatting=function(){function g(a){return(a=t[a])?w.mergeObjects({},a):null}function l(){for(var a=k.rootElement.fontFaceDecls,b={},c,d,a=a&&a.firstChild;a;)a.nodeType===Node.ELEMENT_NODE&&(c=a.getAttributeNS(h,"name"))&&((d=a.getAttributeNS(e,"font-family"))||a.getElementsByTagNameNS(e,"font-face-uri")[0])&&(b[c]=d),a=a.nextSibling;return b}function c(a){for(var b=k.rootElement.styles.firstChild;b;){if(b.nodeType===Node.ELEMENT_NODE&&b.namespaceURI===h&&"default-style"===b.localName&&b.getAttributeNS(h, -"family")===a)return b;b=b.nextSibling}return null}function m(a,b,c){var e,d;c=c||[k.rootElement.automaticStyles,k.rootElement.styles];for(e=c.shift();e;){for(e=e.firstChild;e;){if(e.nodeType===Node.ELEMENT_NODE&&(d=e.getAttributeNS(h,"name"),e.namespaceURI===h&&"style"===e.localName&&e.getAttributeNS(h,"family")===b&&d===a||"list-style"===b&&e.namespaceURI===q&&"list-style"===e.localName&&d===a||"data"===b&&e.namespaceURI===u&&d===a))return e;e=e.nextSibling}e=c.shift()}return null}function f(a){for(var b, -c={},e=a.firstChild;e;){if(e.nodeType===Node.ELEMENT_NODE&&e.namespaceURI===h)for(c[e.nodeName]={},b=0;b @@ -1011,68 +810,17 @@ S=y.getElementsByTagNameNS(k.rootElement.automaticStyles,h,"page-layout");for(Y= @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils");runtime.loadClass("odf.OdfContainer");runtime.loadClass("odf.Formatting");runtime.loadClass("xmldom.XPath");runtime.loadClass("odf.FontLoader");runtime.loadClass("odf.Style2CSS");runtime.loadClass("odf.OdfUtils");runtime.loadClass("gui.AnnotationViewManager"); -odf.OdfCanvas=function(){function g(){function a(e){c=!0;runtime.setTimeout(function(){try{e()}catch(d){runtime.log(d)}c=!1;0 text|list-item > *:first-child:before {";if(E=J.getAttributeNS(M, -"style-name"))J=x[E],G=ia.getFirstNonWhitespaceChild(J),J=void 0,G&&("list-level-style-number"===G.localName?(J=G.getAttributeNS(D,"num-format"),E=G.getAttributeNS(D,"num-suffix"),I="",I={1:"decimal",a:"lower-latin",A:"upper-latin",i:"lower-roman",I:"upper-roman"},L=void 0,L=G.getAttributeNS(D,"num-prefix")||"",L=I.hasOwnProperty(J)?L+(" counter(list, "+I[J]+")"):J?L+("'"+J+"';"):L+" ''",E&&(L+=" '"+E+"'"),J=I="content: "+L+";"):"list-level-style-image"===G.localName?J="content: none;":"list-level-style-bullet"=== -G.localName&&(J="content: '"+G.getAttributeNS(M,"bullet-char")+"';")),G=J;if(R){for(J=v[R];J;)R=J,J=v[R];C+="counter-increment:"+R+";";G?(G=G.replace("list",R),C+=G):C+="content:counter("+R+");"}else R="",G?(G=G.replace("list",z),C+=G):C+="content: counter("+z+");",C+="counter-increment:"+z+";",g.insertRule("text|list#"+z+" {counter-reset:"+z+"}",g.cssRules.length);C+="}";v[z]=R;C&&g.insertRule(C,g.cssRules.length)}T.insertBefore(fa,T.firstChild);A();V(f);if(!a&&(f=[O],ja.hasOwnProperty("statereadychange")))for(g= -ja.statereadychange,G=0;G - - @licstart - This file is part of WebODF. - - WebODF 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. - - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend - - @source: http://www.webodf.org/ - @source: https://github.com/kogmbh/WebODF/ -*/ -ops.Member=function(g,l){var c={};this.getMemberId=function(){return g};this.getProperties=function(){return c};this.setProperties=function(g){Object.keys(g).forEach(function(f){c[f]=g[f]})};this.removeProperties=function(g){delete g.fullName;delete g.color;delete g.imageUrl;Object.keys(g).forEach(function(f){c.hasOwnProperty(f)&&delete c[f]})};runtime.assert(Boolean(g),"No memberId was supplied!");l.fullName||(l.fullName=runtime.tr("Unknown Author"));l.color||(l.color="black");l.imageUrl||(l.imageUrl= -"avatar-joe.png");c=l}; -// Input 42 +runtime.loadClass("core.PositionFilter");runtime.loadClass("odf.OdfUtils"); +ops.TextPositionFilter=function(g){function k(h,g,d){var f,a;if(g&&(f=e.lookLeftForCharacter(g),1===f||2===f&&(e.scanRightForAnyCharacter(d)||e.scanRightForAnyCharacter(e.nextNode(h)))))return q;f=null===g&&e.isParagraph(h);a=e.lookRightForCharacter(d);if(f)return a?q:e.scanRightForAnyCharacter(d)?b:q;if(!a)return b;g=g||e.previousNode(h);return e.scanLeftForAnyCharacter(g)?b:q}var e=new odf.OdfUtils,n=Node.ELEMENT_NODE,m=Node.TEXT_NODE,q=core.PositionFilter.FilterResult.FILTER_ACCEPT,b=core.PositionFilter.FilterResult.FILTER_REJECT; +this.acceptPosition=function(h){var r=h.container(),d=r.nodeType,f,a,c;if(d!==n&&d!==m)return b;if(d===m){if(!e.isGroupingElement(r.parentNode)||e.isWithinTrackedChanges(r.parentNode,g()))return b;d=h.unfilteredDomOffset();f=r.data;runtime.assert(d!==f.length,"Unexpected offset.");if(0/g,">").replace(/'/g,"'").replace(/"/g,""")}function e(g,q){var b="",h=n.filter?n.filter.acceptNode(q):NodeFilter.FILTER_ACCEPT,r;if(h===NodeFilter.FILTER_ACCEPT&&q.nodeType===Node.ELEMENT_NODE){g.push();r=g.getQName(q);var d,f=q.attributes,a,c,p,l="",u;d="<"+r;a=f.length;for(c=0;c")}if(h===NodeFilter.FILTER_ACCEPT||h===NodeFilter.FILTER_SKIP){for(h=q.firstChild;h;)b+=e(g,h),h=h.nextSibling;q.nodeValue&&(b+=k(q.nodeValue))}r&&(b+="",g.pop());return b}var n=this;this.filter=null;this.writeToString=function(k,n){if(!k)return"";var b=new g(n);return e(b,k)}}; +// Input 36 /* Copyright (C) 2013 KO GmbH @@ -1110,8 +858,10 @@ ops.Member=function(g,l){var c={};this.getMemberId=function(){return g};this.get @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.Server=function(){};ops.Server.prototype.connect=function(g,l){};ops.Server.prototype.networkStatus=function(){};ops.Server.prototype.login=function(g,l,c,m){};ops.Server.prototype.joinSession=function(g,l,c,m){};ops.Server.prototype.leaveSession=function(g,l,c,m){};ops.Server.prototype.getGenesisUrl=function(g){}; -// Input 43 +runtime.loadClass("odf.Namespaces");runtime.loadClass("xmldom.LSSerializer");runtime.loadClass("odf.OdfNodeFilter");runtime.loadClass("odf.TextSerializer"); +gui.Clipboard=function(){var g,k,e;this.setDataFromRange=function(e,m){var q=!0,b,h=e.clipboardData;b=runtime.getWindow();var r=m.startContainer.ownerDocument;!h&&b&&(h=b.clipboardData);h?(r=r.createElement("span"),r.appendChild(m.cloneContents()),b=h.setData("text/plain",k.writeToString(r)),q=q&&b,b=h.setData("text/html",g.writeToString(r,odf.Namespaces.namespaceMap)),q=q&&b,e.preventDefault()):q=!1;return q};g=new xmldom.LSSerializer;k=new odf.TextSerializer;e=new odf.OdfNodeFilter;g.filter=e;k.filter= +e}; +// Input 37 /* Copyright (C) 2012-2013 KO GmbH @@ -1149,8 +899,34 @@ ops.Server=function(){};ops.Server.prototype.connect=function(g,l){};ops.Server. @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.Operation=function(){};ops.Operation.prototype.init=function(g){};ops.Operation.prototype.execute=function(g){};ops.Operation.prototype.spec=function(){}; -// Input 44 +runtime.loadClass("core.Base64");runtime.loadClass("core.Zip");runtime.loadClass("xmldom.LSSerializer");runtime.loadClass("odf.StyleInfo");runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.OdfNodeFilter");runtime.loadClass("odf.MetadataManager"); +(function(){function g(a,c,b){for(a=a?a.firstChild:null;a;){if(a.localName===b&&a.namespaceURI===c)return a;a=a.nextSibling}return null}function k(a){var c,b=r.length;for(c=0;cb)break;f=f.nextSibling}a.insertBefore(c,f)}}}var q=new odf.StyleInfo, +b,h=odf.Namespaces.stylens,r="meta settings scripts font-face-decls styles automatic-styles master-styles body".split(" "),d=(new Date).getTime()+"_webodf_",f=new core.Base64;odf.ODFElement=function(){};odf.ODFDocumentElement=function(){};odf.ODFDocumentElement.prototype=new odf.ODFElement;odf.ODFDocumentElement.prototype.constructor=odf.ODFDocumentElement;odf.ODFDocumentElement.prototype.fontFaceDecls=null;odf.ODFDocumentElement.prototype.manifest=null;odf.ODFDocumentElement.prototype.settings=null; +odf.ODFDocumentElement.namespaceURI="urn:oasis:names:tc:opendocument:xmlns:office:1.0";odf.ODFDocumentElement.localName="document";odf.OdfPart=function(a,c,b,d){var f=this;this.size=0;this.type=null;this.name=a;this.container=b;this.url=null;this.mimetype=c;this.onstatereadychange=this.document=null;this.EMPTY=0;this.LOADING=1;this.DONE=2;this.state=this.EMPTY;this.data="";this.load=function(){null!==d&&(this.mimetype=c,d.loadAsDataURL(a,c,function(a,c){a&&runtime.log(a);f.url=c;if(f.onchange)f.onchange(f); +if(f.onstatereadychange)f.onstatereadychange(f)}))}};odf.OdfPart.prototype.load=function(){};odf.OdfPart.prototype.getUrl=function(){return this.data?"data:;base64,"+f.toBase64(this.data):null};odf.OdfContainer=function c(p,l){function k(c){for(var b=c.firstChild,d;b;)d=b.nextSibling,b.nodeType===Node.ELEMENT_NODE?k(b):b.nodeType===Node.PROCESSING_INSTRUCTION_NODE&&c.removeChild(b),b=d}function r(c,b){for(var d=c&&c.firstChild;d;)d.nodeType===Node.ELEMENT_NODE&&d.setAttributeNS("urn:webodf:names:scope", +"scope",b),d=d.nextSibling}function w(c){var b={},d;for(c=c.firstChild;c;)c.nodeType===Node.ELEMENT_NODE&&c.namespaceURI===h&&"font-face"===c.localName&&(d=c.getAttributeNS(h,"name"),b[d]=c),c=c.nextSibling;return b}function y(c,b){var d=null,f,e,h;if(c)for(d=c.cloneNode(!0),f=d.firstElementChild;f;)e=f.nextElementSibling,(h=f.getAttributeNS("urn:webodf:names:scope","scope"))&&h!==b&&d.removeChild(f),f=e;return d}function v(c,b){var d,f,e,l=null,g={};if(c)for(b.forEach(function(c){q.collectUsedFontFaces(g, +c)}),l=c.cloneNode(!0),d=l.firstElementChild;d;)f=d.nextElementSibling,e=d.getAttributeNS(h,"name"),g[e]||l.removeChild(d),d=f;return l}function t(c){var b=H.rootElement.ownerDocument,d;if(c){k(c.documentElement);try{d=b.importNode(c.documentElement,!0)}catch(f){}}return d}function s(c){H.state=c;if(H.onchange)H.onchange(H);if(H.onstatereadychange)H.onstatereadychange(H)}function N(c){L=null;H.rootElement=c;c.fontFaceDecls=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","font-face-decls"); +c.styles=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles");c.automaticStyles=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles");c.masterStyles=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles");c.body=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","body");c.meta=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","meta")}function z(b){var f=t(b),e=H.rootElement,h;f&&"document-styles"===f.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"=== +f.namespaceURI?(e.fontFaceDecls=g(f,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","font-face-decls"),m(e,e.fontFaceDecls),h=g(f,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles"),e.styles=h||b.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles"),m(e,e.styles),h=g(f,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles"),e.automaticStyles=h||b.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles"),r(e.automaticStyles, +"document-styles"),m(e,e.automaticStyles),f=g(f,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles"),e.masterStyles=f||b.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles"),m(e,e.masterStyles),q.prefixStyleNames(e.automaticStyles,d,e.masterStyles)):s(c.INVALID)}function x(b){b=t(b);var d,f,e,l;if(b&&"document-content"===b.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===b.namespaceURI){d=H.rootElement;e=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0", +"font-face-decls");if(d.fontFaceDecls&&e){l=d.fontFaceDecls;var p,k,n,v,u={};f=w(l);v=w(e);for(e=e.firstElementChild;e;){p=e.nextElementSibling;if(e.namespaceURI===h&&"font-face"===e.localName)if(k=e.getAttributeNS(h,"name"),f.hasOwnProperty(k)){if(!e.isEqualNode(f[k])){n=k;for(var z=f,F=v,x=0,A=void 0,A=n=n.replace(/\d+$/,"");z.hasOwnProperty(A)||F.hasOwnProperty(A);)x+=1,A=n+x;n=A;e.setAttributeNS(h,"style:name",n);l.appendChild(e);f[n]=e;delete v[k];u[k]=n}}else l.appendChild(e),f[k]=e,delete v[k]; +e=p}l=u}else e&&(d.fontFaceDecls=e,m(d,e));f=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles");r(f,"document-content");l&&q.changeFontFaceNames(f,l);if(d.automaticStyles&&f)for(l=f.firstChild;l;)d.automaticStyles.appendChild(l),l=f.firstChild;else f&&(d.automaticStyles=f,m(d,f));b=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","body");if(null===b)throw" tag is mising.";d.body=b;m(d,d.body)}else s(c.INVALID)}function R(c){var d=t(c),f;d&&"document-meta"=== +d.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===d.namespaceURI&&(f=H.rootElement,d=g(d,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","meta"),f.meta=d||c.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","meta"),m(f,f.meta),b=new odf.MetadataManager(f.meta))}function F(c){c=t(c);var b;c&&"document-settings"===c.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===c.namespaceURI&&(b=H.rootElement,b.settings=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0", +"settings"),m(b,b.settings))}function U(c){c=t(c);var b;if(c&&"manifest"===c.localName&&"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"===c.namespaceURI)for(b=H.rootElement,b.manifest=c,c=b.manifest.firstElementChild;c;)"file-entry"===c.localName&&"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"===c.namespaceURI&&(Q[c.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","full-path")]=c.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","media-type")),c=c.nextElementSibling} +function P(b){var d=b.shift();d?X.loadAsDOM(d.path,function(f,e){d.handler(e);f||H.state===c.INVALID||P(b)}):s(c.DONE)}function A(c){var b="";odf.Namespaces.forEachPrefix(function(c,d){b+=" xmlns:"+c+'="'+d+'"'});return''}function ja(){var c=new xmldom.LSSerializer,b=A("document-meta");c.filter=new odf.OdfNodeFilter;b+=c.writeToString(H.rootElement.meta,odf.Namespaces.namespaceMap);return b+""}function ka(c, +b){var d=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest:file-entry");d.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest:full-path",c);d.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest:media-type",b);return d}function G(){var c=runtime.parseXML(''),b=g(c,"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0", +"manifest"),d=new xmldom.LSSerializer,f;for(f in Q)Q.hasOwnProperty(f)&&b.appendChild(ka(f,Q[f]));d.filter=new odf.OdfNodeFilter;return'\n'+d.writeToString(c,odf.Namespaces.namespaceMap)}function Z(){var c=new xmldom.LSSerializer,b=A("document-settings");c.filter=new odf.OdfNodeFilter;b+=c.writeToString(H.rootElement.settings,odf.Namespaces.namespaceMap);return b+""}function O(){var c,b,f,h=odf.Namespaces.namespaceMap, +l=new xmldom.LSSerializer,g=A("document-styles");b=y(H.rootElement.automaticStyles,"document-styles");f=H.rootElement.masterStyles.cloneNode(!0);c=v(H.rootElement.fontFaceDecls,[f,H.rootElement.styles,b]);q.removePrefixFromStyleNames(b,d,f);l.filter=new e(f,b);g+=l.writeToString(c,h);g+=l.writeToString(H.rootElement.styles,h);g+=l.writeToString(b,h);g+=l.writeToString(f,h);return g+""}function ba(){var c,b,d=odf.Namespaces.namespaceMap,f=new xmldom.LSSerializer,e=A("document-content"); +b=y(H.rootElement.automaticStyles,"document-content");c=v(H.rootElement.fontFaceDecls,[b]);f.filter=new n(H.rootElement.body,b);e+=f.writeToString(c,d);e+=f.writeToString(b,d);e+=f.writeToString(H.rootElement.body,d);return e+""}function K(b,d){runtime.loadXML(b,function(b,f){if(b)d(b);else{var e=t(f);e&&"document"===e.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===e.namespaceURI?(N(e),s(c.DONE)):s(c.INVALID)}})}function I(){function d(c,b){var f;b||(b=c); +f=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0",b);h[c]=f;h.appendChild(f)}var f=new core.Zip("",null),e=runtime.byteArrayFromString("application/vnd.oasis.opendocument.text","utf8"),h=H.rootElement,l=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","text");f.save("mimetype",e,!1,new Date);d("meta");d("settings");d("scripts");d("fontFaceDecls","font-face-decls");d("styles");d("automaticStyles","automatic-styles");d("masterStyles","master-styles"); +d("body");h.body.appendChild(l);b=new odf.MetadataManager(h.meta);s(c.DONE);return f}function C(){var c,d=new Date,f=runtime.getWindow();c="WebODF/"+("undefined"!==String(typeof webodf_version)?webodf_version:"FromSource");f&&(c=c+" "+f.navigator.userAgent);b.setMetadata({"meta:generator":c},null);c=runtime.byteArrayFromString(Z(),"utf8");X.save("settings.xml",c,!0,d);c=runtime.byteArrayFromString(ja(),"utf8");X.save("meta.xml",c,!0,d);c=runtime.byteArrayFromString(O(),"utf8");X.save("styles.xml", +c,!0,d);c=runtime.byteArrayFromString(ba(),"utf8");X.save("content.xml",c,!0,d);c=runtime.byteArrayFromString(G(),"utf8");X.save("META-INF/manifest.xml",c,!0,d)}function Y(c,b){C();X.writeAs(c,function(c){b(c)})}var H=this,X,Q={},L;this.onstatereadychange=l;this.state=this.onchange=null;this.setRootElement=N;this.getContentElement=function(){var c;L||(c=H.rootElement.body,L=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","text")||g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","presentation")|| +g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","spreadsheet"));if(!L)throw"Could not find content element in .";return L};this.getDocumentType=function(){var c=H.getContentElement();return c&&c.localName};this.getMetadataManager=function(){return b};this.getPart=function(c){return new odf.OdfPart(c,Q[c],H,X)};this.getPartData=function(c,b){X.load(c,b)};this.createByteArray=function(c,b){C();X.createByteArray(c,b)};this.saveAs=Y;this.save=function(c){Y(p,c)};this.getUrl=function(){return p}; +this.setBlob=function(c,b,d){d=f.convertBase64ToByteArray(d);X.save(c,d,!1,new Date);Q.hasOwnProperty(c)&&runtime.log(c+" has been overwritten.");Q[c]=b};this.removeBlob=function(c){var b=X.remove(c);runtime.assert(b,"file is not found: "+c);delete Q[c]};this.state=c.LOADING;this.rootElement=function(c){var b=document.createElementNS(c.namespaceURI,c.localName),d;c=new c.Type;for(d in c)c.hasOwnProperty(d)&&(b[d]=c[d]);return b}({Type:odf.ODFDocumentElement,namespaceURI:odf.ODFDocumentElement.namespaceURI, +localName:odf.ODFDocumentElement.localName});X=p?new core.Zip(p,function(b,d){X=d;b?K(p,function(d){b&&(X.error=b+"\n"+d,s(c.INVALID))}):P([{path:"styles.xml",handler:z},{path:"content.xml",handler:x},{path:"meta.xml",handler:R},{path:"settings.xml",handler:F},{path:"META-INF/manifest.xml",handler:U}])}):I()};odf.OdfContainer.EMPTY=0;odf.OdfContainer.LOADING=1;odf.OdfContainer.DONE=2;odf.OdfContainer.INVALID=3;odf.OdfContainer.SAVING=4;odf.OdfContainer.MODIFIED=5;odf.OdfContainer.getContainer=function(c){return new odf.OdfContainer(c, +null)};return odf.OdfContainer})(); +// Input 38 /* Copyright (C) 2012-2013 KO GmbH @@ -1188,8 +964,11 @@ ops.Operation=function(){};ops.Operation.prototype.init=function(g){};ops.Operat @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpAddCursor=function(){var g,l;this.init=function(c){g=c.memberid;l=c.timestamp};this.isEdit=!1;this.execute=function(c){var l=c.getCursor(g);if(l)return!1;l=new ops.OdtCursor(g,c);c.addCursor(l);c.emit(ops.OdtDocument.signalCursorAdded,l);return!0};this.spec=function(){return{optype:"AddCursor",memberid:g,timestamp:l}}}; -// Input 45 +runtime.loadClass("core.Base64");runtime.loadClass("xmldom.XPath");runtime.loadClass("odf.OdfContainer"); +(function(){function g(k,m,q,b,h){var r,d=0,f;for(f in k)if(k.hasOwnProperty(f)){if(d===q){r=f;break}d+=1}r?m.getPartData(k[r].href,function(a,c){if(a)runtime.log(a);else if(c){var d="@font-face { font-family: '"+(k[r].family||r)+"'; src: url(data:application/x-font-ttf;charset=binary;base64,"+e.convertUTF8ArrayToBase64(c)+') format("truetype"); }';try{b.insertRule(d,b.cssRules.length)}catch(f){runtime.log("Problem inserting rule in CSS: "+runtime.toJson(f)+"\nRule: "+d)}}else runtime.log("missing font data for "+ +k[r].href);g(k,m,q+1,b,h)}):h&&h()}var k=xmldom.XPath,e=new core.Base64;odf.FontLoader=function(){this.loadFonts=function(e,m){for(var q=e.rootElement.fontFaceDecls;m.cssRules.length;)m.deleteRule(m.cssRules.length-1);if(q){var b={},h,r,d,f;if(q)for(q=k.getODFElementsWithXPath(q,"style:font-face[svg:font-face-src]",odf.Namespaces.lookupNamespaceURI),h=0;h @@ -1227,12 +1006,11 @@ ops.OpAddCursor=function(){var g,l;this.init=function(c){g=c.memberid;l=c.timest @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils");runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.OdfUtils"); -gui.StyleHelper=function(g){function l(b,c,f){var d=!0,k;for(k=0;k @@ -1270,10 +1048,12 @@ function(b){return c(b,"fo:text-align",["center"])};this.isAlignedRight=function @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("gui.StyleHelper");runtime.loadClass("odf.OdfUtils"); -ops.OpApplyDirectStyling=function(){function g(b){var c=0<=f?m+f:m,d=b.getIteratorAtPosition(0<=f?m:m+f),c=f?b.getIteratorAtPosition(c):d;b=b.getDOM().createRange();b.setStart(d.container(),d.unfilteredDomOffset());b.setEnd(c.container(),c.unfilteredDomOffset());return b}var l,c,m,f,n,b=new odf.OdfUtils;this.init=function(b){l=b.memberid;c=b.timestamp;m=parseInt(b.position,10);f=parseInt(b.length,10);n=b.setProperties};this.isEdit=!0;this.execute=function(f){var m=g(f),d=b.getImpactedParagraphs(m); -(new gui.StyleHelper(f.getFormatting())).applyStyle(l,m,n);m.detach();f.getOdfCanvas().refreshCSS();f.fixCursorPositions();d.forEach(function(b){f.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:b,memberId:l,timeStamp:c})});f.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"ApplyDirectStyling",memberid:l,timestamp:c,position:m,length:f,setProperties:n}}}; -// Input 47 +runtime.loadClass("core.DomUtils");runtime.loadClass("core.LoopWatchDog");runtime.loadClass("odf.Namespaces");odf.TextStyleApplicatorFormatting=function(){};odf.TextStyleApplicatorFormatting.prototype.getAppliedStylesForElement=function(g){};odf.TextStyleApplicatorFormatting.prototype.createDerivedStyleObject=function(g,k,e){};odf.TextStyleApplicatorFormatting.prototype.updateStyle=function(g,k){}; +odf.TextStyleApplicator=function(g,k,e){function n(b){function f(a,c){return"object"===typeof a&&"object"===typeof c?Object.keys(a).every(function(b){return f(a[b],c[b])}):a===c}this.isStyleApplied=function(a){a=k.getAppliedStylesForElement(a);return f(b,a)}}function m(b){var f={};this.applyStyleToContainer=function(a){var c;c=a.getAttributeNS(h,"style-name");var p=a.ownerDocument;c=c||"";if(!f.hasOwnProperty(c)){var l=c,m;m=c?k.createDerivedStyleObject(c,"text",b):b;p=p.createElementNS(r,"style:style"); +k.updateStyle(p,m);p.setAttributeNS(r,"style:name",g.generateStyleName());p.setAttributeNS(r,"style:family","text");p.setAttributeNS("urn:webodf:names:scope","scope","document-content");e.appendChild(p);f[l]=p}c=f[c].getAttributeNS(r,"name");a.setAttributeNS(h,"text:style-name",c)}}function q(d,f){var a=d.ownerDocument,c=d.parentNode,e,l,g=new core.LoopWatchDog(1E4);l=[];"span"!==c.localName||c.namespaceURI!==h?(e=a.createElementNS(h,"text:span"),c.insertBefore(e,d),c=!1):(d.previousSibling&&!b.rangeContainsNode(f, +c.firstChild)?(e=c.cloneNode(!1),c.parentNode.insertBefore(e,c.nextSibling)):e=c,c=!0);l.push(d);for(a=d.nextSibling;a&&b.rangeContainsNode(f,a);)g.check(),l.push(a),a=a.nextSibling;l.forEach(function(a){a.parentNode!==e&&e.appendChild(a)});if(a&&c)for(l=e.cloneNode(!1),e.parentNode.insertBefore(l,e.nextSibling);a;)g.check(),c=a.nextSibling,l.appendChild(a),a=c;return e}var b=new core.DomUtils,h=odf.Namespaces.textns,r=odf.Namespaces.stylens;this.applyStyle=function(b,f,a){var c={},e,h,g,k;runtime.assert(a&& +a.hasOwnProperty("style:text-properties"),"applyStyle without any text properties");c["style:text-properties"]=a["style:text-properties"];g=new m(c);k=new n(c);b.forEach(function(a){e=k.isStyleApplied(a);!1===e&&(h=q(a,f),g.applyStyleToContainer(h))})}}; +// Input 41 /* Copyright (C) 2012-2013 KO GmbH @@ -1311,8 +1091,21 @@ ops.OpApplyDirectStyling=function(){function g(b){var c=0<=f?m+f:m,d=b.getIterat @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpRemoveCursor=function(){var g,l;this.init=function(c){g=c.memberid;l=c.timestamp};this.isEdit=!1;this.execute=function(c){return c.removeCursor(g)?!0:!1};this.spec=function(){return{optype:"RemoveCursor",memberid:g,timestamp:l}}}; -// Input 48 +runtime.loadClass("core.Utils");runtime.loadClass("odf.ObjectNameGenerator");runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.OdfContainer");runtime.loadClass("odf.StyleInfo");runtime.loadClass("odf.OdfUtils");runtime.loadClass("odf.TextStyleApplicator"); +odf.Formatting=function(){function g(a){return(a=s[a])?t.mergeObjects({},a):{}}function k(a,c,b){for(a=a&&a.firstElementChild;a&&(a.namespaceURI!==c||a.localName!==b);)a=a.nextElementSibling;return a}function e(){for(var c=a.rootElement.fontFaceDecls,b={},d,f,c=c&&c.firstElementChild;c;){if(d=c.getAttributeNS(l,"name"))if((f=c.getAttributeNS(p,"font-family"))||0 @@ -1350,8 +1143,36 @@ ops.OpRemoveCursor=function(){var g,l;this.init=function(c){g=c.memberid;l=c.tim @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpMoveCursor=function(){var g,l,c,m,f;this.init=function(n){g=n.memberid;l=n.timestamp;c=n.position;m=n.length||0;f=n.selectionType||ops.OdtCursor.RangeSelection};this.isEdit=!1;this.execute=function(l){var b=l.getCursor(g),p;if(!b)return!1;p=l.convertCursorToDomRange(c,m);b.setSelectedRange(p,0<=m);b.setSelectionType(f);l.emit(ops.OdtDocument.signalCursorMoved,b);return!0};this.spec=function(){return{optype:"MoveCursor",memberid:g,timestamp:l,position:c,length:m,selectionType:f}}}; -// Input 49 +runtime.loadClass("core.DomUtils");runtime.loadClass("odf.OdfContainer");runtime.loadClass("odf.Formatting");runtime.loadClass("xmldom.XPath");runtime.loadClass("odf.FontLoader");runtime.loadClass("odf.Style2CSS");runtime.loadClass("odf.OdfUtils");runtime.loadClass("gui.AnnotationViewManager"); +(function(){function g(){function a(d){b=!0;runtime.setTimeout(function(){try{d()}catch(f){runtime.log(String(f))}b=!1;0 text|list-item > *:first-child:before {";if(Q=K.getAttributeNS(N,"style-name")){K=E[Q];J=P.getFirstNonWhitespaceChild(K);K=void 0;if(J)if("list-level-style-number"=== +J.localName){K=J.getAttributeNS(v,"num-format");Q=J.getAttributeNS(v,"num-suffix")||"";var V="",V={1:"decimal",a:"lower-latin",A:"upper-latin",i:"lower-roman",I:"upper-roman"},S=void 0,S=J.getAttributeNS(v,"num-prefix")||"",S=V.hasOwnProperty(K)?S+(" counter(list, "+V[K]+")"):K?S+("'"+K+"';"):S+" ''";Q&&(S+=" '"+Q+"'");K=V="content: "+S+";"}else"list-level-style-image"===J.localName?K="content: none;":"list-level-style-bullet"===J.localName&&(K="content: '"+J.getAttributeNS(N,"bullet-char")+"';"); +J=K}if(I){for(K=A[I];K;)K=A[K];D+="counter-increment:"+I+";";J?(J=J.replace("list",I),D+=J):D+="content:counter("+I+");"}else I="",J?(J=J.replace("list",O),D+=J):D+="content: counter("+O+");",D+="counter-increment:"+O+";",p.insertRule("text|list#"+O+" {counter-reset:"+O+"}",p.cssRules.length);D+="}";A[O]=I;D&&p.insertRule(D,p.cssRules.length)}L.insertBefore(da,L.firstChild);z();ba(k);if(!l&&(k=[H],ga.hasOwnProperty("statereadychange")))for(p=ga.statereadychange,J=0;J @@ -1389,8 +1210,64 @@ ops.OpMoveCursor=function(){var g,l,c,m,f;this.init=function(n){g=n.memberid;l=n @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpSetBlob=function(){var g,l,c,m,f;this.init=function(n){g=n.memberid;l=n.timestamp;c=n.filename;m=n.mimetype;f=n.content};this.isEdit=!0;this.execute=function(g){g.getOdfCanvas().odfContainer().setBlob(c,m,f);return!0};this.spec=function(){return{optype:"SetBlob",memberid:g,timestamp:l,filename:c,mimetype:m,content:f}}}; -// Input 50 +runtime.loadClass("core.DomUtils");runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.OdfUtils"); +gui.StyleHelper=function(g){function k(b,e,g){var d=!0,f;for(f=0;f>>8):(Aa(c&255),Aa(c>>>8))},Ba=function(){t=(t<<5^p[F+3-1]&255)&8191;s=B[32768+t];B[F&32767]=s;B[32768+t]=F},V=function(a,c){y>16-c?(w|=a<>16-y,y+=c-16):(w|=a<a;a++)p[a]=p[a+32768];U-=32768;F-=32768;v-=32768;for(a=0;8192>a;a++)c=B[32768+a],B[32768+a]=32768<=c?c-32768:0;for(a=0;32768>a;a++)c=B[a],B[a]=32768<=c?c-32768:0;b+=32768}P||(a=na(p,F+A,b),0>=a?P=!0:A+=a)},oa=function(a){var c=ja,b=F,d,f=R,e=32506=Z&&(c>>=2);do if(d=a,p[d+f]===g&&p[d+f-1]===l&&p[d]===p[b]&&p[++d]===p[b+1]){b+= +2;d++;do++b;while(p[b]===p[++d]&&p[++b]===p[++d]&&p[++b]===p[++d]&&p[++b]===p[++d]&&p[++b]===p[++d]&&p[++b]===p[++d]&&p[++b]===p[++d]&&p[++b]===p[++d]&&bf){U=a;f=d;if(258<=d)break;l=p[b+f-1];g=p[b+f]}a=B[a&32767]}while(a>e&&0!==--c);return f},Ca=function(a,c){u[S++]=c;0===a?O[c].fc++:(a--,O[ma[c]+256+1].fc++,ba[(256>a?T[a]:T[256+(a>>7)])&255].fc++,l[ga++]=a,fa|=E);E<<=1;0===(S&7)&&(da[D++]=fa,fa=0,E=1);if(2f;f++)b+=ba[f].fc* +(5+qa[f]);b>>=3;if(ga>=1,b<<=1;while(0<--c);return b>>1},xa=function(a,c){var b=[];b.length=16;var d=0,f;for(f=1;15>=f;f++)d=d+Q[f-1]<<1,b[f]=d;for(d=0;d<=c;d++)f=a[d].dl,0!==f&&(a[d].fc=Fa(b[f]++,f))},Da=function(a){var c=a.dyn_tree,b=a.static_tree,d=a.elems,f,e= +-1,h=d;ca=0;la=573;for(f=0;fca;)f=L[++ca]=2>e?++e:0,c[f].fc=1,aa[f]=0,pa--,null!==b&&(ha-=b[f].dl);a.max_code=e;for(f=ca>>1;1<=f;f--)va(c,f);do f=L[1],L[1]=L[ca--],va(c,1),b=L[1],L[--la]=f,L[--la]=b,c[h].fc=c[f].fc+c[b].fc,aa[h]=aa[f]>aa[b]+1?aa[f]:aa[b]+1,c[f].dl=c[b].dl=h,L[1]=h++,va(c,1);while(2<=ca);L[--la]=L[1];h=a.dyn_tree;f=a.extra_bits;var d=a.extra_base,b=a.max_code,l=a.max_length,g=a.static_tree,k,p,m,n,r=0;for(p=0;15>=p;p++)Q[p]= +0;h[L[la]].dl=0;for(a=la+1;573>a;a++)k=L[a],p=h[h[k].dl].dl+1,p>l&&(p=l,r++),h[k].dl=p,k>b||(Q[p]++,m=0,k>=d&&(m=f[k-d]),n=h[k].fc,pa+=n*(p+m),null!==g&&(ha+=n*(g[k].dl+m)));if(0!==r){do{for(p=l-1;0===Q[p];)p--;Q[p]--;Q[p+1]+=2;Q[l]--;r-=2}while(0b||(h[f].dl!==p&&(pa+=(p-h[f].dl)*h[f].fc,h[f].fc=p),k--)}xa(c,e)},ya=function(a,c){var b,d=-1,f,e=a[0].dl,h=0,l=7,g=4;0===e&&(l=138,g=3);a[c+1].dl=65535;for(b=0;b<=c;b++)f=e,e=a[b+1].dl,++h=h?C[17].fc++:C[18].fc++,h=0,d=f,0===e?(l=138,g=3):f===e?(l=6,g=3):(l=7,g=4))},Ga=function(){8b?T[b]:T[256+(b>>7)])&255,J(g,c),k=qa[g],0!==k&&(b-=W[g],V(b,k))),h>>=1;while(d=h?(J(17,C),V(h-3,3)):(J(18,C),V(h-11,7));h=0;d=f;0===e?(l=138,g=3):f===e?(l=6,g=3):(l=7,g=4)}},Ja=function(){var a;for(a=0;286>a;a++)O[a].fc=0;for(a=0;30>a;a++)ba[a].fc=0;for(a=0;19>a;a++)C[a].fc=0;O[256].fc=1;fa=S=ga=D=pa=ha=0;E=1},Ha=function(a){var c,b,d,f;f=F-v;da[D]=fa;Da(Y);Da(H);ya(O,Y.max_code);ya(ba,H.max_code);Da(X);for(d=18;3<=d&&0===C[wa[d]].dl;d--); +pa+=3*(d+1)+14;c=pa+3+7>>3;b=ha+3+7>>3;b<=c&&(c=b);if(f+4<=c&&0<=v)for(V(0+a,3),Ga(),ta(f),ta(~f),d=0;db.len&&(g=b.len);for(k=0;kf-a&&(g=f-a);for(k=0;kk;k++)for($[k]=g,l=0;l< +1<k;k++)for(W[k]=g,l=0;l<1<>=7;30>k;k++)for(W[k]=g<<7,l=0;l<1<=l;l++)Q[l]=0;for(l=0;143>=l;)K[l++].dl=8,Q[8]++;for(;255>=l;)K[l++].dl=9,Q[9]++;for(;279>=l;)K[l++].dl=7,Q[7]++;for(;287>=l;)K[l++].dl=8,Q[8]++;xa(K,287);for(l=0;30>l;l++)I[l].dl=5,I[l].fc=Fa(l,5);Ja()}for(l=0;8192>l;l++)B[32768+l]=0;ka=sa[G].max_lazy;Z=sa[G].good_length;ja=sa[G].max_chain;v=F=0;A=na(p,0,65536);if(0>=A)P=!0,A=0;else{for(P= +!1;262>A&&!P;)ea();for(l=t=0;2>l;l++)t=(t<<5^p[l]&255)&8191}b=null;a=f=0;3>=G?(R=2,x=0):(x=2,z=0);c=!1}r=!0;if(0===A)return c=!0,0}l=Ka(d,e,h);if(l===h)return h;if(c)return l;if(3>=G)for(;0!==A&&null===b;){Ba();0!==s&&32506>=F-s&&(x=oa(s),x>A&&(x=A));if(3<=x)if(k=Ca(F-U,x-3),A-=x,x<=ka){x--;do F++,Ba();while(0!==--x);F++}else F+=x,x=0,t=p[F]&255,t=(t<<5^p[F+1]&255)&8191;else k=Ca(0,p[F]&255),A--,F++;k&&(Ha(0),v=F);for(;262>A&&!P;)ea()}else for(;0!==A&&null===b;){Ba();R=x;N=U;x=2;0!==s&&R= +F-s&&(x=oa(s),x>A&&(x=A),3===x&&4096A&&!P;)ea()}0===A&&(0!==z&&Ca(0,p[F-1]&255),Ha(1),c=!0);return l+Ka(d,l+e,h-l)};this.deflate=function(a,c){var f,e;ia=a;Ea=0;"undefined"===String(typeof c)&&(c=6);(f=c)?1>f?f=1:9f;f++)O[f]=new g;ba=[];ba.length=61;for(f=0;61>f;f++)ba[f]=new g;K=[];K.length=288;for(f=0;288>f;f++)K[f]=new g;I=[];I.length=30;for(f=0;30>f;f++)I[f]=new g;C=[];C.length=39;for(f=0;39>f;f++)C[f]=new g;Y=new k;H=new k;X=new k;Q=[];Q.length=16;L=[];L.length=573;aa=[];aa.length=573;ma=[];ma.length=256;T=[];T.length=512;$=[];$.length=29;W=[];W.length=30;da=[];da.length=1024}var n=Array(1024),v=[],s=[];for(f=La(n,0,n.length);0 + + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +ops.Member=function(g,k){var e={};this.getMemberId=function(){return g};this.getProperties=function(){return e};this.setProperties=function(g){Object.keys(g).forEach(function(k){e[k]=g[k]})};this.removeProperties=function(g){delete g.fullName;delete g.color;delete g.imageUrl;Object.keys(g).forEach(function(g){e.hasOwnProperty(g)&&delete e[g]})};runtime.assert(Boolean(g),"No memberId was supplied!");k.fullName||(k.fullName=runtime.tr("Unknown Author"));k.color||(k.color="black");k.imageUrl||(k.imageUrl= +"avatar-joe.png");e=k}; +// Input 48 /* Copyright (C) 2012-2013 KO GmbH @@ -1428,7 +1305,32 @@ ops.OpSetBlob=function(){var g,l,c,m,f;this.init=function(n){g=n.memberid;l=n.ti @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpRemoveBlob=function(){var g,l,c;this.init=function(m){g=m.memberid;l=m.timestamp;c=m.filename};this.isEdit=!0;this.execute=function(g){g.getOdfCanvas().odfContainer().removeBlob(c);return!0};this.spec=function(){return{optype:"RemoveBlob",memberid:g,timestamp:l,filename:c}}}; +runtime.loadClass("core.DomUtils");runtime.loadClass("core.PositionFilter");runtime.loadClass("odf.OdfUtils"); +(function(){function g(e,g,m){function q(a,c){function b(a){for(var c=0;a&&a.previousSibling;)c+=1,a=a.previousSibling;return c}this.steps=a;this.node=c;this.setIteratorPosition=function(a){a.setUnfilteredPosition(c.parentNode,b(c));do if(g.acceptPosition(a)===u)break;while(a.nextPosition())}}function b(a){return a.nodeType===Node.ELEMENT_NODE&&a.getAttributeNS(d,"nodeId")}function h(a){var c=k;a.setAttributeNS(d,"nodeId",c.toString());k+=1;return c}function r(c,f){var h,l=null;for(c=c.childNodes[f]|| +c;!l&&c&&c!==e;)(h=b(c))&&(l=a[h])&&l.node!==c&&(runtime.log("Cloned node detected. Creating new bookmark"),l=null,c.removeAttributeNS(d,"nodeId")),c=c.parentNode;return l}var d="urn:webodf:names:steps",f={},a={},c=new odf.OdfUtils,p=new core.DomUtils,l,u=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.updateCache=function(d,e,l,g){var k;0===l&&c.isParagraph(e)?(k=!0,g||(d+=1)):e.hasChildNodes()&&e.childNodes[l]&&(e=e.childNodes[l],(k=c.isParagraph(e))&&(d+=1));k&&(l=b(e)||h(e),(g=a[l])?g.node=== +e?g.steps=d:(runtime.log("Cloned node detected. Creating new bookmark"),l=h(e),g=a[l]=new q(d,e)):g=a[l]=new q(d,e),l=g,d=Math.ceil(l.steps/m)*m,e=f[d],!e||l.steps>e.steps)&&(f[d]=l)};this.setToClosestStep=function(a,c){for(var b=Math.floor(a/m)*m,d;!d&&0!==b;)d=f[b],b-=m;d=d||l;d.setIteratorPosition(c);return d.steps};this.setToClosestDomPoint=function(a,c,b){var d;if(a===e&&0===c)d=l;else if(a===e&&c===e.childNodes.length)d=Object.keys(f).map(function(a){return f[a]}).reduce(function(a,c){return c.steps> +a.steps?c:a},l);else if(d=r(a,c),!d)for(b.setUnfilteredPosition(a,c);!d&&b.previousNode();)d=r(b.container(),b.unfilteredDomOffset());d=d||l;d.setIteratorPosition(b);return d.steps};this.updateCacheAtPoint=function(c,d){var h={};Object.keys(a).map(function(c){return a[c]}).filter(function(a){return a.steps>c}).forEach(function(c){var l=Math.ceil(c.steps/m)*m,g,k;if(p.containsNode(e,c.node)){if(d(c),g=Math.ceil(c.steps/m)*m,k=h[g],!k||c.steps>k.steps)h[g]=c}else delete a[b(c.node)];f[l]===c&&delete f[l]}); +Object.keys(h).forEach(function(a){f[a]=h[a]})};l=new function(a,c){this.steps=a;this.node=c;this.setIteratorPosition=function(a){a.setUnfilteredPosition(c,0);do if(g.acceptPosition(a)===u)break;while(a.nextPosition())}}(0,e)}var k=0;ops.StepsTranslator=function(e,k,m,q){function b(){var c=e();c!==r&&(runtime.log("Undo detected. Resetting steps cache"),r=c,d=new g(r,m,q),a=k(r))}function h(a,b){if(!b||m.acceptPosition(a)===c)return!0;for(;a.previousPosition();)if(m.acceptPosition(a)===c){if(b(0,a.container(), +a.unfilteredDomOffset()))return!0;break}for(;a.nextPosition();)if(m.acceptPosition(a)===c){if(b(1,a.container(),a.unfilteredDomOffset()))return!0;break}return!1}var r=e(),d=new g(r,m,q),f=new core.DomUtils,a=k(e()),c=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.convertStepsToDomPoint=function(f){var e,h;0>f&&(runtime.log("warn","Requested steps were negative ("+f+")"),f=0);b();for(e=d.setToClosestStep(f,a);ef.comparePoints(r,0,e,l),e=r,l=l?0:r.childNodes.length);a.setUnfilteredPosition(e,l);h(a,g)||a.setUnfilteredPosition(e,l);g=a.container();l=a.unfilteredDomOffset();e=d.setToClosestDomPoint(g,l,a);if(0>f.comparePoints(a.container(),a.unfilteredDomOffset(), +g,l))return 0c.steps&&(c.steps=0)})}};ops.StepsTranslator.PREVIOUS_STEP=0;ops.StepsTranslator.NEXT_STEP=1;return ops.StepsTranslator})(); +// Input 49 +xmldom.RNG={}; +xmldom.RelaxNGParser=function(){function g(b,f){this.message=function(){f&&(b+=1===f.nodeType?" Element ":" Node ",b+=f.nodeName,f.nodeValue&&(b+=" with value '"+f.nodeValue+"'"),b+=".");return b}}function k(b){if(2>=b.e.length)return b;var f={name:b.name,e:b.e.slice(0,2)};return k({name:b.name,e:[f].concat(b.e.slice(2))})}function e(b){b=b.split(":",2);var f="",a;1===b.length?b=["",b[0]]:f=b[0];for(a in h)h[a]===f&&(b[0]=a);return b}function n(b,f){for(var a=0,c,h,l=b.name;b.e&&a=b&&(d=-q.movePointBackward(-b,g));e.handleUpdate();return d};this.handleUpdate=function(){};this.getStepCounter=function(){return q.getStepCounter()};this.getMemberId=function(){return g};this.getNode=function(){return b.getNode()};this.getAnchorNode=function(){return b.getAnchorNode()};this.getSelectedRange=function(){return b.getSelectedRange()}; +this.setSelectedRange=function(h,g){b.setSelectedRange(h,g);e.handleUpdate()};this.hasForwardSelection=function(){return b.hasForwardSelection()};this.getOdtDocument=function(){return k};this.getSelectionType=function(){return m};this.setSelectionType=function(b){n.hasOwnProperty(b)?m=b:runtime.log("Invalid selection type: "+b)};this.resetSelectionType=function(){e.setSelectionType(ops.OdtCursor.RangeSelection)};b=new core.Cursor(k.getDOM(),g);q=new gui.SelectionMover(b,k.getRootNode());n[ops.OdtCursor.RangeSelection]= +!0;n[ops.OdtCursor.RegionSelection]=!0;e.resetSelectionType()};ops.OdtCursor.RangeSelection="Range";ops.OdtCursor.RegionSelection="Region";(function(){return ops.OdtCursor})(); // Input 51 /* @@ -1467,13 +1369,27 @@ ops.OpRemoveBlob=function(){var g,l,c;this.init=function(m){g=m.memberid;l=m.tim @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpInsertImage=function(){var g,l,c,m,f,n,b,p,r=odf.Namespaces.drawns,d=odf.Namespaces.svgns,k=odf.Namespaces.textns,a=odf.Namespaces.xlinkns;this.init=function(a){g=a.memberid;l=a.timestamp;c=a.position;m=a.filename;f=a.frameWidth;n=a.frameHeight;b=a.frameStyleName;p=a.frameName};this.isEdit=!0;this.execute=function(e){var h=e.getOdfCanvas(),q=e.getTextNodeAtStep(c,g),u,x;if(!q)return!1;u=q.textNode;x=e.getParagraphElement(u);var q=q.offset!==u.length?u.splitText(q.offset):u.nextSibling,v=e.getDOM(), -y=v.createElementNS(r,"draw:image"),v=v.createElementNS(r,"draw:frame");y.setAttributeNS(a,"xlink:href",m);y.setAttributeNS(a,"xlink:type","simple");y.setAttributeNS(a,"xlink:show","embed");y.setAttributeNS(a,"xlink:actuate","onLoad");v.setAttributeNS(r,"draw:style-name",b);v.setAttributeNS(r,"draw:name",p);v.setAttributeNS(k,"text:anchor-type","as-char");v.setAttributeNS(d,"svg:width",f);v.setAttributeNS(d,"svg:height",n);v.appendChild(y);u.parentNode.insertBefore(v,q);e.emit(ops.OdtDocument.signalStepsInserted, -{position:c,length:1});0===u.length&&u.parentNode.removeChild(u);h.addCssForFrameWithImage(v);h.refreshCSS();e.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:x,memberId:g,timeStamp:l});h.rerenderAnnotations();return!0};this.spec=function(){return{optype:"InsertImage",memberid:g,timestamp:l,filename:m,position:c,frameWidth:f,frameHeight:n,frameStyleName:b,frameName:p}}}; +runtime.loadClass("core.EventNotifier");runtime.loadClass("core.DomUtils");runtime.loadClass("odf.OdfUtils");runtime.loadClass("odf.Namespaces");runtime.loadClass("gui.SelectionMover");runtime.loadClass("core.PositionFilterChain");runtime.loadClass("ops.StepsTranslator");runtime.loadClass("ops.TextPositionFilter");runtime.loadClass("ops.Member"); +ops.OdtDocument=function(g){function k(){var a=g.odfContainer().getContentElement(),c=a&&a.localName;runtime.assert("text"===c,"Unsupported content element type '"+c+"' for OdtDocument");return a}function e(){return k().ownerDocument}function n(a){for(;a&&!(a.namespaceURI===odf.Namespaces.officens&&"text"===a.localName||a.namespaceURI===odf.Namespaces.officens&&"annotation"===a.localName);)a=a.parentNode;return a}function m(c){this.acceptPosition=function(b){b=b.container();var d;d="string"===typeof c? +a[c].getNode():c;return n(b)===n(d)?l:u}}function q(a){var c=gui.SelectionMover.createPositionIterator(k());a=w.convertStepsToDomPoint(a);c.setUnfilteredPosition(a.node,a.offset);return c}function b(a,c){return g.getFormatting().getStyleElement(a,c)}function h(a){return b(a,"paragraph")}var r=this,d,f,a={},c={},p=new core.EventNotifier([ops.OdtDocument.signalMemberAdded,ops.OdtDocument.signalMemberUpdated,ops.OdtDocument.signalMemberRemoved,ops.OdtDocument.signalCursorAdded,ops.OdtDocument.signalCursorRemoved, +ops.OdtDocument.signalCursorMoved,ops.OdtDocument.signalParagraphChanged,ops.OdtDocument.signalParagraphStyleModified,ops.OdtDocument.signalCommonStyleCreated,ops.OdtDocument.signalCommonStyleDeleted,ops.OdtDocument.signalTableAdded,ops.OdtDocument.signalOperationExecuted,ops.OdtDocument.signalUndoStackChanged,ops.OdtDocument.signalStepsInserted,ops.OdtDocument.signalStepsRemoved]),l=core.PositionFilter.FilterResult.FILTER_ACCEPT,u=core.PositionFilter.FilterResult.FILTER_REJECT,B,w,y;this.getDOM= +e;this.getRootElement=n;this.getIteratorAtPosition=q;this.convertDomPointToCursorStep=function(a,c,b){return w.convertDomPointToSteps(a,c,b)};this.convertDomToCursorRange=function(a,c){var b,d;b=c(a.anchorNode,a.anchorOffset);b=w.convertDomPointToSteps(a.anchorNode,a.anchorOffset,b);c||a.anchorNode!==a.focusNode||a.anchorOffset!==a.focusOffset?(d=c(a.focusNode,a.focusOffset),d=w.convertDomPointToSteps(a.focusNode,a.focusOffset,d)):d=b;return{position:b,length:d-b}};this.convertCursorToDomRange=function(a, +c){var b=e().createRange(),d,f;d=w.convertStepsToDomPoint(a);c?(f=w.convertStepsToDomPoint(a+c),0=f;f+=1){c=a.container();b=a.unfilteredDomOffset();if(c.nodeType===Node.TEXT_NODE&&" "===c.data[b]&&d.isSignificantWhitespace(c, +b)){runtime.assert(" "===c.data[b],"upgradeWhitespaceToElement: textNode.data[offset] should be a literal space");var e=c.ownerDocument.createElementNS(odf.Namespaces.textns,"text:s");e.appendChild(c.ownerDocument.createTextNode(" "));c.deleteData(b,1);0 + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -1508,15 +1424,54 @@ y=v.createElementNS(r,"draw:image"),v=v.createElementNS(r,"draw:frame");y.setAtt @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpInsertTable=function(){function g(b,a){var c;if(1===d.length)c=d[0];else if(3===d.length)switch(b){case 0:c=d[0];break;case m-1:c=d[2];break;default:c=d[1]}else c=d[b];if(1===c.length)return c[0];if(3===c.length)switch(a){case 0:return c[0];case f-1:return c[2];default:return c[1]}return c[a]}var l,c,m,f,n,b,p,r,d;this.init=function(k){l=k.memberid;c=k.timestamp;n=k.position;m=k.initialRows;f=k.initialColumns;b=k.tableName;p=k.tableStyleName;r=k.tableColumnStyleName;d=k.tableCellStyleMatrix}; -this.isEdit=!0;this.execute=function(d){var a=d.getTextNodeAtStep(n),e=d.getRootNode();if(a){var h=d.getDOM(),q=h.createElementNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:table"),u=h.createElementNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:table-column"),x,v,y,w;p&&q.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:style-name",p);b&&q.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:name",b);u.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0", -"table:number-columns-repeated",f);r&&u.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:style-name",r);q.appendChild(u);for(y=0;y=b.length)return c;0===f&&(f=0);for(var e=b.item(f);e.namespaceURI===d;){f+=1;if(f>=b.length)return c;e=b.item(f)}return e=h(a,c.attDeriv(a,b.item(f)),b,f+1)}function r(a,c,b){b.e[0].a?(a.push(b.e[0].text),c.push(b.e[0].a.ns)):r(a,c,b.e[0]); +b.e[1].a?(a.push(b.e[1].text),c.push(b.e[1].a.ns)):r(a,c,b.e[1])}var d="http://www.w3.org/2000/xmlns/",f,a,c,p,l,u,B,w,y,v,t,s,N,z={type:"notAllowed",nullable:!1,hash:"notAllowed",nc:void 0,p:void 0,p1:void 0,p2:void 0,textDeriv:function(){return z},startTagOpenDeriv:function(){return z},attDeriv:function(){return z},startTagCloseDeriv:function(){return z},endTagDeriv:function(){return z}},x={type:"empty",nullable:!0,hash:"empty",nc:void 0,p:void 0,p1:void 0,p2:void 0,textDeriv:function(){return z}, +startTagOpenDeriv:function(){return z},attDeriv:function(){return z},startTagCloseDeriv:function(){return x},endTagDeriv:function(){return z}},R={type:"text",nullable:!0,hash:"text",nc:void 0,p:void 0,p1:void 0,p2:void 0,textDeriv:function(){return R},startTagOpenDeriv:function(){return z},attDeriv:function(){return z},startTagCloseDeriv:function(){return R},endTagDeriv:function(){return z}};f=n("choice",function(a,c){if(a===z)return c;if(c===z||a===c)return a},function(a,c){var b={},d;m(b,{p1:a, +p2:c});c=a=void 0;for(d in b)b.hasOwnProperty(d)&&(void 0===a?a=b[d]:c=void 0===c?b[d]:f(c,b[d]));return function(a,c){return{type:"choice",nullable:a.nullable||c.nullable,hash:void 0,nc:void 0,p:void 0,p1:a,p2:c,textDeriv:function(b,d){return f(a.textDeriv(b,d),c.textDeriv(b,d))},startTagOpenDeriv:e(function(b){return f(a.startTagOpenDeriv(b),c.startTagOpenDeriv(b))}),attDeriv:function(b,d){return f(a.attDeriv(b,d),c.attDeriv(b,d))},startTagCloseDeriv:g(function(){return f(a.startTagCloseDeriv(), +c.startTagCloseDeriv())}),endTagDeriv:g(function(){return f(a.endTagDeriv(),c.endTagDeriv())})}}(a,c)});a=function(a,c,b){return function(){var d={},f=0;return function(e,h){var g=c&&c(e,h),l,k;if(void 0!==g)return g;l=e.hash||e.toString();g=h.hash||h.toString();lNode.ELEMENT_NODE;){if(d!==Node.COMMENT_NODE&&(d!==Node.TEXT_NODE||!/^\s+$/.test(e.currentNode.nodeValue)))return[new g("Not allowed node of type "+ +d+".")];d=(m=e.nextSibling())?m.nodeType:0}if(!m)return[new g("Missing element "+b.names)];if(b.names&&-1===b.names.indexOf(q[m.namespaceURI]+":"+m.localName))return[new g("Found "+m.nodeName+" instead of "+b.names+".",m)];if(e.firstChild()){for(f=k(b.e[1],e,m);e.nextSibling();)if(d=e.currentNode.nodeType,!(e.currentNode&&e.currentNode.nodeType===Node.TEXT_NODE&&/^\s+$/.test(e.currentNode.nodeValue)||d===Node.COMMENT_NODE))return[new g("Spurious content.",e.currentNode)];if(e.parentNode()!==m)return[new g("Implementation error.")]}else f= +k(b.e[1],e,m);e.nextSibling();return f}var n,m,q;m=function(b,h,n,d){var f=b.name,a=null;if("text"===f)a:{for(var c=(b=h.currentNode)?b.nodeType:0;b!==n&&3!==c;){if(1===c){a=[new g("Element not allowed here.",b)];break a}c=(b=h.nextSibling())?b.nodeType:0}h.nextSibling();a=null}else if("data"===f)a=null;else if("value"===f)d!==b.text&&(a=[new g("Wrong value, should be '"+b.text+"', not '"+d+"'",n)]);else if("list"===f)a=null;else if("attribute"===f)a:{if(2!==b.e.length)throw"Attribute with wrong # of elements: "+ +b.e.length;f=b.localnames.length;for(a=0;am(c,e))&&(e=f));a=e;c=g.getOdtDocument().getOdfCanvas().getZoomLevel();d&&g.getSelectionType()===ops.OdtCursor.RangeSelection? +b.style.visibility="visible":b.style.visibility="hidden";a?(b.style.top="0",e=p.getBoundingClientRect(b),8>a.height&&(a={top:a.top-(8-a.height)/2,height:8}),b.style.height=p.adaptRangeDifferenceToZoomLevel(a.height,c)+"px",b.style.top=p.adaptRangeDifferenceToZoomLevel(a.top-e.top,c)+"px"):(b.style.height="1em",b.style.top="5%")}var b,h,r,d=!0,f=!1,a=!1,c,p=new core.DomUtils;this.handleUpdate=q;this.refreshCursorBlinking=function(){e||g.getSelectedRange().collapsed?(f=!0,n(!0)):(f=!1,b.style.opacity= +"0")};this.setFocus=function(){f=!0;h.markAsFocussed(!0);n(!0)};this.removeFocus=function(){f=!1;h.markAsFocussed(!1);b.style.opacity="1"};this.show=function(){d=!0;q();h.markAsFocussed(!0)};this.hide=function(){d=!1;q();h.markAsFocussed(!1)};this.setAvatarImageUrl=function(a){h.setImageUrl(a)};this.setColor=function(a){b.style.borderColor=a;h.setColor(a)};this.getCursor=function(){return g};this.getFocusElement=function(){return b};this.toggleHandleVisibility=function(){h.isVisible()?h.hide():h.show()}; +this.showHandle=function(){h.show()};this.hideHandle=function(){h.hide()};this.ensureVisible=function(){var a,c,d,f,e=g.getOdtDocument().getOdfCanvas().getElement().parentNode,h;d=e.offsetWidth-e.clientWidth+5;f=e.offsetHeight-e.clientHeight+5;h=b.getBoundingClientRect();a=h.left-d;c=h.top-f;d=h.right+d;f=h.bottom+f;h=e.getBoundingClientRect();ch.bottom&&(e.scrollTop+=f-h.bottom);ah.right&&(e.scrollLeft+=d-h.right);q()};this.destroy=function(a){h.destroy(function(c){c? +a(c):(r.removeChild(b),a())})};(function(){var a=g.getOdtDocument().getDOM();b=a.createElementNS(a.documentElement.namespaceURI,"span");b.style.top="5%";r=g.getNode();r.appendChild(b);h=new gui.Avatar(r,k);q()})()}; +// Input 56 +gui.EventManager=function(g){function k(){var b=this,a=[];this.handlers=[];this.isSubscribed=!1;this.handleEvent=function(c){-1===a.indexOf(c)&&(a.push(c),b.handlers.forEach(function(a){a(c)}),runtime.setTimeout(function(){a.splice(a.indexOf(c),1)},0))}}function e(b){var a=b.scrollX,c=b.scrollY;this.restore=function(){b.scrollX===a&&b.scrollY===c||b.scrollTo(a,c)}}function n(b){var a=b.scrollTop,c=b.scrollLeft;this.restore=function(){if(b.scrollTop!==a||b.scrollLeft!==c)b.scrollTop=a,b.scrollLeft= +c}}function m(b,a,c){var d="on"+a,e=!1;b.attachEvent&&(e=b.attachEvent(d,c));!e&&b.addEventListener&&(b.addEventListener(a,c,!1),e=!0);e&&!r[a]||!b.hasOwnProperty(d)||(b[d]=c)}function q(){return g.getDOM().activeElement===b}var b=g.getOdfCanvas().getElement(),h=runtime.getWindow(),r={beforecut:!0,beforepaste:!0},d;this.subscribe=function(f,a){var c=h&&d[f];c?(c.handlers.push(a),c.isSubscribed||(c.isSubscribed=!0,m(h,f,c.handleEvent),m(b,f,c.handleEvent))):m(b,f,a)};this.unsubscribe=function(f,a){var c= +h&&d[f],e=c&&c.handlers.indexOf(a);c?-1!==e&&c.handlers.splice(e,1):(c=b,e="on"+f,c.detachEvent&&c.detachEvent(e,a),c.removeEventListener&&c.removeEventListener(f,a,!1),c[e]===a&&(c[e]=null))};this.hasFocus=q;this.focus=function(){var d;if(!q()){for(d=b;d&&!d.scrollTop&&!d.scrollLeft;)d=d.parentNode;d=d?new n(d):h?new e(h):null;b.focus();d&&d.restore()}};d={mousedown:new k,mouseup:new k}}; +// Input 57 +runtime.loadClass("gui.SelectionMover");gui.ShadowCursor=function(g){var k=g.getDOM().createRange(),e=!0;this.removeFromOdtDocument=function(){};this.getMemberId=function(){return gui.ShadowCursor.ShadowCursorMemberId};this.getSelectedRange=function(){return k};this.setSelectedRange=function(g,m){k=g;e=!1!==m};this.hasForwardSelection=function(){return e};this.getOdtDocument=function(){return g};this.getSelectionType=function(){return ops.OdtCursor.RangeSelection};k.setStart(g.getRootNode(),0)}; +gui.ShadowCursor.ShadowCursorMemberId="";(function(){return gui.ShadowCursor})(); +// Input 58 /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -1551,13 +1506,12 @@ tableColumnStyleName:r,tableCellStyleMatrix:d}}}; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpInsertText=function(){var g,l,c,m;this.init=function(f){g=f.memberid;l=f.timestamp;c=f.position;m=f.text};this.isEdit=!0;this.execute=function(f){var n,b,p,r=null,d=f.getDOM(),k,a=0,e,h;f.upgradeWhitespacesAtPosition(c);if(n=f.getTextNodeAtStep(c,g)){b=n.textNode;r=b.nextSibling;p=b.parentNode;k=f.getParagraphElement(b);for(h=0;h + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -1592,16 +1546,12 @@ length:m.length});0 + Copyright (C) 2012 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -1636,10 +1586,9 @@ q.mergeChildrenIntoParent(h);return d});p.emit(ops.OdtDocument.signalStepsRemove @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpSplitParagraph=function(){var g,l,c,m;this.init=function(f){g=f.memberid;l=f.timestamp;c=f.position;m=new odf.OdfUtils};this.isEdit=!0;this.execute=function(f){var n,b,p,r,d,k,a;f.upgradeWhitespacesAtPosition(c);n=f.getTextNodeAtStep(c,g);if(!n)return!1;b=f.getParagraphElement(n.textNode);if(!b)return!1;p=m.isListItem(b.parentNode)?b.parentNode:b;0===n.offset?(a=n.textNode.previousSibling,k=null):(a=n.textNode,k=n.offset>=n.textNode.length?null:n.textNode.splitText(n.offset));for(r=n.textNode;r!== -p;){r=r.parentNode;d=r.cloneNode(!1);k&&d.appendChild(k);if(a)for(;a&&a.nextSibling;)d.appendChild(a.nextSibling);else for(;r.firstChild;)d.appendChild(r.firstChild);r.parentNode.insertBefore(d,r.nextSibling);a=r;k=d}m.isListItem(k)&&(k=k.childNodes[0]);0===n.textNode.length&&n.textNode.parentNode.removeChild(n.textNode);f.emit(ops.OdtDocument.signalStepsInserted,{position:c,length:1});f.fixCursorPositions();f.getOdfCanvas().refreshSize();f.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:b, -memberId:g,timeStamp:l});f.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:k,memberId:g,timeStamp:l});f.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"SplitParagraph",memberid:g,timestamp:l,position:c}}}; -// Input 56 +ops.EditInfo=function(g,k){function e(){var e=[],b;for(b in m)m.hasOwnProperty(b)&&e.push({memberid:b,time:m[b].time});e.sort(function(b,e){return b.time-e.time});return e}var n,m={};this.getNode=function(){return n};this.getOdtDocument=function(){return k};this.getEdits=function(){return m};this.getSortedEdits=function(){return e()};this.addEdit=function(e,b){m[e]={time:b}};this.clearEdits=function(){m={}};this.destroy=function(e){g.parentNode&&g.removeChild(n);e()};n=k.getDOM().createElementNS("urn:webodf:names:editinfo", +"editinfo");g.insertBefore(n,g.firstChild)}; +// Input 61 /* Copyright (C) 2012-2013 KO GmbH @@ -1677,9 +1626,12 @@ memberId:g,timeStamp:l});f.emit(ops.OdtDocument.signalParagraphChanged,{paragrap @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpSetParagraphStyle=function(){var g,l,c,m;this.init=function(f){g=f.memberid;l=f.timestamp;c=f.position;m=f.styleName};this.isEdit=!0;this.execute=function(f){var n;n=f.getIteratorAtPosition(c);return(n=f.getParagraphElement(n.container()))?(""!==m?n.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0","text:style-name",m):n.removeAttributeNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0","style-name"),f.getOdfCanvas().refreshSize(),f.emit(ops.OdtDocument.signalParagraphChanged, -{paragraphElement:n,timeStamp:l,memberId:g}),f.getOdfCanvas().rerenderAnnotations(),!0):!1};this.spec=function(){return{optype:"SetParagraphStyle",memberid:g,timestamp:l,position:c,styleName:m}}}; -// Input 57 +runtime.loadClass("core.DomUtils"); +ops.OpAddAnnotation=function(){function g(b,e,d){var f=b.getTextNodeAtStep(d,k);f&&(b=f.textNode,d=b.parentNode,f.offset!==b.length&&b.splitText(f.offset),d.insertBefore(e,b.nextSibling),0===b.length&&d.removeChild(b))}var k,e,n,m,q,b;this.init=function(b){k=b.memberid;e=parseInt(b.timestamp,10);n=parseInt(b.position,10);m=parseInt(b.length,10)||0;q=b.name};this.isEdit=!0;this.execute=function(h){var r={},d=h.getCursor(k),f,a;a=new core.DomUtils;b=h.getDOM();var c=new Date(e),p,l,u,B;f=b.createElementNS(odf.Namespaces.officens, +"office:annotation");f.setAttributeNS(odf.Namespaces.officens,"office:name",q);p=b.createElementNS(odf.Namespaces.dcns,"dc:creator");p.setAttributeNS("urn:webodf:names:editinfo","editinfo:memberid",k);p.textContent=h.getMember(k).getProperties().fullName;l=b.createElementNS(odf.Namespaces.dcns,"dc:date");l.appendChild(b.createTextNode(c.toISOString()));c=b.createElementNS(odf.Namespaces.textns,"text:list");u=b.createElementNS(odf.Namespaces.textns,"text:list-item");B=b.createElementNS(odf.Namespaces.textns, +"text:p");u.appendChild(B);c.appendChild(u);f.appendChild(p);f.appendChild(l);f.appendChild(c);r.node=f;if(!r.node)return!1;if(m){f=b.createElementNS(odf.Namespaces.officens,"office:annotation-end");f.setAttributeNS(odf.Namespaces.officens,"office:name",q);r.end=f;if(!r.end)return!1;g(h,r.end,n+m)}g(h,r.node,n);h.emit(ops.OdtDocument.signalStepsInserted,{position:n,length:m});d&&(f=b.createRange(),a=a.getElementsByTagNameNS(r.node,odf.Namespaces.textns,"p")[0],f.selectNodeContents(a),d.setSelectedRange(f), +h.emit(ops.OdtDocument.signalCursorMoved,d));h.getOdfCanvas().addAnnotation(r);h.fixCursorPositions();return!0};this.spec=function(){return{optype:"AddAnnotation",memberid:k,timestamp:e,position:n,length:m,name:q}}}; +// Input 62 /* Copyright (C) 2012-2013 KO GmbH @@ -1717,11 +1669,34 @@ ops.OpSetParagraphStyle=function(){var g,l,c,m;this.init=function(f){g=f.memberi @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.Namespaces"); -ops.OpUpdateParagraphStyle=function(){function g(b,c){var d,f,a=c?c.split(","):[];for(d=0;d + + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("ops.Member");ops.OpAddMember=function(){var g,k,e;this.init=function(n){g=n.memberid;k=parseInt(n.timestamp,10);e=n.setProperties};this.isEdit=!1;this.execute=function(k){if(k.getMember(g))return!1;var m=new ops.Member(g,e);k.addMember(m);k.emit(ops.OdtDocument.signalMemberAdded,m);return!0};this.spec=function(){return{optype:"AddMember",memberid:g,timestamp:k,setProperties:e}}}; +// Input 64 /* Copyright (C) 2012-2013 KO GmbH @@ -1760,9 +1735,9 @@ function(){return{optype:"UpdateParagraphStyle",memberid:l,timestamp:c,styleName @source: https://github.com/kogmbh/WebODF/ */ runtime.loadClass("odf.Namespaces"); -ops.OpAddStyle=function(){var g,l,c,m,f,n,b=odf.Namespaces.stylens;this.init=function(b){g=b.memberid;l=b.timestamp;c=b.styleName;m=b.styleFamily;f="true"===b.isAutomaticStyle||!0===b.isAutomaticStyle;n=b.setProperties};this.isEdit=!0;this.execute=function(g){var l=g.getOdfCanvas().odfContainer(),d=g.getFormatting(),k=g.getDOM().createElementNS(b,"style:style");if(!k)return!1;n&&d.updateStyle(k,n);k.setAttributeNS(b,"style:family",m);k.setAttributeNS(b,"style:name",c);f?l.rootElement.automaticStyles.appendChild(k): -l.rootElement.styles.appendChild(k);g.getOdfCanvas().refreshCSS();f||g.emit(ops.OdtDocument.signalCommonStyleCreated,{name:c,family:m});return!0};this.spec=function(){return{optype:"AddStyle",memberid:g,timestamp:l,styleName:c,styleFamily:m,isAutomaticStyle:f,setProperties:n}}}; -// Input 59 +ops.OpAddStyle=function(){var g,k,e,n,m,q,b=odf.Namespaces.stylens;this.init=function(b){g=b.memberid;k=b.timestamp;e=b.styleName;n=b.styleFamily;m="true"===b.isAutomaticStyle||!0===b.isAutomaticStyle;q=b.setProperties};this.isEdit=!0;this.execute=function(g){var k=g.getOdfCanvas().odfContainer(),d=g.getFormatting(),f=g.getDOM().createElementNS(b,"style:style");if(!f)return!1;q&&d.updateStyle(f,q);f.setAttributeNS(b,"style:family",n);f.setAttributeNS(b,"style:name",e);m?k.rootElement.automaticStyles.appendChild(f): +k.rootElement.styles.appendChild(f);g.getOdfCanvas().refreshCSS();m||g.emit(ops.OdtDocument.signalCommonStyleCreated,{name:e,family:n});return!0};this.spec=function(){return{optype:"AddStyle",memberid:g,timestamp:k,styleName:e,styleFamily:n,isAutomaticStyle:m,setProperties:q}}}; +// Input 65 /* Copyright (C) 2012-2013 KO GmbH @@ -1800,8 +1775,10 @@ l.rootElement.styles.appendChild(k);g.getOdfCanvas().refreshCSS();f||g.emit(ops. @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpRemoveStyle=function(){var g,l,c,m;this.init=function(f){g=f.memberid;l=f.timestamp;c=f.styleName;m=f.styleFamily};this.isEdit=!0;this.execute=function(f){var g=f.getStyleElement(c,m);if(!g)return!1;g.parentNode.removeChild(g);f.getOdfCanvas().refreshCSS();f.emit(ops.OdtDocument.signalCommonStyleDeleted,{name:c,family:m});return!0};this.spec=function(){return{optype:"RemoveStyle",memberid:g,timestamp:l,styleName:c,styleFamily:m}}}; -// Input 60 +runtime.loadClass("gui.StyleHelper");runtime.loadClass("odf.OdfUtils"); +ops.OpApplyDirectStyling=function(){function g(b){var e=0<=m?n+m:n,d=b.getIteratorAtPosition(0<=m?n:n+m),e=m?b.getIteratorAtPosition(e):d;b=b.getDOM().createRange();b.setStart(d.container(),d.unfilteredDomOffset());b.setEnd(e.container(),e.unfilteredDomOffset());return b}var k,e,n,m,q,b=new odf.OdfUtils;this.init=function(b){k=b.memberid;e=b.timestamp;n=parseInt(b.position,10);m=parseInt(b.length,10);q=b.setProperties};this.isEdit=!0;this.execute=function(h){var m=g(h),d=b.getImpactedParagraphs(m); +(new gui.StyleHelper(h.getFormatting())).applyStyle(k,m,q);m.detach();h.getOdfCanvas().refreshCSS();h.fixCursorPositions();d.forEach(function(b){h.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:b,memberId:k,timeStamp:e})});h.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"ApplyDirectStyling",memberid:k,timestamp:e,position:n,length:m,setProperties:q}}}; +// Input 66 /* Copyright (C) 2012-2013 KO GmbH @@ -1839,14 +1816,13 @@ ops.OpRemoveStyle=function(){var g,l,c,m;this.init=function(f){g=f.memberid;l=f. @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpAddAnnotation=function(){function g(b,c,f){var d=b.getTextNodeAtStep(f,l);d&&(b=d.textNode,f=b.parentNode,d.offset!==b.length&&b.splitText(d.offset),f.insertBefore(c,b.nextSibling),0===b.length&&f.removeChild(b))}var l,c,m,f,n;this.init=function(b){l=b.memberid;c=parseInt(b.timestamp,10);m=parseInt(b.position,10);f=parseInt(b.length,10)||0;n=b.name};this.isEdit=!0;this.execute=function(b){var p={},r=b.getPositionFilter(),d=b.getCursor(l),k=b.getCursorPosition(l),k=m-k-1,a=new Date(c),e,h,q, -u,x;x=b.getDOM();e=x.createElementNS(odf.Namespaces.officens,"office:annotation");e.setAttributeNS(odf.Namespaces.officens,"office:name",n);h=x.createElementNS(odf.Namespaces.dcns,"dc:creator");h.setAttributeNS("urn:webodf:names:editinfo","editinfo:memberid",l);h.textContent=b.getMember(l).getProperties().fullName;q=x.createElementNS(odf.Namespaces.dcns,"dc:date");q.appendChild(x.createTextNode(a.toISOString()));a=x.createElementNS(odf.Namespaces.textns,"text:list");u=x.createElementNS(odf.Namespaces.textns, -"text:list-item");x=x.createElementNS(odf.Namespaces.textns,"text:p");u.appendChild(x);a.appendChild(u);e.appendChild(h);e.appendChild(q);e.appendChild(a);p.node=e;if(!p.node)return!1;if(f){e=b.getDOM().createElementNS(odf.Namespaces.officens,"office:annotation-end");e.setAttributeNS(odf.Namespaces.officens,"office:name",n);p.end=e;if(!p.end)return!1;g(b,p.end,m+f)}g(b,p.node,m);b.emit(ops.OdtDocument.signalStepsInserted,{position:m,length:f});d&&(r=d.getStepCounter().countSteps(k,r),d.move(r),d.resetSelectionType(), -b.emit(ops.OdtDocument.signalCursorMoved,d));b.getOdfCanvas().addAnnotation(p);b.fixCursorPositions();return!0};this.spec=function(){return{optype:"AddAnnotation",memberid:l,timestamp:c,position:m,length:f,name:n}}}; -// Input 61 +ops.OpInsertImage=function(){var g,k,e,n,m,q,b,h,r=odf.Namespaces.drawns,d=odf.Namespaces.svgns,f=odf.Namespaces.textns,a=odf.Namespaces.xlinkns;this.init=function(a){g=a.memberid;k=a.timestamp;e=a.position;n=a.filename;m=a.frameWidth;q=a.frameHeight;b=a.frameStyleName;h=a.frameName};this.isEdit=!0;this.execute=function(c){var p=c.getOdfCanvas(),l=c.getTextNodeAtStep(e,g),u,B;if(!l)return!1;u=l.textNode;B=c.getParagraphElement(u);var l=l.offset!==u.length?u.splitText(l.offset):u.nextSibling,w=c.getDOM(), +y=w.createElementNS(r,"draw:image"),w=w.createElementNS(r,"draw:frame");y.setAttributeNS(a,"xlink:href",n);y.setAttributeNS(a,"xlink:type","simple");y.setAttributeNS(a,"xlink:show","embed");y.setAttributeNS(a,"xlink:actuate","onLoad");w.setAttributeNS(r,"draw:style-name",b);w.setAttributeNS(r,"draw:name",h);w.setAttributeNS(f,"text:anchor-type","as-char");w.setAttributeNS(d,"svg:width",m);w.setAttributeNS(d,"svg:height",q);w.appendChild(y);u.parentNode.insertBefore(w,l);c.emit(ops.OdtDocument.signalStepsInserted, +{position:e,length:1});0===u.length&&u.parentNode.removeChild(u);p.addCssForFrameWithImage(w);p.refreshCSS();c.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:B,memberId:g,timeStamp:k});p.rerenderAnnotations();return!0};this.spec=function(){return{optype:"InsertImage",memberid:g,timestamp:k,filename:n,position:e,frameWidth:m,frameHeight:q,frameStyleName:b,frameName:h}}}; +// Input 67 /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -1881,118 +1857,53 @@ b.emit(ops.OdtDocument.signalCursorMoved,d));b.getOdfCanvas().addAnnotation(p);b @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.Namespaces");runtime.loadClass("core.DomUtils"); -ops.OpRemoveAnnotation=function(){var g,l,c,m,f;this.init=function(n){g=n.memberid;l=n.timestamp;c=parseInt(n.position,10);m=parseInt(n.length,10);f=new core.DomUtils};this.isEdit=!0;this.execute=function(g){for(var b=g.getIteratorAtPosition(c).container(),l,r,d;b.namespaceURI!==odf.Namespaces.officens||"annotation"!==b.localName;)b=b.parentNode;if(null===b)return!1;(l=b.getAttributeNS(odf.Namespaces.officens,"name"))&&(r=f.getElementsByTagNameNS(g.getRootNode(),odf.Namespaces.officens,"annotation-end").filter(function(b){return l=== -b.getAttributeNS(odf.Namespaces.officens,"name")})[0]||null);g.getOdfCanvas().forgetAnnotations();for(d=f.getElementsByTagNameNS(b,"urn:webodf:names:cursor","cursor");d.length;)b.parentNode.insertBefore(d.pop(),b);b.parentNode.removeChild(b);r&&r.parentNode.removeChild(r);g.emit(ops.OdtDocument.signalStepsRemoved,{position:0 - - @licstart - This file is part of WebODF. - - WebODF 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. - - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend - - @source: http://www.webodf.org/ - @source: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("ops.Member");ops.OpAddMember=function(){var g,l,c;this.init=function(m){g=m.memberid;l=parseInt(m.timestamp,10);c=m.setProperties};this.isEdit=!1;this.execute=function(l){if(l.getMember(g))return!1;var f=new ops.Member(g,c);l.addMember(f);l.emit(ops.OdtDocument.signalMemberAdded,f);return!0};this.spec=function(){return{optype:"AddMember",memberid:g,timestamp:l,setProperties:c}}}; -// Input 63 -/* - - Copyright (C) 2013 KO GmbH - - @licstart - This file is part of WebODF. - - WebODF 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. - - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend - - @source: http://www.webodf.org/ - @source: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("ops.Member");runtime.loadClass("xmldom.XPath"); -ops.OpUpdateMember=function(){function g(){for(var b=new xmldom.XPath,c="//dc:creator[@editinfo:memberid='"+l+"']",b=b.getODFElementsWithXPath(n.getRootNode(),c,function(b){return"editinfo"===b?"urn:webodf:names:editinfo":odf.Namespaces.resolvePrefix(b)}),c=0;c + Copyright (C) 2012-2013 KO GmbH @licstart - This file is part of WebODF. - - WebODF 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. - - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. + 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. You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend - - @source: http://www.webodf.org/ - @source: https://github.com/kogmbh/WebODF/ -*/ -runtime.loadClass("ops.Member");ops.OpRemoveMember=function(){var g,l;this.init=function(c){g=c.memberid;l=parseInt(c.timestamp,10)};this.isEdit=!1;this.execute=function(c){if(!c.getMember(g))return!1;c.removeMember(g);c.emit(ops.OdtDocument.signalMemberRemoved,g);return!0};this.spec=function(){return{optype:"RemoveMember",memberid:g,timestamp:l}}}; -// Input 65 -/* - - Copyright (C) 2013 KO GmbH - - @licstart - This file is part of WebODF. + along with this code. If not, see . - WebODF 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. + 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. - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. + 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. - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . + This license applies to this entire compilation. @licend - @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpUpdateMetadata=function(){var g,l,c,m;this.init=function(f){g=f.memberid;l=parseInt(f.timestamp,10);c=f.setProperties;m=f.removedProperties};this.isEdit=!0;this.execute=function(f){f=f.getOdfCanvas().odfContainer().getMetadataManager();var g=[],b=["dc:date","dc:creator","meta:editing-cycles"];c&&b.forEach(function(b){if(c[b])return!1});m&&(b.forEach(function(b){if(-1!==g.indexOf(b))return!1}),g=m.attributes.split(","));f.setMetadata(c,g);return!0};this.spec=function(){return{optype:"UpdateMetadata", -memberid:g,timestamp:l,setProperties:c,removedProperties:m}}}; -// Input 66 +ops.OpInsertText=function(){var g,k,e,n;this.init=function(m){g=m.memberid;k=m.timestamp;e=m.position;n=m.text};this.isEdit=!0;this.execute=function(m){var q,b,h,r=null,d=m.getDOM(),f,a=0,c,p;m.upgradeWhitespacesAtPosition(e);if(q=m.getTextNodeAtStep(e,g)){b=q.textNode;r=b.nextSibling;h=b.parentNode;f=m.getParagraphElement(b);for(p=0;p @@ -2030,11 +1941,8 @@ memberid:g,timestamp:l,setProperties:c,removedProperties:m}}}; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("ops.OpAddMember");runtime.loadClass("ops.OpUpdateMember");runtime.loadClass("ops.OpRemoveMember");runtime.loadClass("ops.OpAddCursor");runtime.loadClass("ops.OpApplyDirectStyling");runtime.loadClass("ops.OpRemoveCursor");runtime.loadClass("ops.OpMoveCursor");runtime.loadClass("ops.OpSetBlob");runtime.loadClass("ops.OpRemoveBlob");runtime.loadClass("ops.OpInsertImage");runtime.loadClass("ops.OpInsertTable");runtime.loadClass("ops.OpInsertText");runtime.loadClass("ops.OpRemoveText"); -runtime.loadClass("ops.OpSplitParagraph");runtime.loadClass("ops.OpSetParagraphStyle");runtime.loadClass("ops.OpUpdateParagraphStyle");runtime.loadClass("ops.OpAddStyle");runtime.loadClass("ops.OpRemoveStyle");runtime.loadClass("ops.OpAddAnnotation");runtime.loadClass("ops.OpRemoveAnnotation");runtime.loadClass("ops.OpUpdateMetadata"); -ops.OperationFactory=function(){function g(c){return function(){return new c}}var l;this.register=function(c,g){l[c]=g};this.create=function(c){var g=null,f=l[c.optype];f&&(g=f(c),g.init(c));return g};l={AddMember:g(ops.OpAddMember),UpdateMember:g(ops.OpUpdateMember),RemoveMember:g(ops.OpRemoveMember),AddCursor:g(ops.OpAddCursor),ApplyDirectStyling:g(ops.OpApplyDirectStyling),SetBlob:g(ops.OpSetBlob),RemoveBlob:g(ops.OpRemoveBlob),InsertImage:g(ops.OpInsertImage),InsertTable:g(ops.OpInsertTable), -InsertText:g(ops.OpInsertText),RemoveText:g(ops.OpRemoveText),SplitParagraph:g(ops.OpSplitParagraph),SetParagraphStyle:g(ops.OpSetParagraphStyle),UpdateParagraphStyle:g(ops.OpUpdateParagraphStyle),AddStyle:g(ops.OpAddStyle),RemoveStyle:g(ops.OpRemoveStyle),MoveCursor:g(ops.OpMoveCursor),RemoveCursor:g(ops.OpRemoveCursor),AddAnnotation:g(ops.OpAddAnnotation),RemoveAnnotation:g(ops.OpRemoveAnnotation),UpdateMetadata:g(ops.OpUpdateMetadata)}}; -// Input 67 +ops.OpMoveCursor=function(){var g,k,e,n,m;this.init=function(q){g=q.memberid;k=q.timestamp;e=q.position;n=q.length||0;m=q.selectionType||ops.OdtCursor.RangeSelection};this.isEdit=!1;this.execute=function(k){var b=k.getCursor(g),h;if(!b)return!1;h=k.convertCursorToDomRange(e,n);b.setSelectedRange(h,0<=n);b.setSelectionType(m);k.emit(ops.OdtDocument.signalCursorMoved,b);return!0};this.spec=function(){return{optype:"MoveCursor",memberid:g,timestamp:k,position:e,length:n,selectionType:m}}}; +// Input 70 /* Copyright (C) 2012-2013 KO GmbH @@ -2072,19 +1980,11 @@ InsertText:g(ops.OpInsertText),RemoveText:g(ops.OpRemoveText),SplitParagraph:g(o @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.Cursor");runtime.loadClass("core.DomUtils");runtime.loadClass("core.PositionIterator");runtime.loadClass("core.PositionFilter");runtime.loadClass("core.LoopWatchDog");runtime.loadClass("odf.OdfUtils"); -gui.SelectionMover=function(g,l){function c(){v.setUnfilteredPosition(g.getNode(),0);return v}function m(a,b){var c,e=null;a&&(c=b?a[a.length-1]:a[0]);c&&(e={top:c.top,left:b?c.right:c.left,bottom:c.bottom});return e}function f(a,b,c,e){var d=a.nodeType;c.setStart(a,b);c.collapse(!e);e=m(c.getClientRects(),!0===e);!e&&0a?-1:1;for(a=Math.abs(a);0m?q.previousPosition():q.nextPosition());)if(ha.check(),g.acceptPosition(q)===t&&(p+=1,n=q.container(),v=f(n,q.unfilteredDomOffset(),I),v.top!==Y)){if(v.top!==G&&G!==Y)break;G=v.top;v=Math.abs(V-v.left);if(null===r||va?(e=g.previousPosition,d=-1):(e=g.nextPosition,d=1);for(h=f(g.container(),g.unfilteredDomOffset(),n);e.call(g);)if(b.acceptPosition(g)===t){if(u.getParagraphElement(g.getCurrentNode())!==m)break;k=f(g.container(),g.unfilteredDomOffset(),n);if(k.bottom!==h.bottom&&(h=k.top>=h.top&&k.bottomh.bottom,!h))break;q+= -d;h=k}n.detach();return q}function q(a,b,e){runtime.assert(null!==a,"SelectionMover.countStepsToPosition called with element===null");var d=c(),h=d.container(),f=d.unfilteredDomOffset(),k=0,g=new core.LoopWatchDog(1E4);for(d.setUnfilteredPosition(a,b);e.acceptPosition(d)!==t&&d.previousPosition();)g.check();a=d.container();runtime.assert(Boolean(a),"SelectionMover.countStepsToPosition: positionIterator.container() returned null");b=d.unfilteredDomOffset();for(d.setUnfilteredPosition(h,f);e.acceptPosition(d)!== -t&&d.previousPosition();)g.check();h=x.comparePoints(a,b,d.container(),d.unfilteredDomOffset());if(0>h)for(;d.nextPosition()&&(g.check(),e.acceptPosition(d)===t&&(k+=1),d.container()!==a||d.unfilteredDomOffset()!==b););else if(0 @@ -2122,16 +2022,8 @@ gui.SelectionMover.createPositionIterator=function(g){var l=new function(){this. @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils");runtime.loadClass("core.PositionFilter");runtime.loadClass("odf.OdfUtils"); -(function(){function g(c,g,f){function n(a,b){function c(a){for(var b=0;a&&a.previousSibling;)b+=1,a=a.previousSibling;return b}this.steps=a;this.node=b;this.setIteratorPosition=function(a){a.setUnfilteredPosition(b.parentNode,c(b));do if(g.acceptPosition(a)===u)break;while(a.nextPosition())}}function b(a){return a.nodeType===Node.ELEMENT_NODE&&a.getAttributeNS(d,"nodeId")}function p(a){var b=l;a.setAttributeNS(d,"nodeId",b.toString());l+=1;return b}function r(e,h){var f,k=null;for(e=e.childNodes[h]|| -e;!k&&e&&e!==c;)(f=b(e))&&(k=a[f])&&k.node!==e&&(runtime.log("Cloned node detected. Creating new bookmark"),k=null,e.removeAttributeNS(d,"nodeId")),e=e.parentNode;return k}var d="urn:webodf:names:steps",k={},a={},e=new odf.OdfUtils,h=new core.DomUtils,q,u=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.updateCache=function(c,d,h,g){var l;0===h&&e.isParagraph(d)?(l=!0,g||(c+=1)):d.hasChildNodes()&&d.childNodes[h]&&(d=d.childNodes[h],(l=e.isParagraph(d))&&(c+=1));l&&(h=b(d)||p(d),(g=a[h])?g.node=== -d?g.steps=c:(runtime.log("Cloned node detected. Creating new bookmark"),h=p(d),g=a[h]=new n(c,d)):g=a[h]=new n(c,d),h=g,c=Math.ceil(h.steps/f)*f,d=k[c],!d||h.steps>d.steps)&&(k[c]=h)};this.setToClosestStep=function(a,b){for(var c=Math.floor(a/f)*f,e;!e&&0!==c;)e=k[c],c-=f;e=e||q;e.setIteratorPosition(b);return e.steps};this.setToClosestDomPoint=function(a,b,e){var d;if(a===c&&0===b)d=q;else if(a===c&&b===c.childNodes.length)d=Object.keys(k).map(function(a){return k[a]}).reduce(function(a,b){return b.steps> -a.steps?b:a},q);else if(d=r(a,b),!d)for(e.setUnfilteredPosition(a,b);!d&&e.previousNode();)d=r(e.container(),e.unfilteredDomOffset());d=d||q;d.setIteratorPosition(e);return d.steps};this.updateCacheAtPoint=function(e,d){var g={};Object.keys(a).map(function(b){return a[b]}).filter(function(a){return a.steps>e}).forEach(function(e){var l=Math.ceil(e.steps/f)*f,m,q;if(h.containsNode(c,e.node)){if(d(e),m=Math.ceil(e.steps/f)*f,q=g[m],!q||e.steps>q.steps)g[m]=e}else delete a[b(e.node)];k[l]===e&&delete k[l]}); -Object.keys(g).forEach(function(a){k[a]=g[a]})};q=new function(a,b){this.steps=a;this.node=b;this.setIteratorPosition=function(a){a.setUnfilteredPosition(b,0);do if(g.acceptPosition(a)===u)break;while(a.nextPosition())}}(0,c)}var l=0;ops.StepsTranslator=function(c,l,f,n){function b(){var a=c();a!==p&&(runtime.log("Undo detected. Resetting steps cache"),p=a,r=new g(p,f,n),k=l(p))}var p=c(),r=new g(p,f,n),d=new core.DomUtils,k=l(c()),a=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.convertStepsToDomPoint= -function(c){var d,g;0>c&&(runtime.log("warn","Requested steps were negative ("+c+")"),c=0);b();for(d=r.setToClosestStep(c,k);dd.comparePoints(p,0,c,h),c=p,h=l?0: -p.childNodes.length);k.setUnfilteredPosition(c,h);l=k.container();m=k.unfilteredDomOffset();g&&f.acceptPosition(k)!==a&&(n=1);c=r.setToClosestDomPoint(c,h,k);if(0>d.comparePoints(k.container(),k.unfilteredDomOffset(),l,m))return 0b.steps&&(b.steps=0)})}};return ops.StepsTranslator})(); -// Input 69 +ops.OpRemoveBlob=function(){var g,k,e;this.init=function(n){g=n.memberid;k=n.timestamp;e=n.filename};this.isEdit=!0;this.execute=function(g){g.getOdfCanvas().odfContainer().removeBlob(e);return!0};this.spec=function(){return{optype:"RemoveBlob",memberid:g,timestamp:k,filename:e}}}; +// Input 72 /* Copyright (C) 2012-2013 KO GmbH @@ -2169,11 +2061,8 @@ f.acceptPosition(k)===a)&&(c+=1),r.updateCache(c,k.container(),k.unfilteredDomOf @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.PositionFilter");runtime.loadClass("odf.OdfUtils"); -ops.TextPositionFilter=function(g){function l(f,g,d){var k,a;if(g&&(k=c.lookLeftForCharacter(g),1===k||2===k&&(c.scanRightForAnyCharacter(d)||c.scanRightForAnyCharacter(c.nextNode(f)))))return n;k=null===g&&c.isParagraph(f);a=c.lookRightForCharacter(d);if(k)return a?n:c.scanRightForAnyCharacter(d)?b:n;if(!a)return b;g=g||c.previousNode(f);return c.scanLeftForAnyCharacter(g)?b:n}var c=new odf.OdfUtils,m=Node.ELEMENT_NODE,f=Node.TEXT_NODE,n=core.PositionFilter.FilterResult.FILTER_ACCEPT,b=core.PositionFilter.FilterResult.FILTER_REJECT; -this.acceptPosition=function(p){var r=p.container(),d=r.nodeType,k,a,e;if(d!==m&&d!==f)return b;if(d===f){if(!c.isGroupingElement(r.parentNode)||c.isWithinTrackedChanges(r.parentNode,g()))return b;d=p.unfilteredDomOffset();k=r.data;runtime.assert(d!==k.length,"Unexpected offset.");if(0 @@ -2198,64 +2087,50 @@ d)?b:n;a=k[d];return c.isODFWhitespace(a)?b:c.scanLeftForAnyCharacter(c.previous @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OperationTransformMatrix=function(){function g(a){a.position+=a.length;a.length*=-1}function l(a){var b=0>a.length;b&&g(a);return b}function c(a,b){var c=[];a&&["style:parent-style-name","style:next-style-name"].forEach(function(d){a[d]===b&&c.push(d)});return c}function m(a,b){a&&["style:parent-style-name","style:next-style-name"].forEach(function(c){a[c]===b&&delete a[c]})}function f(a){var b={};Object.keys(a).forEach(function(c){b[c]="object"===typeof a[c]?f(a[c]):a[c]});return b}function n(a, -b,c,d){var f,k,g=!1,l=!1,m,n,p=d&&d.attributes?d.attributes.split(","):[];a&&(c||0=c.position+c.length)){k=d?a:c;g=d?c:a;if(a.position!==c.position||a.length!==c.length)p=f(k),t=f(g);c=r(g,k,"style:text-properties");if(c.majorChanged||c.minorChanged)l=[],a=[],m=k.position+k.length,n=g.position+g.length,g.positionm?c.minorChanged&&(p=t,p.position=m,p.length=n-m,a.push(p),g.length=m-g.position):m>n&&c.majorChanged&&(p.position=n,p.length=m-n,l.push(p),k.length=n-k.position),k.setProperties&&b(k.setProperties)&&l.push(k),g.setProperties&&b(g.setProperties)&&a.push(g),d?(m=l,l=a):m=a}return{opSpecsA:m,opSpecsB:l}},InsertText:function(a, -b){b.position<=a.position?a.position+=b.text.length:b.position<=a.position+a.length&&(a.length+=b.text.length);return{opSpecsA:[a],opSpecsB:[b]}},MoveCursor:d,RemoveCursor:d,RemoveStyle:d,RemoveText:function(a,b){var c=a.position+a.length,d=b.position+b.length,f=[a],k=[b];d<=a.position?a.position-=b.length:b.positionb.position)a.position+=b.text.length;else return c?b.position+=a.text.length:a.position+=b.text.length,null;return{opSpecsA:[a],opSpecsB:[b]}},MoveCursor:function(a,b){var c=l(b);a.positionb.position)a.position+=1;else return c?b.position+=a.text.length:a.position+=1,null;return{opSpecsA:[a],opSpecsB:[b]}},UpdateMember:d,UpdateMetadata:d,UpdateParagraphStyle:d},MoveCursor:{MoveCursor:d,RemoveCursor:function(a,b){return{opSpecsA:a.memberid===b.memberid?[]:[a],opSpecsB:[b]}},RemoveMember:d,RemoveStyle:d,RemoveText:function(a,b){var c=l(a),d=a.position+a.length,f=b.position+b.length;f<= -a.position?a.position-=b.length:b.positionb.position)a.position+=1;else if(a.position===b.position)return c?b.position+=1:a.position+=1,null;return{opSpecsA:[a],opSpecsB:[b]}},UpdateMember:d,UpdateMetadata:d,UpdateParagraphStyle:d},UpdateMember:{UpdateMetadata:d,UpdateParagraphStyle:d},UpdateMetadata:{UpdateMetadata:function(a, -c,d){var f,k=[a],g=[c];f=d?a:c;a=d?c:a;n(a.setProperties||null,a.removedProperties||null,f.setProperties||null,f.removedProperties||null);f.setProperties&&b(f.setProperties)||f.removedProperties&&p(f.removedProperties)||(d?k=[]:g=[]);a.setProperties&&b(a.setProperties)||a.removedProperties&&p(a.removedProperties)||(d?g=[]:k=[]);return{opSpecsA:k,opSpecsB:g}},UpdateParagraphStyle:d},UpdateParagraphStyle:{UpdateParagraphStyle:function(a,c,d){var f,k=[a],g=[c];a.styleName===c.styleName&&(f=d?a:c,a=d? -c:a,r(a,f,"style:paragraph-properties"),r(a,f,"style:text-properties"),n(a.setProperties||null,a.removedProperties||null,f.setProperties||null,f.removedProperties||null),f.setProperties&&b(f.setProperties)||f.removedProperties&&p(f.removedProperties)||(d?k=[]:g=[]),a.setProperties&&b(a.setProperties)||a.removedProperties&&p(a.removedProperties)||(d?g=[]:k=[]));return{opSpecsA:k,opSpecsB:g}}}};this.passUnchanged=d;this.extendTransformations=function(a){Object.keys(a).forEach(function(b){var c=a[b], -d,f=k.hasOwnProperty(b);runtime.log((f?"Extending":"Adding")+" map for optypeA: "+b);f||(k[b]={});d=k[b];Object.keys(c).forEach(function(a){var f=d.hasOwnProperty(a);runtime.assert(b<=a,"Wrong order:"+b+", "+a);runtime.log(" "+(f?"Overwriting":"Adding")+" entry for optypeB: "+a);d[a]=c[a]})})};this.transformOpspecVsOpspec=function(a,b){var c=a.optype<=b.optype,d;runtime.log("Crosstransforming:");runtime.log(runtime.toJson(a));runtime.log(runtime.toJson(b));c||(d=a,a=b,b=d);(d=(d=k[a.optype])&&d[b.optype])? -(d=d(a,b,!c),c||null===d||(d={opSpecsA:d.opSpecsB,opSpecsB:d.opSpecsA})):d=null;runtime.log("result:");d?(runtime.log(runtime.toJson(d.opSpecsA)),runtime.log(runtime.toJson(d.opSpecsB))):runtime.log("null");return d}}; -// Input 71 +runtime.loadClass("ops.Member");ops.OpRemoveMember=function(){var g,k;this.init=function(e){g=e.memberid;k=parseInt(e.timestamp,10)};this.isEdit=!1;this.execute=function(e){if(!e.getMember(g))return!1;e.removeMember(g);e.emit(ops.OdtDocument.signalMemberRemoved,g);return!0};this.spec=function(){return{optype:"RemoveMember",memberid:g,timestamp:k}}}; +// Input 74 /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart - This file is part of WebODF. + 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. - WebODF 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. + You should have received a copy of the GNU Affero General Public License + along with this code. If not, see . - WebODF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License 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. - You should have received a copy of the GNU Affero General Public License - along with WebODF. If not, see . - @licend + 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: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("ops.OperationFactory");runtime.loadClass("ops.OperationTransformMatrix"); -ops.OperationTransformer=function(){function g(f){var g=[];f.forEach(function(b){g.push(c.create(b))});return g}function l(c,g){for(var b,p,r=[],d=[];0=b&&(d=-n.movePointBackward(-b,f));c.handleUpdate();return d};this.handleUpdate=function(){};this.getStepCounter=function(){return n.getStepCounter()};this.getMemberId=function(){return g};this.getNode=function(){return b.getNode()};this.getAnchorNode=function(){return b.getAnchorNode()};this.getSelectedRange=function(){return b.getSelectedRange()}; -this.setSelectedRange=function(f,g){b.setSelectedRange(f,g);c.handleUpdate()};this.hasForwardSelection=function(){return b.hasForwardSelection()};this.getOdtDocument=function(){return l};this.getSelectionType=function(){return f};this.setSelectionType=function(b){m.hasOwnProperty(b)?f=b:runtime.log("Invalid selection type: "+b)};this.resetSelectionType=function(){c.setSelectionType(ops.OdtCursor.RangeSelection)};b=new core.Cursor(l.getDOM(),g);n=new gui.SelectionMover(b,l.getRootNode());m[ops.OdtCursor.RangeSelection]= -!0;m[ops.OdtCursor.RegionSelection]=!0;c.resetSelectionType()};ops.OdtCursor.RangeSelection="Range";ops.OdtCursor.RegionSelection="Region";(function(){return ops.OdtCursor})(); -// Input 73 +ops.OpRemoveStyle=function(){var g,k,e,n;this.init=function(m){g=m.memberid;k=m.timestamp;e=m.styleName;n=m.styleFamily};this.isEdit=!0;this.execute=function(g){var k=g.getStyleElement(e,n);if(!k)return!1;k.parentNode.removeChild(k);g.getOdfCanvas().refreshCSS();g.emit(ops.OdtDocument.signalCommonStyleDeleted,{name:e,family:n});return!0};this.spec=function(){return{optype:"RemoveStyle",memberid:g,timestamp:k,styleName:e,styleFamily:n}}}; +// Input 75 /* - Copyright (C) 2012 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2290,26 +2165,16 @@ this.setSelectedRange=function(f,g){b.setSelectedRange(f,g);c.handleUpdate()};th @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.EditInfo=function(g,l){function c(){var c=[],b;for(b in f)f.hasOwnProperty(b)&&c.push({memberid:b,time:f[b].time});c.sort(function(b,c){return b.time-c.time});return c}var m,f={};this.getNode=function(){return m};this.getOdtDocument=function(){return l};this.getEdits=function(){return f};this.getSortedEdits=function(){return c()};this.addEdit=function(c,b){f[c]={time:b}};this.clearEdits=function(){f={}};this.destroy=function(c){g.parentNode&&g.removeChild(m);c()};m=l.getDOM().createElementNS("urn:webodf:names:editinfo", -"editinfo");g.insertBefore(m,g.firstChild)}; -// Input 74 -runtime.loadClass("gui.SelectionMover");gui.ShadowCursor=function(g){var l=g.getDOM().createRange(),c=!0;this.removeFromOdtDocument=function(){};this.getMemberId=function(){return gui.ShadowCursor.ShadowCursorMemberId};this.getSelectedRange=function(){return l};this.setSelectedRange=function(g,f){l=g;c=!1!==f};this.hasForwardSelection=function(){return c};this.getOdtDocument=function(){return g};this.getSelectionType=function(){return ops.OdtCursor.RangeSelection};l.setStart(g.getRootNode(),0)}; -gui.ShadowCursor.ShadowCursorMemberId="";(function(){return gui.ShadowCursor})(); -// Input 75 -gui.Avatar=function(g,l){var c=this,m,f,n;this.setColor=function(b){f.style.borderColor=b};this.setImageUrl=function(b){c.isVisible()?f.src=b:n=b};this.isVisible=function(){return"block"===m.style.display};this.show=function(){n&&(f.src=n,n=void 0);m.style.display="block"};this.hide=function(){m.style.display="none"};this.markAsFocussed=function(b){m.className=b?"active":""};this.destroy=function(b){g.removeChild(m);b()};(function(){var b=g.ownerDocument,c=b.documentElement.namespaceURI;m=b.createElementNS(c, -"div");f=b.createElementNS(c,"img");f.width=64;f.height=64;m.appendChild(f);m.style.width="64px";m.style.height="70px";m.style.position="absolute";m.style.top="-80px";m.style.left="-34px";m.style.display=l?"block":"none";g.appendChild(m)})()}; +runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.OdfUtils");runtime.loadClass("core.DomUtils"); +ops.OpRemoveText=function(){function g(e){function d(a){return h.hasOwnProperty(a.namespaceURI)||"br"===a.localName&&q.isLineBreak(a.parentNode)||a.nodeType===Node.TEXT_NODE&&h.hasOwnProperty(a.parentNode.namespaceURI)}function f(a){if(q.isCharacterElement(a))return!1;if(a.nodeType===Node.TEXT_NODE)return 0===a.textContent.length;for(a=a.firstChild;a;){if(h.hasOwnProperty(a.namespaceURI)||!f(a))return!1;a=a.nextSibling}return!0}function a(c){var g;c.nodeType===Node.TEXT_NODE?(g=c.parentNode,g.removeChild(c)): +g=b.removeUnwantedNodes(c,d);return!q.isParagraph(g)&&g!==e&&f(g)?a(g):g}this.isEmpty=f;this.mergeChildrenIntoParent=a}var k,e,n,m,q,b,h={};this.init=function(g){runtime.assert(0<=g.length,"OpRemoveText only supports positive lengths");k=g.memberid;e=g.timestamp;n=parseInt(g.position,10);m=parseInt(g.length,10);q=new odf.OdfUtils;b=new core.DomUtils;h[odf.Namespaces.dbns]=!0;h[odf.Namespaces.dcns]=!0;h[odf.Namespaces.dr3dns]=!0;h[odf.Namespaces.drawns]=!0;h[odf.Namespaces.chartns]=!0;h[odf.Namespaces.formns]= +!0;h[odf.Namespaces.numberns]=!0;h[odf.Namespaces.officens]=!0;h[odf.Namespaces.presentationns]=!0;h[odf.Namespaces.stylens]=!0;h[odf.Namespaces.svgns]=!0;h[odf.Namespaces.tablens]=!0;h[odf.Namespaces.textns]=!0};this.isEdit=!0;this.execute=function(h){var d,f,a,c,p=h.getCursor(k),l=new g(h.getRootNode());h.upgradeWhitespacesAtPosition(n);h.upgradeWhitespacesAtPosition(n+m);f=h.convertCursorToDomRange(n,m);b.splitBoundaries(f);d=h.getParagraphElement(f.startContainer);a=q.getTextElements(f,!1,!0); +c=q.getParagraphElements(f);f.detach();a.forEach(function(a){l.mergeChildrenIntoParent(a)});f=c.reduce(function(a,c){var b,d=!1,f=a,e=c,g,h=null;l.isEmpty(a)&&(d=!0,c.parentNode!==a.parentNode&&(g=c.parentNode,a.parentNode.insertBefore(c,a.nextSibling)),e=a,f=c,h=f.getElementsByTagNameNS("urn:webodf:names:editinfo","editinfo")[0]||f.firstChild);for(;e.hasChildNodes();)b=d?e.lastChild:e.firstChild,e.removeChild(b),"editinfo"!==b.localName&&f.insertBefore(b,h);g&&l.isEmpty(g)&&l.mergeChildrenIntoParent(g); +l.mergeChildrenIntoParent(e);return f});h.emit(ops.OdtDocument.signalStepsRemoved,{position:n,length:m});h.downgradeWhitespacesAtPosition(n);h.fixCursorPositions();h.getOdfCanvas().refreshSize();h.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:f||d,memberId:k,timeStamp:e});p&&(p.resetSelectionType(),h.emit(ops.OdtDocument.signalCursorMoved,p));h.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"RemoveText",memberid:k,timestamp:e,position:n,length:m}}}; // Input 76 -runtime.loadClass("core.DomUtils");runtime.loadClass("gui.Avatar");runtime.loadClass("ops.OdtCursor"); -gui.Caret=function(g,l,c){function m(c){k&&r.parentNode&&(!a||c)&&(c&&void 0!==e&&runtime.clearTimeout(e),a=!0,b.style.opacity=c||"0"===b.style.opacity?"1":"0",e=runtime.setTimeout(function(){a=!1;m(!1)},500))}function f(a,b){var c=a.getBoundingClientRect(),d=0,e=0;c&&b&&(d=Math.max(c.top,b.top),e=Math.min(c.bottom,b.bottom));return e-d}function n(){var a;a=g.getSelectedRange().cloneRange();var c=g.getNode(),e,k=null;c.previousSibling&&(e=c.previousSibling.nodeType===Node.TEXT_NODE?c.previousSibling.textContent.length: -c.previousSibling.childNodes.length,a.setStart(c.previousSibling,0f(c,k))&&(k=e));a=k;c=g.getOdtDocument().getOdfCanvas().getZoomLevel();d&&g.getSelectionType()===ops.OdtCursor.RangeSelection? -b.style.visibility="visible":b.style.visibility="hidden";a?(b.style.top="0",k=h.getBoundingClientRect(b),8>a.height&&(a={top:a.top-(8-a.height)/2,height:8}),b.style.height=h.adaptRangeDifferenceToZoomLevel(a.height,c)+"px",b.style.top=h.adaptRangeDifferenceToZoomLevel(a.top-k.top,c)+"px"):(b.style.height="1em",b.style.top="5%")}var b,p,r,d=!0,k=!1,a=!1,e,h=new core.DomUtils;this.handleUpdate=n;this.refreshCursorBlinking=function(){c||g.getSelectedRange().collapsed?(k=!0,m(!0)):(k=!1,b.style.opacity= -"0")};this.setFocus=function(){k=!0;p.markAsFocussed(!0);m(!0)};this.removeFocus=function(){k=!1;p.markAsFocussed(!1);b.style.opacity="1"};this.show=function(){d=!0;n();p.markAsFocussed(!0)};this.hide=function(){d=!1;n();p.markAsFocussed(!1)};this.setAvatarImageUrl=function(a){p.setImageUrl(a)};this.setColor=function(a){b.style.borderColor=a;p.setColor(a)};this.getCursor=function(){return g};this.getFocusElement=function(){return b};this.toggleHandleVisibility=function(){p.isVisible()?p.hide():p.show()}; -this.showHandle=function(){p.show()};this.hideHandle=function(){p.hide()};this.ensureVisible=function(){var a,c,d,e,f=g.getOdtDocument().getOdfCanvas().getElement().parentNode,k;d=f.offsetWidth-f.clientWidth+5;e=f.offsetHeight-f.clientHeight+5;k=b.getBoundingClientRect();a=k.left-d;c=k.top-e;d=k.right+d;e=k.bottom+e;k=f.getBoundingClientRect();ck.bottom&&(f.scrollTop+=e-k.bottom);ak.right&&(f.scrollLeft+=d-k.right);n()};this.destroy=function(a){p.destroy(function(c){c? -a(c):(r.removeChild(b),a())})};(function(){var a=g.getOdtDocument().getDOM();b=a.createElementNS(a.documentElement.namespaceURI,"span");b.style.top="5%";r=g.getNode();r.appendChild(b);p=new gui.Avatar(r,l);n()})()}; -// Input 77 /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2344,11 +2209,11 @@ a(c):(r.removeChild(b),a())})};(function(){var a=g.getOdtDocument().getDOM();b=a @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -gui.PlainTextPasteboard=function(g,l){function c(c,f){c.init(f);return c}this.createPasteOps=function(m){var f=g.getCursorPosition(l),n=f,b=[];m.replace(/\r/g,"").split("\n").forEach(function(f){b.push(c(new ops.OpSplitParagraph,{memberid:l,position:n}));n+=1;b.push(c(new ops.OpInsertText,{memberid:l,position:n,text:f}));n+=f.length});b.push(c(new ops.OpRemoveText,{memberid:l,position:f,length:1}));return b}}; -// Input 78 +ops.OpSetBlob=function(){var g,k,e,n,m;this.init=function(q){g=q.memberid;k=q.timestamp;e=q.filename;n=q.mimetype;m=q.content};this.isEdit=!0;this.execute=function(g){g.getOdfCanvas().odfContainer().setBlob(e,n,m);return!0};this.spec=function(){return{optype:"SetBlob",memberid:g,timestamp:k,filename:e,mimetype:n,content:m}}}; +// Input 77 /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2383,13 +2248,12 @@ gui.PlainTextPasteboard=function(g,l){function c(c,f){c.init(f);return c}this.cr @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.Namespaces");runtime.loadClass("xmldom.LSSerializer");runtime.loadClass("odf.OdfNodeFilter");runtime.loadClass("odf.TextSerializer"); -gui.Clipboard=function(){var g,l,c;this.setDataFromRange=function(c,f){var n=!0,b,p=c.clipboardData;b=runtime.getWindow();var r=f.startContainer.ownerDocument;!p&&b&&(p=b.clipboardData);p?(r=r.createElement("span"),r.appendChild(f.cloneContents()),b=p.setData("text/plain",l.writeToString(r)),n=n&&b,b=p.setData("text/html",g.writeToString(r,odf.Namespaces.namespaceMap)),n=n&&b,c.preventDefault()):n=!1;return n};g=new xmldom.LSSerializer;l=new odf.TextSerializer;c=new odf.OdfNodeFilter;g.filter=c;l.filter= -c}; -// Input 79 +ops.OpSetParagraphStyle=function(){var g,k,e,n;this.init=function(m){g=m.memberid;k=m.timestamp;e=m.position;n=m.styleName};this.isEdit=!0;this.execute=function(m){var q;q=m.getIteratorAtPosition(e);return(q=m.getParagraphElement(q.container()))?(""!==n?q.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0","text:style-name",n):q.removeAttributeNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0","style-name"),m.getOdfCanvas().refreshSize(),m.emit(ops.OdtDocument.signalParagraphChanged, +{paragraphElement:q,timeStamp:k,memberId:g}),m.getOdfCanvas().rerenderAnnotations(),!0):!1};this.spec=function(){return{optype:"SetParagraphStyle",memberid:g,timestamp:k,position:e,styleName:n}}}; +// Input 78 /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2424,20 +2288,69 @@ c}; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.EventNotifier");runtime.loadClass("core.Utils");runtime.loadClass("ops.OpApplyDirectStyling");runtime.loadClass("gui.StyleHelper"); -gui.DirectTextStyler=function(g,l){function c(a,b){for(var c=0,d=b[c];d&&a;)a=a[d],c+=1,d=b[c];return b.length===c?a:void 0}function m(a,b){var d=c(a[0],b);return a.every(function(a){return d===c(a,b)})?d:void 0}function f(){var a=t.getCursor(l),a=(a=a&&a.getSelectedRange())&&s.getAppliedStyles(a)||[];a[0]&&A&&(a[0]=w.mergeObjects(a[0],A));return a}function n(){function a(b,d,e){b!==d&&(void 0===c&&(c={}),c[e]=d);return d}var b,c;B=f();M=a(M,B?s.isBold(B):!1,"isBold");z=a(z,B?s.isItalic(B):!1,"isItalic"); -K=a(K,B?s.hasUnderline(B):!1,"hasUnderline");F=a(F,B?s.hasStrikeThrough(B):!1,"hasStrikeThrough");b=B&&m(B,["style:text-properties","fo:font-size"]);Q=a(Q,b&&parseFloat(b),"fontSize");$=a($,B&&m(B,["style:text-properties","style:font-name"]),"fontName");c&&D.emit(gui.DirectTextStyler.textStylingChanged,c)}function b(a){a.getMemberId()===l&&n()}function p(a){a===l&&n()}function r(a){a.getMemberId()===l&&n()}function d(){n()}function k(a){var b=t.getCursor(l);b&&t.getParagraphElement(b.getNode())=== -a.paragraphElement&&n()}function a(a,b){var c=t.getCursor(l);if(!c)return!1;c=s.getAppliedStyles(c.getSelectedRange());b(!a(c));return!0}function e(a){var b=t.getCursorSelection(l),c={"style:text-properties":a};0!==b.length?(a=new ops.OpApplyDirectStyling,a.init({memberid:l,position:b.position,length:b.length,setProperties:c}),g.enqueue([a])):(A=w.mergeObjects(A||{},c),n())}function h(a,b){var c={};c[a]=b;e(c)}function q(a){a=a.spec();A&&a.memberid===l&&"SplitParagraph"!==a.optype&&(A=null,n())}function u(a){h("fo:font-weight", -a?"bold":"normal")}function x(a){h("fo:font-style",a?"italic":"normal")}function v(a){h("style:text-underline-style",a?"solid":"none")}function y(a){h("style:text-line-through-style",a?"solid":"none")}var w=new core.Utils,t=g.getOdtDocument(),s=new gui.StyleHelper(t.getFormatting()),D=new core.EventNotifier([gui.DirectTextStyler.textStylingChanged]),A,B=[],M=!1,z=!1,K=!1,F=!1,Q,$;this.formatTextSelection=e;this.createCursorStyleOp=function(a,b){var c=null;A&&(c=new ops.OpApplyDirectStyling,c.init({memberid:l, -position:a,length:b,setProperties:A}),A=null,n());return c};this.setBold=u;this.setItalic=x;this.setHasUnderline=v;this.setHasStrikethrough=y;this.setFontSize=function(a){h("fo:font-size",a+"pt")};this.setFontName=function(a){h("style:font-name",a)};this.getAppliedStyles=function(){return B};this.toggleBold=a.bind(this,s.isBold,u);this.toggleItalic=a.bind(this,s.isItalic,x);this.toggleUnderline=a.bind(this,s.hasUnderline,v);this.toggleStrikethrough=a.bind(this,s.hasStrikeThrough,y);this.isBold=function(){return M}; -this.isItalic=function(){return z};this.hasUnderline=function(){return K};this.hasStrikeThrough=function(){return F};this.fontSize=function(){return Q};this.fontName=function(){return $};this.subscribe=function(a,b){D.subscribe(a,b)};this.unsubscribe=function(a,b){D.unsubscribe(a,b)};this.destroy=function(a){t.unsubscribe(ops.OdtDocument.signalCursorAdded,b);t.unsubscribe(ops.OdtDocument.signalCursorRemoved,p);t.unsubscribe(ops.OdtDocument.signalCursorMoved,r);t.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, -d);t.unsubscribe(ops.OdtDocument.signalParagraphChanged,k);t.unsubscribe(ops.OdtDocument.signalOperationExecuted,q);a()};t.subscribe(ops.OdtDocument.signalCursorAdded,b);t.subscribe(ops.OdtDocument.signalCursorRemoved,p);t.subscribe(ops.OdtDocument.signalCursorMoved,r);t.subscribe(ops.OdtDocument.signalParagraphStyleModified,d);t.subscribe(ops.OdtDocument.signalParagraphChanged,k);t.subscribe(ops.OdtDocument.signalOperationExecuted,q);n()};gui.DirectTextStyler.textStylingChanged="textStyling/changed"; -(function(){return gui.DirectTextStyler})(); +ops.OpSplitParagraph=function(){var g,k,e,n;this.init=function(m){g=m.memberid;k=m.timestamp;e=m.position;n=new odf.OdfUtils};this.isEdit=!0;this.execute=function(m){var q,b,h,r,d,f,a;m.upgradeWhitespacesAtPosition(e);q=m.getTextNodeAtStep(e,g);if(!q)return!1;b=m.getParagraphElement(q.textNode);if(!b)return!1;h=n.isListItem(b.parentNode)?b.parentNode:b;0===q.offset?(a=q.textNode.previousSibling,f=null):(a=q.textNode,f=q.offset>=q.textNode.length?null:q.textNode.splitText(q.offset));for(r=q.textNode;r!== +h;){r=r.parentNode;d=r.cloneNode(!1);f&&d.appendChild(f);if(a)for(;a&&a.nextSibling;)d.appendChild(a.nextSibling);else for(;r.firstChild;)d.appendChild(r.firstChild);r.parentNode.insertBefore(d,r.nextSibling);a=r;f=d}n.isListItem(f)&&(f=f.childNodes[0]);0===q.textNode.length&&q.textNode.parentNode.removeChild(q.textNode);m.emit(ops.OdtDocument.signalStepsInserted,{position:e,length:1});m.fixCursorPositions();m.getOdfCanvas().refreshSize();m.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:b, +memberId:g,timeStamp:k});m.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:f,memberId:g,timeStamp:k});m.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"SplitParagraph",memberid:g,timestamp:k,position:e}}}; +// Input 79 +/* + + Copyright (C) 2013 KO GmbH + + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("ops.Member");runtime.loadClass("xmldom.XPath"); +ops.OpUpdateMember=function(){function g(){var b="//dc:creator[@editinfo:memberid='"+k+"']",b=xmldom.XPath.getODFElementsWithXPath(q.getRootNode(),b,function(b){return"editinfo"===b?"urn:webodf:names:editinfo":odf.Namespaces.lookupNamespaceURI(b)}),e;for(e=0;e + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +ops.OpUpdateMetadata=function(){var g,k,e,n;this.init=function(m){g=m.memberid;k=parseInt(m.timestamp,10);e=m.setProperties;n=m.removedProperties};this.isEdit=!0;this.execute=function(g){g=g.getOdfCanvas().odfContainer().getMetadataManager();var k=[],b=["dc:date","dc:creator","meta:editing-cycles"];e&&b.forEach(function(b){if(e[b])return!1});n&&(b.forEach(function(b){if(-1!==k.indexOf(b))return!1}),k=n.attributes.split(","));g.setMetadata(e,k);return!0};this.spec=function(){return{optype:"UpdateMetadata", +memberid:g,timestamp:k,setProperties:e,removedProperties:n}}}; +// Input 81 +/* + + Copyright (C) 2012-2013 KO GmbH + @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 @@ -2471,14 +2384,11 @@ d);t.unsubscribe(ops.OdtDocument.signalParagraphChanged,k);t.unsubscribe(ops.Odt @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.EventNotifier");runtime.loadClass("core.Utils");runtime.loadClass("odf.OdfUtils");runtime.loadClass("ops.OpAddStyle");runtime.loadClass("ops.OpSetParagraphStyle");runtime.loadClass("gui.StyleHelper"); -gui.DirectParagraphStyler=function(g,l,c){function m(){function a(b,d,e){b!==d&&(void 0===c&&(c={}),c[e]=d);return d}var b=h.getCursor(l),b=b&&b.getSelectedRange(),c;y=a(y,b?x.isAlignedLeft(b):!1,"isAlignedLeft");w=a(w,b?x.isAlignedCenter(b):!1,"isAlignedCenter");t=a(t,b?x.isAlignedRight(b):!1,"isAlignedRight");s=a(s,b?x.isAlignedJustified(b):!1,"isAlignedJustified");c&&v.emit(gui.DirectParagraphStyler.paragraphStylingChanged,c)}function f(a){a.getMemberId()===l&&m()}function n(a){a===l&&m()}function b(a){a.getMemberId()=== -l&&m()}function p(){m()}function r(a){var b=h.getCursor(l);b&&h.getParagraphElement(b.getNode())===a.paragraphElement&&m()}function d(a){var b=h.getCursor(l).getSelectedRange(),d=h.getCursorPosition(l),b=u.getParagraphElements(b),e=h.getFormatting();b.forEach(function(b){var f=d+h.getDistanceFromCursor(l,b,0),k=b.getAttributeNS(odf.Namespaces.textns,"style-name");b=c.generateStyleName();var m,f=f+1;k&&(m=e.createDerivedStyleObject(k,"paragraph",{}));m=a(m||{});k=new ops.OpAddStyle;k.init({memberid:l, -styleName:b,styleFamily:"paragraph",isAutomaticStyle:!0,setProperties:m});m=new ops.OpSetParagraphStyle;m.init({memberid:l,styleName:b,position:f});g.enqueue([k,m])})}function k(a){d(function(b){return q.mergeObjects(b,a)})}function a(a){k({"style:paragraph-properties":{"fo:text-align":a}})}function e(a,b){var c=h.getFormatting().getDefaultTabStopDistance(),d=b["style:paragraph-properties"],d=(d=d&&d["fo:margin-left"])&&u.parseLength(d);return q.mergeObjects(b,{"style:paragraph-properties":{"fo:margin-left":d&& -d.unit===c.unit?d.value+a*c.value+d.unit:a*c.value+c.unit}})}var h=g.getOdtDocument(),q=new core.Utils,u=new odf.OdfUtils,x=new gui.StyleHelper(h.getFormatting()),v=new core.EventNotifier([gui.DirectParagraphStyler.paragraphStylingChanged]),y,w,t,s;this.isAlignedLeft=function(){return y};this.isAlignedCenter=function(){return w};this.isAlignedRight=function(){return t};this.isAlignedJustified=function(){return s};this.alignParagraphLeft=function(){a("left");return!0};this.alignParagraphCenter=function(){a("center"); -return!0};this.alignParagraphRight=function(){a("right");return!0};this.alignParagraphJustified=function(){a("justify");return!0};this.indent=function(){d(e.bind(null,1));return!0};this.outdent=function(){d(e.bind(null,-1));return!0};this.subscribe=function(a,b){v.subscribe(a,b)};this.unsubscribe=function(a,b){v.unsubscribe(a,b)};this.destroy=function(a){h.unsubscribe(ops.OdtDocument.signalCursorAdded,f);h.unsubscribe(ops.OdtDocument.signalCursorRemoved,n);h.unsubscribe(ops.OdtDocument.signalCursorMoved, -b);h.unsubscribe(ops.OdtDocument.signalParagraphStyleModified,p);h.unsubscribe(ops.OdtDocument.signalParagraphChanged,r);a()};h.subscribe(ops.OdtDocument.signalCursorAdded,f);h.subscribe(ops.OdtDocument.signalCursorRemoved,n);h.subscribe(ops.OdtDocument.signalCursorMoved,b);h.subscribe(ops.OdtDocument.signalParagraphStyleModified,p);h.subscribe(ops.OdtDocument.signalParagraphChanged,r);m()};gui.DirectParagraphStyler.paragraphStylingChanged="paragraphStyling/changed";(function(){return gui.DirectParagraphStyler})(); -// Input 81 +runtime.loadClass("odf.Namespaces"); +ops.OpUpdateParagraphStyle=function(){function g(b,e){var d,f,a=e?e.split(","):[];for(d=0;d @@ -2516,20 +2426,11 @@ b);h.unsubscribe(ops.OdtDocument.signalParagraphStyleModified,p);h.unsubscribe(o @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -gui.KeyboardHandler=function(){function g(c,g){g||(g=l.None);return c+":"+g}var l=gui.KeyboardHandler.Modifier,c=null,m={};this.setDefault=function(f){c=f};this.bind=function(c,l,b){c=g(c,l);runtime.assert(!1===m.hasOwnProperty(c),"tried to overwrite the callback handler of key combo: "+c);m[c]=b};this.unbind=function(c,l){var b=g(c,l);delete m[b]};this.reset=function(){c=null;m={}};this.handleEvent=function(f){var n=f.keyCode,b=l.None;f.metaKey&&(b|=l.Meta);f.ctrlKey&&(b|=l.Ctrl);f.altKey&&(b|=l.Alt); -f.shiftKey&&(b|=l.Shift);n=g(n,b);n=m[n];b=!1;n?b=n():null!==c&&(b=c(f));b&&(f.preventDefault?f.preventDefault():f.returnValue=!1)}};gui.KeyboardHandler.Modifier={None:0,Meta:1,Ctrl:2,Alt:4,CtrlAlt:6,Shift:8,MetaShift:9,CtrlShift:10,AltShift:12};gui.KeyboardHandler.KeyCode={Backspace:8,Tab:9,Clear:12,Enter:13,End:35,Home:36,Left:37,Up:38,Right:39,Down:40,Delete:46,A:65,B:66,C:67,D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90};(function(){return gui.KeyboardHandler})(); -// Input 82 -runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.ObjectNameGenerator"); -gui.ImageManager=function(g,l,c){var m={"image/gif":".gif","image/jpeg":".jpg","image/png":".png"},f=odf.Namespaces.textns,n=g.getOdtDocument(),b=n.getFormatting(),p={};this.insertImage=function(r,d,k,a){var e;runtime.assert(0e.width&&(h=e.width/k);a>e.height&&(q=e.height/a);h=Math.min(h,q);e=k*h;k=a*h;q=n.getOdfCanvas().odfContainer().rootElement.styles;a=r.toLowerCase();var h=m.hasOwnProperty(a)?m[a]:null,u;a=[];runtime.assert(null!==h,"Image type is not supported: "+r);h="Pictures/"+c.generateImageName()+h;u=new ops.OpSetBlob;u.init({memberid:l,filename:h,mimetype:r,content:d});a.push(u);b.getStyleElement("Graphics","graphic",[q])||(r=new ops.OpAddStyle,r.init({memberid:l,styleName:"Graphics",styleFamily:"graphic", -isAutomaticStyle:!1,setProperties:{"style:graphic-properties":{"text:anchor-type":"paragraph","svg:x":"0cm","svg:y":"0cm","style:wrap":"dynamic","style:number-wrapped-paragraphs":"no-limit","style:wrap-contour":"false","style:vertical-pos":"top","style:vertical-rel":"paragraph","style:horizontal-pos":"center","style:horizontal-rel":"paragraph"}}}),a.push(r));r=c.generateStyleName();d=new ops.OpAddStyle;d.init({memberid:l,styleName:r,styleFamily:"graphic",isAutomaticStyle:!0,setProperties:{"style:parent-style-name":"Graphics", -"style:graphic-properties":{"style:vertical-pos":"top","style:vertical-rel":"baseline","style:horizontal-pos":"center","style:horizontal-rel":"paragraph","fo:background-color":"transparent","style:background-transparency":"100%","style:shadow":"none","style:mirror":"none","fo:clip":"rect(0cm, 0cm, 0cm, 0cm)","draw:luminance":"0%","draw:contrast":"0%","draw:red":"0%","draw:green":"0%","draw:blue":"0%","draw:gamma":"100%","draw:color-inversion":"false","draw:image-opacity":"100%","draw:color-mode":"standard"}}}); -a.push(d);u=new ops.OpInsertImage;u.init({memberid:l,position:n.getCursorPosition(l),filename:h,frameWidth:e+"cm",frameHeight:k+"cm",frameStyleName:r,frameName:c.generateFrameName()});a.push(u);g.enqueue(a)}}; +runtime.loadClass("ops.OpAddMember");runtime.loadClass("ops.OpUpdateMember");runtime.loadClass("ops.OpRemoveMember");runtime.loadClass("ops.OpAddCursor");runtime.loadClass("ops.OpApplyDirectStyling");runtime.loadClass("ops.OpRemoveCursor");runtime.loadClass("ops.OpMoveCursor");runtime.loadClass("ops.OpSetBlob");runtime.loadClass("ops.OpRemoveBlob");runtime.loadClass("ops.OpInsertImage");runtime.loadClass("ops.OpInsertTable");runtime.loadClass("ops.OpInsertText");runtime.loadClass("ops.OpRemoveText"); +runtime.loadClass("ops.OpSplitParagraph");runtime.loadClass("ops.OpSetParagraphStyle");runtime.loadClass("ops.OpUpdateParagraphStyle");runtime.loadClass("ops.OpAddStyle");runtime.loadClass("ops.OpRemoveStyle");runtime.loadClass("ops.OpAddAnnotation");runtime.loadClass("ops.OpRemoveAnnotation");runtime.loadClass("ops.OpUpdateMetadata"); +ops.OperationFactory=function(){function g(e){return function(){return new e}}var k;this.register=function(e,g){k[e]=g};this.create=function(e){var g=null,m=k[e.optype];m&&(g=m(e),g.init(e));return g};k={AddMember:g(ops.OpAddMember),UpdateMember:g(ops.OpUpdateMember),RemoveMember:g(ops.OpRemoveMember),AddCursor:g(ops.OpAddCursor),ApplyDirectStyling:g(ops.OpApplyDirectStyling),SetBlob:g(ops.OpSetBlob),RemoveBlob:g(ops.OpRemoveBlob),InsertImage:g(ops.OpInsertImage),InsertTable:g(ops.OpInsertTable), +InsertText:g(ops.OpInsertText),RemoveText:g(ops.OpRemoveText),SplitParagraph:g(ops.OpSplitParagraph),SetParagraphStyle:g(ops.OpSetParagraphStyle),UpdateParagraphStyle:g(ops.OpUpdateParagraphStyle),AddStyle:g(ops.OpAddStyle),RemoveStyle:g(ops.OpRemoveStyle),MoveCursor:g(ops.OpMoveCursor),RemoveCursor:g(ops.OpRemoveCursor),AddAnnotation:g(ops.OpAddAnnotation),RemoveAnnotation:g(ops.OpRemoveAnnotation),UpdateMetadata:g(ops.OpUpdateMetadata)}}; // Input 83 -runtime.loadClass("odf.Namespaces"); -gui.ImageSelector=function(g){function l(){var b=g.getSizer(),c,l;c=f.createElement("div");c.id="imageSelector";c.style.borderWidth="1px";b.appendChild(c);m.forEach(function(b){l=f.createElement("div");l.className=b;c.appendChild(l)});return c}var c=odf.Namespaces.svgns,m="topLeft topRight bottomRight bottomLeft topMiddle rightMiddle bottomMiddle leftMiddle".split(" "),f=g.getElement().ownerDocument,n=!1;this.select=function(b){var m,r,d=f.getElementById("imageSelector");d||(d=l());n=!0;m=d.parentNode; -r=b.getBoundingClientRect();var k=m.getBoundingClientRect(),a=g.getZoomLevel();m=(r.left-k.left)/a-1;r=(r.top-k.top)/a-1;d.style.display="block";d.style.left=m+"px";d.style.top=r+"px";d.style.width=b.getAttributeNS(c,"width");d.style.height=b.getAttributeNS(c,"height")};this.clearSelection=function(){var b;n&&(b=f.getElementById("imageSelector"))&&(b.style.display="none");n=!1};this.isSelectorElement=function(b){var c=f.getElementById("imageSelector");return c?b===c||b.parentNode===c:!1}}; -// Input 84 /* Copyright (C) 2013 KO GmbH @@ -2567,11 +2468,53 @@ r=b.getBoundingClientRect();var k=m.getBoundingClientRect(),a=g.getZoomLevel();m @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.PositionFilter"); -gui.TextManipulator=function(g,l,c){function m(b){var c=new ops.OpRemoveText;c.init({memberid:l,position:b.position,length:b.length});return c}function f(b){0>b.length&&(b.position+=b.length,b.length=-b.length);return b}function n(c,d){var f=new core.PositionFilterChain,a=gui.SelectionMover.createPositionIterator(b.getRootElement(c)),e=d?a.nextPosition:a.previousPosition;f.addFilter("BaseFilter",b.getPositionFilter());f.addFilter("RootFilter",b.createRootFilter(l));for(a.setUnfilteredPosition(c,0);e();)if(f.acceptPosition(a)=== -p)return!0;return!1}var b=g.getOdtDocument(),p=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.enqueueParagraphSplittingOps=function(){var c=f(b.getCursorSelection(l)),d,k=[];0 + + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +ops.OperationTransformMatrix=function(){function g(a){a.position+=a.length;a.length*=-1}function k(a){var c=0>a.length;c&&g(a);return c}function e(a,c){var b=[];a&&["style:parent-style-name","style:next-style-name"].forEach(function(d){a[d]===c&&b.push(d)});return b}function n(a,c){a&&["style:parent-style-name","style:next-style-name"].forEach(function(b){a[b]===c&&delete a[b]})}function m(a){var c={};Object.keys(a).forEach(function(b){c[b]="object"===typeof a[b]?m(a[b]):a[b]});return c}function q(a, +c,b,d){var f,e,g=!1,h=!1,k,m,n=d&&d.attributes?d.attributes.split(","):[];a&&(b||0=c.position+c.length)){f=d?a:c;e=d?c:a;if(a.position!==c.position||a.length!==c.length)n=m(f),q=m(e);c=r(e,f,"style:text-properties");if(c.majorChanged||c.minorChanged)g=[],a=[],h=f.position+f.length,k=e.position+e.length,e.positionh?c.minorChanged&&(n=q,n.position=h,n.length=k-h,a.push(n),e.length=h-e.position):h>k&&c.majorChanged&&(n.position=k,n.length=h-k,g.push(n),f.length=k-f.position),f.setProperties&&b(f.setProperties)&&g.push(f),e.setProperties&&b(e.setProperties)&&a.push(e),d?(h=g,g=a):h=a}return{opSpecsA:h,opSpecsB:g}},InsertText:function(a, +c){c.position<=a.position?a.position+=c.text.length:c.position<=a.position+a.length&&(a.length+=c.text.length);return{opSpecsA:[a],opSpecsB:[c]}},MoveCursor:d,RemoveCursor:d,RemoveStyle:d,RemoveText:function(a,c){var b=a.position+a.length,d=c.position+c.length,f=[a],e=[c];d<=a.position?a.position-=c.length:c.positionc.position)a.position+=c.text.length;else return b?c.position+=a.text.length:a.position+=c.text.length,null;return{opSpecsA:[a],opSpecsB:[c]}},MoveCursor:function(a,c){var b=k(c);a.positionc.position)a.position+=1;else return b?c.position+=a.text.length:a.position+=1,null;return{opSpecsA:[a],opSpecsB:[c]}},UpdateMember:d,UpdateMetadata:d,UpdateParagraphStyle:d},MoveCursor:{MoveCursor:d,RemoveCursor:function(a,c){return{opSpecsA:a.memberid===c.memberid?[]:[a],opSpecsB:[c]}},RemoveMember:d,RemoveStyle:d,RemoveText:function(a,c){var b=k(a),d=a.position+a.length,f=c.position+c.length;f<= +a.position?a.position-=c.length:c.positionb.position)a.position+=1;else if(a.position===b.position)return d?b.position+=1:a.position+=1,null;return{opSpecsA:[a],opSpecsB:[b]}},UpdateMember:d,UpdateMetadata:d,UpdateParagraphStyle:d},UpdateMember:{UpdateMetadata:d,UpdateParagraphStyle:d},UpdateMetadata:{UpdateMetadata:function(a, +c,d){var f,e=[a],g=[c];f=d?a:c;a=d?c:a;q(a.setProperties||null,a.removedProperties||null,f.setProperties||null,f.removedProperties||null);f.setProperties&&b(f.setProperties)||f.removedProperties&&h(f.removedProperties)||(d?e=[]:g=[]);a.setProperties&&b(a.setProperties)||a.removedProperties&&h(a.removedProperties)||(d?g=[]:e=[]);return{opSpecsA:e,opSpecsB:g}},UpdateParagraphStyle:d},UpdateParagraphStyle:{UpdateParagraphStyle:function(a,c,d){var f,e=[a],g=[c];a.styleName===c.styleName&&(f=d?a:c,a=d? +c:a,r(a,f,"style:paragraph-properties"),r(a,f,"style:text-properties"),q(a.setProperties||null,a.removedProperties||null,f.setProperties||null,f.removedProperties||null),f.setProperties&&b(f.setProperties)||f.removedProperties&&h(f.removedProperties)||(d?e=[]:g=[]),a.setProperties&&b(a.setProperties)||a.removedProperties&&h(a.removedProperties)||(d?g=[]:e=[]));return{opSpecsA:e,opSpecsB:g}}}};this.passUnchanged=d;this.extendTransformations=function(a){Object.keys(a).forEach(function(b){var d=a[b], +e,g=f.hasOwnProperty(b);runtime.log((g?"Extending":"Adding")+" map for optypeA: "+b);g||(f[b]={});e=f[b];Object.keys(d).forEach(function(a){var f=e.hasOwnProperty(a);runtime.assert(b<=a,"Wrong order:"+b+", "+a);runtime.log(" "+(f?"Overwriting":"Adding")+" entry for optypeB: "+a);e[a]=d[a]})})};this.transformOpspecVsOpspec=function(a,b){var d=a.optype<=b.optype,e;runtime.log("Crosstransforming:");runtime.log(runtime.toJson(a));runtime.log(runtime.toJson(b));d||(e=a,a=b,b=e);(e=(e=f[a.optype])&&e[b.optype])? +(e=e(a,b,!d),d||null===e||(e={opSpecsA:e.opSpecsB,opSpecsB:e.opSpecsA})):e=null;runtime.log("result:");e?(runtime.log(runtime.toJson(e.opSpecsA)),runtime.log(runtime.toJson(e.opSpecsB))):runtime.log("null");return e}}; // Input 85 /* @@ -2597,49 +2540,13 @@ k,a=[];0 + Copyright (C) 2012 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2674,11 +2581,11 @@ void 0===a.which?String.fromCharCode(a.keyCode):0!==a.which&&0!==a.charCode?Stri @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OperationRouter=function(){};ops.OperationRouter.prototype.setOperationFactory=function(g){};ops.OperationRouter.prototype.setPlaybackFunction=function(g){};ops.OperationRouter.prototype.push=function(g){};ops.OperationRouter.prototype.close=function(g){}; -// Input 89 +ops.TrivialOperationRouter=function(){var g,k;this.setOperationFactory=function(e){g=e};this.setPlaybackFunction=function(e){k=e};this.push=function(e){e.forEach(function(e){e=e.spec();e.timestamp=(new Date).getTime();e=g.create(e);k(e)})};this.close=function(e){e()};this.subscribe=function(e,g){};this.unsubscribe=function(e,g){};this.hasLocalUnsyncedOps=function(){return!1};this.hasSessionHostConnection=function(){return!0}}; +// Input 87 /* - Copyright (C) 2012 KO GmbH + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2713,14 +2620,14 @@ ops.OperationRouter=function(){};ops.OperationRouter.prototype.setOperationFacto @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.TrivialOperationRouter=function(){var g,l;this.setOperationFactory=function(c){g=c};this.setPlaybackFunction=function(c){l=c};this.push=function(c){c.forEach(function(c){c=c.spec();c.timestamp=(new Date).getTime();c=g.create(c);l(c)})};this.close=function(c){c()}}; -// Input 90 -gui.EditInfoHandle=function(g){var l=[],c,m=g.ownerDocument,f=m.documentElement.namespaceURI;this.setEdits=function(g){l=g;var b,p,r,d;c.innerHTML="";for(g=0;ga?(e(1,0),h=e(0.5,1E4-a),r=e(0.2,2E4-a)):1E4<=a&&2E4>a?(e(0.5,0),r=e(0.2,2E4-a)):e(0.2,0)};this.getEdits=function(){return g.getEdits()};this.clearEdits=function(){g.clearEdits(); +q.setEdits([]);b.hasAttributeNS("urn:webodf:names:editinfo","editinfo:memberid")&&b.removeAttributeNS("urn:webodf:names:editinfo","editinfo:memberid")};this.getEditInfo=function(){return g};this.show=function(){b.style.display="block"};this.hide=function(){n.hideHandle();b.style.display="none"};this.showHandle=function(){q.show()};this.hideHandle=function(){q.hide()};this.destroy=function(d){m.removeChild(b);q.destroy(function(b){b?d(b):g.destroy(d)})};(function(){var d=g.getOdtDocument().getDOM(); +b=d.createElementNS(d.documentElement.namespaceURI,"div");b.setAttribute("class","editInfoMarker");b.onmouseover=function(){n.showHandle()};b.onmouseout=function(){n.hideHandle()};m=g.getNode();m.appendChild(b);q=new gui.EditInfoHandle(m);k||n.hide()})()}; +// Input 88 /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2755,14 +2662,24 @@ d=m.createElementNS(f,"span"),d.className="editInfoTime",d.setAttributeNS("urn:w @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("ops.EditInfo");runtime.loadClass("gui.EditInfoHandle"); -gui.EditInfoMarker=function(g,l){function c(c,f){return runtime.setTimeout(function(){b.style.opacity=c},f)}var m=this,f,n,b,p,r;this.addEdit=function(d,f){var a=Date.now()-f;g.addEdit(d,f);n.setEdits(g.getSortedEdits());b.setAttributeNS("urn:webodf:names:editinfo","editinfo:memberid",d);p&&runtime.clearTimeout(p);r&&runtime.clearTimeout(r);1E4>a?(c(1,0),p=c(0.5,1E4-a),r=c(0.2,2E4-a)):1E4<=a&&2E4>a?(c(0.5,0),r=c(0.2,2E4-a)):c(0.2,0)};this.getEdits=function(){return g.getEdits()};this.clearEdits=function(){g.clearEdits(); -n.setEdits([]);b.hasAttributeNS("urn:webodf:names:editinfo","editinfo:memberid")&&b.removeAttributeNS("urn:webodf:names:editinfo","editinfo:memberid")};this.getEditInfo=function(){return g};this.show=function(){b.style.display="block"};this.hide=function(){m.hideHandle();b.style.display="none"};this.showHandle=function(){n.show()};this.hideHandle=function(){n.hide()};this.destroy=function(c){f.removeChild(b);n.destroy(function(b){b?c(b):g.destroy(c)})};(function(){var c=g.getOdtDocument().getDOM(); -b=c.createElementNS(c.documentElement.namespaceURI,"div");b.setAttribute("class","editInfoMarker");b.onmouseover=function(){m.showHandle()};b.onmouseout=function(){m.hideHandle()};f=g.getNode();f.appendChild(b);n=new gui.EditInfoHandle(f);l||m.hide()})()}; -// Input 92 +gui.PlainTextPasteboard=function(g,k){function e(e,g){e.init(g);return e}this.createPasteOps=function(n){var m=g.getCursorPosition(k),q=m,b=[];n.replace(/\r/g,"").split("\n").forEach(function(g){b.push(e(new ops.OpSplitParagraph,{memberid:k,position:q}));q+=1;b.push(e(new ops.OpInsertText,{memberid:k,position:q,text:g}));q+=g.length});b.push(e(new ops.OpRemoveText,{memberid:k,position:m,length:1}));return b}}; +// Input 89 +runtime.loadClass("core.DomUtils");runtime.loadClass("odf.OdfUtils");runtime.loadClass("odf.OdfNodeFilter");runtime.loadClass("gui.SelectionMover"); +gui.SelectionView=function(g){function k(){var a=p.getRootNode();l!==a&&(l=a,u=l.parentNode.parentNode.parentNode,u.appendChild(w),u.appendChild(y),u.appendChild(v))}function e(a,b){a.style.left=b.left+"px";a.style.top=b.top+"px";a.style.width=b.width+"px";a.style.height=b.height+"px"}function n(a){N=a;w.style.display=y.style.display=v.style.display=!0===a?"block":"none"}function m(a){var b=s.getBoundingClientRect(u),c=p.getOdfCanvas().getZoomLevel(),d={};d.top=s.adaptRangeDifferenceToZoomLevel(a.top- +b.top,c);d.left=s.adaptRangeDifferenceToZoomLevel(a.left-b.left,c);d.bottom=s.adaptRangeDifferenceToZoomLevel(a.bottom-b.top,c);d.right=s.adaptRangeDifferenceToZoomLevel(a.right-b.left,c);d.width=s.adaptRangeDifferenceToZoomLevel(a.width,c);d.height=s.adaptRangeDifferenceToZoomLevel(a.height,c);return d}function q(a){a=a.getBoundingClientRect();return Boolean(a&&0!==a.height)}function b(a){var b=t.getTextElements(a,!0,!1),c=a.cloneRange(),d=a.cloneRange();a=a.cloneRange();if(!b.length)return null; +var f;a:{f=0;var e=b[f],g=c.startContainer===e?c.startOffset:0,h=g;c.setStart(e,g);for(c.setEnd(e,h);!q(c);){if(e.nodeType===Node.ELEMENT_NODE&&h + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2797,20 +2714,13 @@ b=c.createElementNS(c.documentElement.namespaceURI,"div");b.setAttribute("class" @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("gui.Caret");runtime.loadClass("ops.EditInfo");runtime.loadClass("gui.EditInfoMarker");gui.SessionViewOptions=function(){this.caretBlinksOnRangeSelect=this.caretAvatarsInitiallyVisible=this.editInfoMarkersInitiallyVisible=!0}; -gui.SessionView=function(){return function(g,l,c,m,f){function n(a,b,c){function d(b,c,e){c=b+'[editinfo|memberid="'+a+'"]'+e+c;a:{var f=u.firstChild;for(b=b+'[editinfo|memberid="'+a+'"]'+e+"{";f;){if(f.nodeType===Node.TEXT_NODE&&0===f.data.indexOf(b)){b=f;break a}f=f.nextSibling}b=null}b?b.data=c:u.appendChild(document.createTextNode(c))}d("div.editInfoMarker","{ background-color: "+c+"; }","");d("span.editInfoColor","{ background-color: "+c+"; }","");d("span.editInfoAuthor",'{ content: "'+b+'"; }', -":before");d("dc|creator","{ background-color: "+c+"; }","");d("div.selectionOverlay","{ background-color: "+c+";}","")}function b(a){var b,c;for(c in v)v.hasOwnProperty(c)&&(b=v[c],a?b.show():b.hide())}function p(a){m.getCarets().forEach(function(b){a?b.showHandle():b.hideHandle()})}function r(a){var b=a.getMemberId();a=a.getProperties();n(b,a.fullName,a.color);l===b&&n("","",a.color)}function d(a){var b=a.getMemberId(),d=c.getOdtDocument().getMember(b).getProperties();m.registerCursor(a,w,t);f.registerCursor(a, -!0);if(a=m.getCaret(b))a.setAvatarImageUrl(d.imageUrl),a.setColor(d.color);runtime.log("+++ View here +++ eagerly created an Caret for '"+b+"'! +++")}function k(a){a=a.getMemberId();var b=f.getSelectionView(l),c=f.getSelectionView(gui.ShadowCursor.ShadowCursorMemberId),d=m.getCaret(l);a===l?(c.hide(),b.show(),d&&d.show()):a===gui.ShadowCursor.ShadowCursorMemberId&&(c.show(),b.hide(),d&&d.hide())}function a(a){f.removeSelectionView(a)}function e(a){var b=a.paragraphElement,d=a.memberId;a=a.timeStamp; -var e,f="",g=b.getElementsByTagNameNS(x,"editinfo")[0];g?(f=g.getAttributeNS(x,"id"),e=v[f]):(f=Math.random().toString(),e=new ops.EditInfo(b,c.getOdtDocument()),e=new gui.EditInfoMarker(e,y),g=b.getElementsByTagNameNS(x,"editinfo")[0],g.setAttributeNS(x,"id",f),v[f]=e);e.addEdit(d,new Date(a))}function h(){D=!0}function q(){s=runtime.getWindow().setInterval(function(){D&&(f.rerenderSelectionViews(),D=!1)},200)}var u,x="urn:webodf:names:editinfo",v={},y=void 0!==g.editInfoMarkersInitiallyVisible? -Boolean(g.editInfoMarkersInitiallyVisible):!0,w=void 0!==g.caretAvatarsInitiallyVisible?Boolean(g.caretAvatarsInitiallyVisible):!0,t=void 0!==g.caretBlinksOnRangeSelect?Boolean(g.caretBlinksOnRangeSelect):!0,s,D=!1;this.showEditInfoMarkers=function(){y||(y=!0,b(y))};this.hideEditInfoMarkers=function(){y&&(y=!1,b(y))};this.showCaretAvatars=function(){w||(w=!0,p(w))};this.hideCaretAvatars=function(){w&&(w=!1,p(w))};this.getSession=function(){return c};this.getCaret=function(a){return m.getCaret(a)}; -this.destroy=function(b){var f=c.getOdtDocument(),g=Object.keys(v).map(function(a){return v[a]});f.unsubscribe(ops.OdtDocument.signalMemberAdded,r);f.unsubscribe(ops.OdtDocument.signalMemberUpdated,r);f.unsubscribe(ops.OdtDocument.signalCursorAdded,d);f.unsubscribe(ops.OdtDocument.signalCursorRemoved,a);f.unsubscribe(ops.OdtDocument.signalParagraphChanged,e);f.unsubscribe(ops.OdtDocument.signalCursorMoved,k);f.unsubscribe(ops.OdtDocument.signalParagraphChanged,h);f.unsubscribe(ops.OdtDocument.signalTableAdded, -h);f.unsubscribe(ops.OdtDocument.signalParagraphStyleModified,h);runtime.getWindow().clearInterval(s);u.parentNode.removeChild(u);(function K(a,c){c?b(c):a + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2845,15 +2755,17 @@ f.appendChild(u)})()}}(); @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("gui.Caret"); -gui.CaretManager=function(g){function l(a){return e.hasOwnProperty(a)?e[a]:null}function c(){return Object.keys(e).map(function(a){return e[a]})}function m(a){a===g.getInputMemberId()&&g.getSession().getOdtDocument().getOdfCanvas().getElement().removeAttribute("tabindex");delete e[a]}function f(a){a=a.getMemberId();a===g.getInputMemberId()&&(a=l(a))&&a.refreshCursorBlinking()}function n(){var a=l(g.getInputMemberId());q=!1;a&&a.ensureVisible()}function b(){var a=l(g.getInputMemberId());a&&(a.handleUpdate(), -q||(q=!0,runtime.setTimeout(n,50)))}function p(a){a.memberId===g.getInputMemberId()&&b()}function r(){var a=l(g.getInputMemberId());a&&a.setFocus()}function d(){var a=l(g.getInputMemberId());a&&a.removeFocus()}function k(){var a=l(g.getInputMemberId());a&&a.show()}function a(){var a=l(g.getInputMemberId());a&&a.hide()}var e={},h=runtime.getWindow(),q=!1;this.registerCursor=function(a,c,d){var f=a.getMemberId();c=new gui.Caret(a,c,d);e[f]=c;f===g.getInputMemberId()?(runtime.log("Starting to track input on new cursor of "+ -f),a.handleUpdate=b,g.getSession().getOdtDocument().getOdfCanvas().getElement().setAttribute("tabindex",-1),g.getEventManager().focus()):a.handleUpdate=c.handleUpdate;return c};this.getCaret=l;this.getCarets=c;this.destroy=function(b){var l=g.getSession().getOdtDocument(),n=g.getEventManager(),q=c();l.unsubscribe(ops.OdtDocument.signalParagraphChanged,p);l.unsubscribe(ops.OdtDocument.signalCursorMoved,f);l.unsubscribe(ops.OdtDocument.signalCursorRemoved,m);n.unsubscribe("focus",r);n.unsubscribe("blur", -d);h.removeEventListener("focus",k,!1);h.removeEventListener("blur",a,!1);(function t(a,c){c?b(c):a + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2888,9 +2800,40 @@ d);h.removeEventListener("focus",k,!1);h.removeEventListener("blur",a,!1);(funct @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -gui.UndoManager=function(){};gui.UndoManager.prototype.subscribe=function(g,l){};gui.UndoManager.prototype.unsubscribe=function(g,l){};gui.UndoManager.prototype.setOdtDocument=function(g){};gui.UndoManager.prototype.saveInitialState=function(){};gui.UndoManager.prototype.resetInitialState=function(){};gui.UndoManager.prototype.setPlaybackFunction=function(g){};gui.UndoManager.prototype.hasUndoStates=function(){};gui.UndoManager.prototype.hasRedoStates=function(){}; -gui.UndoManager.prototype.moveForward=function(g){};gui.UndoManager.prototype.moveBackward=function(g){};gui.UndoManager.prototype.onOperationExecuted=function(g){};gui.UndoManager.signalUndoStackChanged="undoStackChanged";gui.UndoManager.signalUndoStateCreated="undoStateCreated";gui.UndoManager.signalUndoStateModified="undoStateModified";(function(){return gui.UndoManager})(); -// Input 95 +runtime.loadClass("ops.TrivialOperationRouter");runtime.loadClass("ops.OperationFactory");runtime.loadClass("ops.OdtDocument"); +ops.Session=function(g){var k=new ops.OperationFactory,e=new ops.OdtDocument(g),n=null;this.setOperationFactory=function(e){k=e;n&&n.setOperationFactory(k)};this.setOperationRouter=function(g){n=g;g.setPlaybackFunction(function(g){return g.execute(e)?(e.emit(ops.OdtDocument.signalOperationExecuted,g),!0):!1});g.setOperationFactory(k)};this.getOperationFactory=function(){return k};this.getOdtDocument=function(){return e};this.enqueue=function(e){n.push(e)};this.close=function(g){n.close(function(k){k? +g(k):e.close(g)})};this.destroy=function(g){e.destroy(g)};this.setOperationRouter(new ops.TrivialOperationRouter)}; +// Input 93 +/* + + Copyright (C) 2013 KO GmbH + + @licstart + This file is part of WebODF. + + WebODF 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. + + WebODF is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with WebODF. If not, see . + @licend + + @source: http://www.webodf.org/ + @source: https://github.com/kogmbh/WebODF/ +*/ +runtime.loadClass("core.EventNotifier");runtime.loadClass("core.PositionFilter");runtime.loadClass("ops.Session");runtime.loadClass("ops.OpAddAnnotation");runtime.loadClass("ops.OpRemoveAnnotation");runtime.loadClass("gui.SelectionMover"); +gui.AnnotationController=function(g,k){function e(){var f=b.getCursor(k),f=f&&f.getNode(),a=!1;if(f){a:{for(a=b.getRootNode();f&&f!==a;){if(f.namespaceURI===d&&"annotation"===f.localName){f=!0;break a}f=f.parentNode}f=!1}a=!f}a!==h&&(h=a,r.emit(gui.AnnotationController.annotatableChanged,h))}function n(b){b.getMemberId()===k&&e()}function m(b){b===k&&e()}function q(b){b.getMemberId()===k&&e()}var b=g.getOdtDocument(),h=!1,r=new core.EventNotifier([gui.AnnotationController.annotatableChanged]),d=odf.Namespaces.officens; +this.isAnnotatable=function(){return h};this.addAnnotation=function(){var d=new ops.OpAddAnnotation,a=b.getCursorSelection(k),c=a.length,a=a.position;h&&(a=0<=c?a:a+c,c=Math.abs(c),d.init({memberid:k,position:a,length:c,name:k+Date.now()}),g.enqueue([d]))};this.removeAnnotation=function(d){var a,c;a=b.convertDomPointToCursorStep(d,0)+1;c=b.convertDomPointToCursorStep(d,d.childNodes.length);d=new ops.OpRemoveAnnotation;d.init({memberid:k,position:a,length:c-a});c=new ops.OpMoveCursor;c.init({memberid:k, +position:0 @@ -2928,9 +2871,14 @@ gui.UndoManager.prototype.moveForward=function(g){};gui.UndoManager.prototype.mo @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -gui.UndoStateRules=function(){function g(c){return c.spec().optype}function l(c){return c.isEdit}this.getOpType=g;this.isEditOperation=l;this.isPartOfOperationSet=function(c,m){if(c.isEdit){if(0===m.length)return!0;var f;if(f=m[m.length-1].isEdit)a:{f=m.filter(l);var n=g(c),b;b:switch(n){case "RemoveText":case "InsertText":b=!0;break b;default:b=!1}if(b&&n===g(f[0])){if(1===f.length){f=!0;break a}n=f[f.length-2].spec().position;f=f[f.length-1].spec().position;b=c.spec().position;if(f===b-(f-n)){f= -!0;break a}}f=!1}return f}return!0}}; -// Input 96 +runtime.loadClass("core.EventNotifier");runtime.loadClass("core.Utils");runtime.loadClass("odf.OdfUtils");runtime.loadClass("ops.OpAddStyle");runtime.loadClass("ops.OpSetParagraphStyle");runtime.loadClass("gui.StyleHelper"); +gui.DirectParagraphStyler=function(g,k,e){function n(){function a(b,d,e){b!==d&&(void 0===c&&(c={}),c[e]=d);return d}var b=l.getCursor(k),b=b&&b.getSelectedRange(),c;v=a(v,b?w.isAlignedLeft(b):!1,"isAlignedLeft");t=a(t,b?w.isAlignedCenter(b):!1,"isAlignedCenter");s=a(s,b?w.isAlignedRight(b):!1,"isAlignedRight");N=a(N,b?w.isAlignedJustified(b):!1,"isAlignedJustified");c&&y.emit(gui.DirectParagraphStyler.paragraphStylingChanged,c)}function m(a){a.getMemberId()===k&&n()}function q(a){a===k&&n()}function b(a){a.getMemberId()=== +k&&n()}function h(){n()}function r(a){var b=l.getCursor(k);b&&l.getParagraphElement(b.getNode())===a.paragraphElement&&n()}function d(a){return a===ops.StepsTranslator.NEXT_STEP}function f(a){var b=l.getCursor(k).getSelectedRange(),b=B.getParagraphElements(b),c=l.getFormatting();b.forEach(function(b){var f=l.convertDomPointToCursorStep(b,0,d),h=b.getAttributeNS(odf.Namespaces.textns,"style-name");b=e.generateStyleName();var m;h&&(m=c.createDerivedStyleObject(h,"paragraph",{}));m=a(m||{});h=new ops.OpAddStyle; +h.init({memberid:k,styleName:b,styleFamily:"paragraph",isAutomaticStyle:!0,setProperties:m});m=new ops.OpSetParagraphStyle;m.init({memberid:k,styleName:b,position:f});g.enqueue([h,m])})}function a(a){f(function(b){return u.mergeObjects(b,a)})}function c(b){a({"style:paragraph-properties":{"fo:text-align":b}})}function p(a,b){var c=l.getFormatting().getDefaultTabStopDistance(),d=b["style:paragraph-properties"],d=(d=d&&d["fo:margin-left"])&&B.parseLength(d);return u.mergeObjects(b,{"style:paragraph-properties":{"fo:margin-left":d&& +d.unit===c.unit?d.value+a*c.value+d.unit:a*c.value+c.unit}})}var l=g.getOdtDocument(),u=new core.Utils,B=new odf.OdfUtils,w=new gui.StyleHelper(l.getFormatting()),y=new core.EventNotifier([gui.DirectParagraphStyler.paragraphStylingChanged]),v,t,s,N;this.isAlignedLeft=function(){return v};this.isAlignedCenter=function(){return t};this.isAlignedRight=function(){return s};this.isAlignedJustified=function(){return N};this.alignParagraphLeft=function(){c("left");return!0};this.alignParagraphCenter=function(){c("center"); +return!0};this.alignParagraphRight=function(){c("right");return!0};this.alignParagraphJustified=function(){c("justify");return!0};this.indent=function(){f(p.bind(null,1));return!0};this.outdent=function(){f(p.bind(null,-1));return!0};this.subscribe=function(a,b){y.subscribe(a,b)};this.unsubscribe=function(a,b){y.unsubscribe(a,b)};this.destroy=function(a){l.unsubscribe(ops.OdtDocument.signalCursorAdded,m);l.unsubscribe(ops.OdtDocument.signalCursorRemoved,q);l.unsubscribe(ops.OdtDocument.signalCursorMoved, +b);l.unsubscribe(ops.OdtDocument.signalParagraphStyleModified,h);l.unsubscribe(ops.OdtDocument.signalParagraphChanged,r);a()};l.subscribe(ops.OdtDocument.signalCursorAdded,m);l.subscribe(ops.OdtDocument.signalCursorRemoved,q);l.subscribe(ops.OdtDocument.signalCursorMoved,b);l.subscribe(ops.OdtDocument.signalParagraphStyleModified,h);l.subscribe(ops.OdtDocument.signalParagraphChanged,r);n()};gui.DirectParagraphStyler.paragraphStylingChanged="paragraphStyling/changed";(function(){return gui.DirectParagraphStyler})(); +// Input 95 /* Copyright (C) 2013 KO GmbH @@ -2968,27 +2916,23 @@ gui.UndoStateRules=function(){function g(c){return c.spec().optype}function l(c) @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils");runtime.loadClass("gui.UndoManager");runtime.loadClass("gui.UndoStateRules"); -gui.TrivialUndoManager=function(g){function l(){u.emit(gui.UndoManager.signalUndoStackChanged,{undoAvailable:b.hasUndoStates(),redoAvailable:b.hasRedoStates()})}function c(){e!==d&&e!==h[h.length-1]&&h.push(e)}function m(a){var b=a.previousSibling||a.nextSibling;a.parentNode.removeChild(a);p.normalizeTextNodes(b)}function f(a){return Object.keys(a).map(function(b){return a[b]})}function n(b){function c(a){var b=a.spec();if(g[b.memberid])switch(b.optype){case "AddCursor":d[b.memberid]||(d[b.memberid]= -a,delete g[b.memberid],h-=1);break;case "MoveCursor":e[b.memberid]||(e[b.memberid]=a)}}var d={},e={},g={},h,k=b.pop();a.getCursors().forEach(function(a){g[a.getMemberId()]=!0});for(h=Object.keys(g).length;k&&0c.width&&(p=c.width/f);a>c.height&&(l=c.height/a);p=Math.min(p,l);c=f*p;f=a*p;l=q.getOdfCanvas().odfContainer().rootElement.styles;a=r.toLowerCase();var p=n.hasOwnProperty(a)?n[a]:null,u;a=[];runtime.assert(null!==p,"Image type is not supported: "+r);p="Pictures/"+e.generateImageName()+p;u=new ops.OpSetBlob;u.init({memberid:k,filename:p,mimetype:r,content:d});a.push(u);b.getStyleElement("Graphics","graphic",[l])||(r=new ops.OpAddStyle,r.init({memberid:k,styleName:"Graphics",styleFamily:"graphic", +isAutomaticStyle:!1,setProperties:{"style:graphic-properties":{"text:anchor-type":"paragraph","svg:x":"0cm","svg:y":"0cm","style:wrap":"dynamic","style:number-wrapped-paragraphs":"no-limit","style:wrap-contour":"false","style:vertical-pos":"top","style:vertical-rel":"paragraph","style:horizontal-pos":"center","style:horizontal-rel":"paragraph"}}}),a.push(r));r=e.generateStyleName();d=new ops.OpAddStyle;d.init({memberid:k,styleName:r,styleFamily:"graphic",isAutomaticStyle:!0,setProperties:{"style:parent-style-name":"Graphics", +"style:graphic-properties":{"style:vertical-pos":"top","style:vertical-rel":"baseline","style:horizontal-pos":"center","style:horizontal-rel":"paragraph","fo:background-color":"transparent","style:background-transparency":"100%","style:shadow":"none","style:mirror":"none","fo:clip":"rect(0cm, 0cm, 0cm, 0cm)","draw:luminance":"0%","draw:contrast":"0%","draw:red":"0%","draw:green":"0%","draw:blue":"0%","draw:gamma":"100%","draw:color-inversion":"false","draw:image-opacity":"100%","draw:color-mode":"standard"}}}); +a.push(d);u=new ops.OpInsertImage;u.init({memberid:k,position:q.getCursorPosition(k),filename:p,frameWidth:c+"cm",frameHeight:f+"cm",frameStyleName:r,frameName:e.generateFrameName()});a.push(u);g.enqueue(a)}}; // Input 97 -runtime.loadClass("core.DomUtils");runtime.loadClass("odf.OdfUtils");runtime.loadClass("odf.OdfNodeFilter");runtime.loadClass("gui.SelectionMover"); -gui.SelectionView=function(g){function l(){var a=h.getRootNode();q!==a&&(q=a,u=q.parentNode.parentNode.parentNode,u.appendChild(v),u.appendChild(y),u.appendChild(w))}function c(a,b){a.style.left=b.left+"px";a.style.top=b.top+"px";a.style.width=b.width+"px";a.style.height=b.height+"px"}function m(a){D=a;v.style.display=y.style.display=w.style.display=!0===a?"block":"none"}function f(a){var b=s.getBoundingClientRect(u),c=h.getOdfCanvas().getZoomLevel(),d={};d.top=s.adaptRangeDifferenceToZoomLevel(a.top- -b.top,c);d.left=s.adaptRangeDifferenceToZoomLevel(a.left-b.left,c);d.bottom=s.adaptRangeDifferenceToZoomLevel(a.bottom-b.top,c);d.right=s.adaptRangeDifferenceToZoomLevel(a.right-b.left,c);d.width=s.adaptRangeDifferenceToZoomLevel(a.width,c);d.height=s.adaptRangeDifferenceToZoomLevel(a.height,c);return d}function n(a){a=a.getBoundingClientRect();return Boolean(a&&0!==a.height)}function b(a){var b=t.getTextElements(a,!0,!1),c=a.cloneRange(),d=a.cloneRange();a=a.cloneRange();if(!b.length)return null; -var e;a:{e=0;var f=b[e],g=c.startContainer===f?c.startOffset:0,h=g;c.setStart(f,g);for(c.setEnd(f,h);!n(c);){if(f.nodeType===Node.ELEMENT_NODE&&h @@ -3026,9 +2970,41 @@ a);v.className=y.className=w.className="selectionOverlay";g.getOdtDocument().sub @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("gui.SelectionView"); -gui.SelectionViewManager=function(){function g(){return Object.keys(l).map(function(c){return l[c]})}var l={};this.getSelectionView=function(c){return l.hasOwnProperty(c)?l[c]:null};this.getSelectionViews=g;this.removeSelectionView=function(c){l.hasOwnProperty(c)&&(l[c].destroy(function(){}),delete l[c])};this.hideSelectionView=function(c){l.hasOwnProperty(c)&&l[c].hide()};this.showSelectionView=function(c){l.hasOwnProperty(c)&&l[c].show()};this.rerenderSelectionViews=function(){Object.keys(l).forEach(function(c){l[c].visible()&& -l[c].rerender()})};this.registerCursor=function(c,g){var f=c.getMemberId(),n=new gui.SelectionView(c);g?n.show():n.hide();return l[f]=n};this.destroy=function(c){var l=g();(function n(b,g){g?c(g):bb.length&&(b.position+=b.length,b.length=-b.length);return b}function q(e,d){var f=new core.PositionFilterChain,a=gui.SelectionMover.createPositionIterator(b.getRootElement(e)),c=d?a.nextPosition:a.previousPosition;f.addFilter("BaseFilter",b.getPositionFilter());f.addFilter("RootFilter",b.createRootFilter(k));for(a.setUnfilteredPosition(e,0);c();)if(f.acceptPosition(a)=== +h)return!0;return!1}var b=g.getOdtDocument(),h=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.enqueueParagraphSplittingOps=function(){var e=m(b.getCursorSelection(k)),d,f=[];0=e;e+=1){b=a.container();c=a.unfilteredDomOffset();if(b.nodeType===Node.TEXT_NODE&&" "===b.data[c]&&d.isSignificantWhitespace(b,c)){runtime.assert(" "===b.data[c],"upgradeWhitespaceToElement: textNode.data[offset] should be a literal space");var f=b.ownerDocument.createElementNS(odf.Namespaces.textns, -"text:s");f.appendChild(b.ownerDocument.createTextNode(" "));b.deleteData(c,1);0 span {\n /* IMPORTANT: when changing these values ensure DEFAULT_CARET_TOP and DEFAULT_CARET_HEIGHT\n in Caret.js remain in sync */\n display: inline;\n position: absolute;\n top: 5%; /* push down the caret; 0px can do the job, 5% looks better, 10% is a bit over */\n height: 1em;\n border-left: 2px solid black;\n outline: none;\n}\n\ncursor|cursor > div {\n padding: 3px;\n box-shadow: 0px 0px 5px rgba(50, 50, 50, 0.75);\n border: none !important;\n border-radius: 5px;\n opacity: 0.3;\n}\n\ncursor|cursor > div > img {\n border-radius: 5px;\n}\n\ncursor|cursor > div.active {\n opacity: 0.8;\n}\n\ncursor|cursor > div:after {\n content: ' ';\n position: absolute;\n width: 0px;\n height: 0px;\n border-style: solid;\n border-width: 8.7px 5px 0 5px;\n border-color: black transparent transparent transparent;\n\n top: 100%;\n left: 43%;\n}\n\n\n.editInfoMarker {\n position: absolute;\n width: 10px;\n height: 100%;\n left: -20px;\n opacity: 0.8;\n top: 0;\n border-radius: 5px;\n background-color: transparent;\n box-shadow: 0px 0px 5px rgba(50, 50, 50, 0.75);\n}\n.editInfoMarker:hover {\n box-shadow: 0px 0px 8px rgba(0, 0, 0, 1);\n}\n\n.editInfoHandle {\n position: absolute;\n background-color: black;\n padding: 5px;\n border-radius: 5px;\n opacity: 0.8;\n box-shadow: 0px 0px 5px rgba(50, 50, 50, 0.75);\n bottom: 100%;\n margin-bottom: 10px;\n z-index: 3;\n left: -25px;\n}\n.editInfoHandle:after {\n content: ' ';\n position: absolute;\n width: 0px;\n height: 0px;\n border-style: solid;\n border-width: 8.7px 5px 0 5px;\n border-color: black transparent transparent transparent;\n\n top: 100%;\n left: 5px;\n}\n.editInfo {\n font-family: sans-serif;\n font-weight: normal;\n font-style: normal;\n text-decoration: none;\n color: white;\n width: 100%;\n height: 12pt;\n}\n.editInfoColor {\n float: left;\n width: 10pt;\n height: 10pt;\n border: 1px solid white;\n}\n.editInfoAuthor {\n float: left;\n margin-left: 5pt;\n font-size: 10pt;\n text-align: left;\n height: 12pt;\n line-height: 12pt;\n}\n.editInfoTime {\n float: right;\n margin-left: 30pt;\n font-size: 8pt;\n font-style: italic;\n color: yellow;\n height: 12pt;\n line-height: 12pt;\n}\n\n.annotationWrapper {\n display: inline;\n position: relative;\n}\n\n.annotationRemoveButton:before {\n content: '\u00d7';\n color: white;\n padding: 5px;\n line-height: 1em;\n}\n\n.annotationRemoveButton {\n width: 20px;\n height: 20px;\n border-radius: 10px;\n background-color: black;\n box-shadow: 0px 0px 5px rgba(50, 50, 50, 0.75);\n position: absolute;\n top: -10px;\n left: -10px;\n z-index: 3;\n text-align: center;\n font-family: sans-serif;\n font-style: normal;\n font-weight: normal;\n text-decoration: none;\n font-size: 15px;\n}\n.annotationRemoveButton:hover {\n cursor: pointer;\n box-shadow: 0px 0px 5px rgba(0, 0, 0, 1);\n}\n\n.annotationNote {\n width: 4cm;\n position: absolute;\n display: inline;\n z-index: 10;\n}\n.annotationNote > office|annotation {\n display: block;\n text-align: left;\n}\n\n.annotationConnector {\n position: absolute;\n display: inline;\n z-index: 2;\n border-top: 1px dashed brown;\n}\n.annotationConnector.angular {\n -moz-transform-origin: left top;\n -webkit-transform-origin: left top;\n -ms-transform-origin: left top;\n transform-origin: left top;\n}\n.annotationConnector.horizontal {\n left: 0;\n}\n.annotationConnector.horizontal:before {\n content: '';\n display: inline;\n position: absolute;\n width: 0px;\n height: 0px;\n border-style: solid;\n border-width: 8.7px 5px 0 5px;\n border-color: brown transparent transparent transparent;\n top: -1px;\n left: -5px;\n}\n\noffice|annotation {\n width: 100%;\n height: 100%;\n display: none;\n background: rgb(198, 238, 184);\n background: -moz-linear-gradient(90deg, rgb(198, 238, 184) 30%, rgb(180, 196, 159) 100%);\n background: -webkit-linear-gradient(90deg, rgb(198, 238, 184) 30%, rgb(180, 196, 159) 100%);\n background: -o-linear-gradient(90deg, rgb(198, 238, 184) 30%, rgb(180, 196, 159) 100%);\n background: -ms-linear-gradient(90deg, rgb(198, 238, 184) 30%, rgb(180, 196, 159) 100%);\n background: linear-gradient(180deg, rgb(198, 238, 184) 30%, rgb(180, 196, 159) 100%);\n box-shadow: 0 3px 4px -3px #ccc;\n}\n\noffice|annotation > dc|creator {\n display: block;\n font-size: 10pt;\n font-weight: normal;\n font-style: normal;\n font-family: sans-serif;\n color: white;\n background-color: brown;\n padding: 4px;\n}\noffice|annotation > dc|date {\n display: block;\n font-size: 10pt;\n font-weight: normal;\n font-style: normal;\n font-family: sans-serif;\n border: 4px solid transparent;\n}\noffice|annotation > text|list {\n display: block;\n padding: 5px;\n}\n\n/* This is very temporary CSS. This must go once\n * we start bundling webodf-default ODF styles for annotations.\n */\noffice|annotation text|p {\n font-size: 10pt;\n color: black;\n font-weight: normal;\n font-style: normal;\n text-decoration: none;\n font-family: sans-serif;\n}\n\ndc|*::selection {\n background: transparent;\n}\ndc|*::-moz-selection {\n background: transparent;\n}\n\n#annotationsPane {\n background-color: #EAEAEA;\n width: 4cm;\n height: 100%;\n display: none;\n position: absolute;\n outline: 1px solid #ccc;\n}\n\n.annotationHighlight {\n background-color: yellow;\n position: relative;\n}\n\n.selectionOverlay {\n position: absolute;\n z-index: 15;\n opacity: 0.2;\n pointer-events: none;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n}\n\n#imageSelector {\n display: none;\n position: absolute;\n border-style: solid;\n border-color: black;\n}\n\n#imageSelector > div {\n width: 5px;\n height: 5px;\n display: block;\n position: absolute;\n border: 1px solid black;\n background-color: #ffffff;\n}\n\n#imageSelector > .topLeft {\n top: -4px;\n left: -4px;\n}\n\n#imageSelector > .topRight {\n top: -4px;\n right: -4px;\n}\n\n#imageSelector > .bottomRight {\n right: -4px;\n bottom: -4px;\n}\n\n#imageSelector > .bottomLeft {\n bottom: -4px;\n left: -4px;\n}\n\n#imageSelector > .topMiddle {\n top: -4px;\n left: 50%;\n margin-left: -2.5px; /* half of the width defined in #imageSelector > div */\n}\n\n#imageSelector > .rightMiddle {\n top: 50%;\n right: -4px;\n margin-top: -2.5px; /* half of the height defined in #imageSelector > div */\n}\n\n#imageSelector > .bottomMiddle {\n bottom: -4px;\n left: 50%;\n margin-left: -2.5px; /* half of the width defined in #imageSelector > div */\n}\n\n#imageSelector > .leftMiddle {\n top: 50%;\n left: -4px;\n margin-top: -2.5px; /* half of the height defined in #imageSelector > div */\n}\n"; -// Input 102 -var webodf_version="0.4.2-1451-gc9cf878";