diff --git a/css/3rdparty/webodf/editor.css b/css/3rdparty/webodf/editor.css index 23f86ee9..6f23a0c8 100644 --- a/css/3rdparty/webodf/editor.css +++ b/css/3rdparty/webodf/editor.css @@ -25,13 +25,18 @@ body.claro, #mainContainer { #toolbar { overflow: hidden; + top: 0; + left: 0; + right: 0; + position: absolute; + z-index: 5; + box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25); } #container { text-align: center; background-color: #ddd; overflow: auto; - position: absolute; top: 30px; bottom: 0; @@ -40,7 +45,6 @@ body.claro, #mainContainer { } #canvas { - box-shadow: 0px 0px 20px #aaa; margin-top: 30px; margin-left: 10px; margin-right: 10px; @@ -50,8 +54,16 @@ body.claro, #mainContainer { -webkit-transform-origin: top center; -moz-transform-origin: top center; -o-transform-origin: top center; - overflow: hidden; + overflow: visible; +} +/* Add shadow to the sizer and not the canvas, + * so that it will follow the smooth zooming + * of the slider and not have to be updated + * every time a gesture ends + */ +#canvas > div { + box-shadow: 0px 0px 20px #aaa; border: 1px solid #ccc; } @@ -241,7 +253,7 @@ div.memberListLabel[fullname]:before { text-align: center; } -cursor div { +cursor .handle { margin-top: 5px; padding-top: 3px; margin-left: auto; @@ -269,11 +281,11 @@ cursor img { margin: auto; } -cursor div.active { +cursor .handle.active { opacity: 0.8; } -cursor div:after { +cursor .handle:after { content: ' '; position: absolute; width: 0px; diff --git a/js/3rdparty/webodf/editor/Editor.js b/js/3rdparty/webodf/editor/Editor.js index 86840e2f..ce2d1b92 100644 --- a/js/3rdparty/webodf/editor/Editor.js +++ b/js/3rdparty/webodf/editor/Editor.js @@ -317,6 +317,7 @@ define("webodf/editor/Editor", [ runtime.assert(editorSession, "editorSession should exist here."); tools.setEditorSession(editorSession); + editorSession.sessionController.insertLocalCursor(); editorSession.sessionController.startEditing(); }; @@ -330,6 +331,7 @@ define("webodf/editor/Editor", [ tools.setEditorSession(undefined); editorSession.sessionController.endEditing(); + editorSession.sessionController.removeLocalCursor(); }; /** @@ -364,6 +366,40 @@ define("webodf/editor/Editor", [ eventNotifier.unsubscribe(eventid, listener); }; + /** + * Applies a CSS transformation to the toolbar + * to ensure that if there is a body-scroll, + * the toolbar remains visible at the top of + * the screen. + * The bodyscroll quirk has been observed on + * iOS, generally when the keyboard appears. + * But this workaround should function on + * other platforms that exhibit this behaviour + * as well. + * @return {undefined} + */ + function translateToolbar() { + var bar = document.getElementById('toolbar'), + y = document.body.scrollTop; + + bar.style.WebkitTransformOrigin = "center top"; + bar.style.WebkitTransform = 'translateY(' + y + 'px)'; + } + + /** + * FIXME: At the moment both the toolbar and the canvas + * container are absolutely positioned. Changing them to + * relative positioning to ensure that they do not overlap + * causes scrollbars *within* the container to disappear. + * Not sure why this happens, and a proper CSS fix has not + * been found yet, so for now we need to reposition + * the container using Js. + * @return {undefined} + */ + function repositionContainer() { + document.getElementById('container').style.top = document.getElementById('toolbar').getBoundingClientRect().height + 'px'; + } + /** * @param {!function(!Object=)} callback, passing an error object in case of error * @return {undefined} @@ -371,6 +407,11 @@ define("webodf/editor/Editor", [ this.destroy = function (callback) { var destroyMemberListView = memberListView ? memberListView.destroy : function(cb) { cb(); }; + window.removeEventListener('scroll', translateToolbar); + window.removeEventListener('focusout', translateToolbar); + window.removeEventListener('touchmove', translateToolbar); + window.removeEventListener('resize', repositionContainer); + // TODO: decide if some forced close should be done here instead of enforcing proper API usage runtime.assert(!session, "session should not exist here."); @@ -407,10 +448,12 @@ define("webodf/editor/Editor", [ var editorPane, memberListPane, inviteButton, canvasElement = document.getElementById("canvas"), + container = document.getElementById('container'), memberListElement = document.getElementById('memberList'), collabEditing = Boolean(server), directParagraphStylingEnabled = (! collabEditing) || args.unstableFeaturesEnabled, imageInsertingEnabled = (! collabEditing) || args.unstableFeaturesEnabled, + hyperlinkEditingEnabled = (! collabEditing) || args.unstableFeaturesEnabled, // annotations not yet properly supported for OT annotationsEnabled = (! collabEditing) || args.unstableFeaturesEnabled, // undo manager is not yet integrated with collaboration @@ -471,18 +514,19 @@ define("webodf/editor/Editor", [ } tools = new Tools({ - onToolDone: setFocusToOdfCanvas, - loadOdtFile: loadOdtFile, - saveOdtFile: saveOdtFile, - close: close, - directParagraphStylingEnabled: directParagraphStylingEnabled, - imageInsertingEnabled: imageInsertingEnabled, - annotationsEnabled: annotationsEnabled, - undoRedoEnabled: undoRedoEnabled - }); + onToolDone: setFocusToOdfCanvas, + loadOdtFile: loadOdtFile, + saveOdtFile: saveOdtFile, + close: close, + directParagraphStylingEnabled: directParagraphStylingEnabled, + imageInsertingEnabled: imageInsertingEnabled, + hyperlinkEditingEnabled: hyperlinkEditingEnabled, + annotationsEnabled: annotationsEnabled, + undoRedoEnabled: undoRedoEnabled + }); odfCanvas = new odf.OdfCanvas(canvasElement); - odfCanvas.enableAnnotations(annotationsEnabled); + odfCanvas.enableAnnotations(annotationsEnabled, true); odfCanvas.addListener("statereadychange", function () { var viewOptions = { @@ -496,7 +540,8 @@ define("webodf/editor/Editor", [ editorSession = new EditorSession(session, pendingMemberId, { viewOptions: viewOptions, directParagraphStylingEnabled: directParagraphStylingEnabled, - imageInsertingEnabled: imageInsertingEnabled + imageInsertingEnabled: imageInsertingEnabled, + hyperlinkEditingEnabled: hyperlinkEditingEnabled }); if (undoRedoEnabled) { editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager()); @@ -512,6 +557,13 @@ define("webodf/editor/Editor", [ pendingEditorReadyCallback = null; pendingMemberId = null; }); + + repositionContainer(); + + window.addEventListener('scroll', translateToolbar); + window.addEventListener('focusout', translateToolbar); + window.addEventListener('touchmove', translateToolbar); + window.addEventListener('resize', repositionContainer); } init(); diff --git a/js/3rdparty/webodf/editor/EditorSession.js b/js/3rdparty/webodf/editor/EditorSession.js index 91d28b43..5c33e5af 100644 --- a/js/3rdparty/webodf/editor/EditorSession.js +++ b/js/3rdparty/webodf/editor/EditorSession.js @@ -43,10 +43,6 @@ define("webodf/editor/EditorSession", [ ], function (fontsCSS) { // fontsCSS is retrieved as a string, using dojo's text retrieval AMD plugin "use strict"; - runtime.libraryPaths = function () { - return [ "../../webodf/lib" ]; - }; - runtime.loadClass("core.DomUtils"); runtime.loadClass("odf.OdfUtils"); runtime.loadClass("ops.OdtDocument"); @@ -54,11 +50,13 @@ define("webodf/editor/EditorSession", [ runtime.loadClass("ops.Session"); runtime.loadClass("odf.Namespaces"); runtime.loadClass("odf.OdfCanvas"); + runtime.loadClass("odf.OdfUtils"); runtime.loadClass("gui.CaretManager"); runtime.loadClass("gui.Caret"); runtime.loadClass("gui.SessionController"); runtime.loadClass("gui.SessionView"); runtime.loadClass("gui.TrivialUndoManager"); + runtime.loadClass("gui.SvgSelectionView"); runtime.loadClass("gui.SelectionViewManager"); runtime.loadClass("core.EventNotifier"); runtime.loadClass("gui.ShadowCursor"); @@ -208,7 +206,7 @@ define("webodf/editor/EditorSession", [ function trackCurrentParagraph(info) { var cursor = odtDocument.getCursor(localMemberId), range = cursor && cursor.getSelectedRange(), - paragraphRange = odtDocument.getDOM().createRange(); + paragraphRange = odtDocument.getDOMDocument().createRange(); paragraphRange.selectNode(info.paragraphElement); if ((range && domUtils.rangesIntersect(range, paragraphRange)) || info.paragraphElement === currentParagraphNode) { self.emit(EditorSession.signalParagraphChanged, info); @@ -312,7 +310,7 @@ define("webodf/editor/EditorSession", [ /** * Round the step up to the next step * @param {!number} step - * @returns {!boolean} + * @return {!boolean} */ function roundUp(step) { return step === ops.StepsTranslator.NEXT_STEP; @@ -387,14 +385,14 @@ define("webodf/editor/EditorSession", [ return formatting.isStyleUsed(styleElement); }; - function getDefaultParagraphStyleAttributes () { + function getDefaultParagraphStyleAttributes() { var styleNode = formatting.getDefaultStyleElement('paragraph'); if (styleNode) { return formatting.getInheritedStyleAttributes(styleNode); } return null; - }; + } /** * Returns the attributes of a given paragraph style name @@ -522,22 +520,30 @@ define("webodf/editor/EditorSession", [ return array; }; + this.getSelectedHyperlinks = function () { + var cursor = odtDocument.getCursor(localMemberId); + // no own cursor yet/currently added? + if (!cursor) { + return []; + } + return odfUtils.getHyperlinkElements(cursor.getSelectedRange()); + }; + + this.getSelectedRange = function () { + var cursor = odtDocument.getCursor(localMemberId); + return cursor && cursor.getSelectedRange(); + }; + function undoStackModified(e) { self.emit(EditorSession.signalUndoStackChanged, e); } - this.hasUndoManager = function () { - return Boolean(self.sessionController.getUndoManager()); - }; - this.undo = function () { - var undoManager = self.sessionController.getUndoManager(); - undoManager.moveBackward(1); + self.sessionController.undo(); }; this.redo = function () { - var undoManager = self.sessionController.getUndoManager(); - undoManager.moveForward(1); + self.sessionController.redo(); }; /** @@ -548,8 +554,8 @@ define("webodf/editor/EditorSession", [ * @param {!number} height */ this.insertImage = function (mimetype, content, width, height) { - self.sessionController.getTextManipulator().removeCurrentSelection(); - self.sessionController.getImageManager().insertImage(mimetype, content, width, height); + self.sessionController.getTextController().removeCurrentSelection(); + self.sessionController.getImageController().insertImage(mimetype, content, width, height); }; /** @@ -569,12 +575,12 @@ define("webodf/editor/EditorSession", [ head.removeChild(fontStyles); - odtDocument.unsubscribe(ops.OdtDocument.signalMemberAdded, onMemberAdded); - odtDocument.unsubscribe(ops.OdtDocument.signalMemberUpdated, onMemberUpdated); - odtDocument.unsubscribe(ops.OdtDocument.signalMemberRemoved, onMemberRemoved); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); + odtDocument.unsubscribe(ops.Document.signalMemberAdded, onMemberAdded); + odtDocument.unsubscribe(ops.Document.signalMemberUpdated, onMemberUpdated); + odtDocument.unsubscribe(ops.Document.signalMemberRemoved, onMemberRemoved); + odtDocument.unsubscribe(ops.Document.signalCursorAdded, onCursorAdded); + odtDocument.unsubscribe(ops.Document.signalCursorRemoved, onCursorRemoved); + odtDocument.unsubscribe(ops.Document.signalCursorMoved, onCursorMoved); odtDocument.unsubscribe(ops.OdtDocument.signalCommonStyleCreated, onStyleCreated); odtDocument.unsubscribe(ops.OdtDocument.signalCommonStyleDeleted, onStyleDeleted); odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); @@ -623,17 +629,17 @@ define("webodf/editor/EditorSession", [ directParagraphStylingEnabled: config.directParagraphStylingEnabled }); caretManager = new gui.CaretManager(self.sessionController); - selectionViewManager = new gui.SelectionViewManager(); + selectionViewManager = new gui.SelectionViewManager(gui.SvgSelectionView); self.sessionView = new gui.SessionView(config.viewOptions, localMemberId, session, caretManager, selectionViewManager); self.availableFonts = getAvailableFonts(); selectionViewManager.registerCursor(shadowCursor, true); // Custom signals, that make sense in the Editor context. We do not want to expose webodf's ops signals to random bits of the editor UI. - odtDocument.subscribe(ops.OdtDocument.signalMemberAdded, onMemberAdded); - odtDocument.subscribe(ops.OdtDocument.signalMemberUpdated, onMemberUpdated); - odtDocument.subscribe(ops.OdtDocument.signalMemberRemoved, onMemberRemoved); - odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); + odtDocument.subscribe(ops.Document.signalMemberAdded, onMemberAdded); + odtDocument.subscribe(ops.Document.signalMemberUpdated, onMemberUpdated); + odtDocument.subscribe(ops.Document.signalMemberRemoved, onMemberRemoved); + odtDocument.subscribe(ops.Document.signalCursorAdded, onCursorAdded); + odtDocument.subscribe(ops.Document.signalCursorRemoved, onCursorRemoved); + odtDocument.subscribe(ops.Document.signalCursorMoved, onCursorMoved); odtDocument.subscribe(ops.OdtDocument.signalCommonStyleCreated, onStyleCreated); odtDocument.subscribe(ops.OdtDocument.signalCommonStyleDeleted, onStyleDeleted); odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); diff --git a/js/3rdparty/webodf/editor/Tools.js b/js/3rdparty/webodf/editor/Tools.js index 8ab50b2b..c0aa4dad 100644 --- a/js/3rdparty/webodf/editor/Tools.js +++ b/js/3rdparty/webodf/editor/Tools.js @@ -49,11 +49,12 @@ define("webodf/editor/Tools", [ "webodf/editor/widgets/undoRedoMenu", "webodf/editor/widgets/toolbarWidgets/currentStyle", "webodf/editor/widgets/annotation", - "webodf/editor/widgets/paragraphStylesDialog", + "webodf/editor/widgets/editHyperlinks", "webodf/editor/widgets/imageInserter", + "webodf/editor/widgets/paragraphStylesDialog", "webodf/editor/widgets/zoomSlider", "webodf/editor/EditorSession"], - function (ready, MenuItem, DropDownMenu, Button, DropDownButton, Toolbar, ParagraphAlignment, SimpleStyles, UndoRedoMenu, CurrentStyle, AnnotationControl, ParagraphStylesDialog, ImageInserter, ZoomSlider, EditorSession) { + function (ready, MenuItem, DropDownMenu, Button, DropDownButton, Toolbar, ParagraphAlignment, SimpleStyles, UndoRedoMenu, CurrentStyle, AnnotationControl, EditHyperlinks, ImageInserter, ParagraphStylesDialog, ZoomSlider, EditorSession) { "use strict"; return function Tools(args) { @@ -72,6 +73,7 @@ define("webodf/editor/Tools", [ paragraphAlignment, imageInserter, annotationControl, + editHyperlinks, sessionSubscribers = []; function handleCursorMoved(cursor) { @@ -128,6 +130,7 @@ define("webodf/editor/Tools", [ widget.startup(); }); sessionSubscribers.push(undoRedoMenu); + undoRedoMenu.onToolDone = onToolDone; } // Add annotation @@ -226,6 +229,15 @@ define("webodf/editor/Tools", [ sessionSubscribers.push(paragraphStylesDialog); paragraphStylesDialog.onToolDone = onToolDone; + if (args.hyperlinkEditingEnabled) { + editHyperlinks = new EditHyperlinks(function (widget) { + widget.placeAt(toolbar); + widget.startup(); + }); + sessionSubscribers.push(editHyperlinks); + editHyperlinks.onToolDone = onToolDone; + } + formatMenuButton = new DropDownButton({ dropDown: formatDropDownMenu, label: tr('Format'), diff --git a/js/3rdparty/webodf/editor/server/pullbox/OperationRouter.js b/js/3rdparty/webodf/editor/server/pullbox/OperationRouter.js index a6cf8d61..de24b50f 100644 --- a/js/3rdparty/webodf/editor/server/pullbox/OperationRouter.js +++ b/js/3rdparty/webodf/editor/server/pullbox/OperationRouter.js @@ -94,7 +94,9 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () { EVENT_BEFORESAVETOFILE, EVENT_SAVEDTOFILE, EVENT_HASLOCALUNSYNCEDOPERATIONSCHANGED, - EVENT_HASSESSIONHOSTCONNECTIONCHANGED + EVENT_HASSESSIONHOSTCONNECTIONCHANGED, + ops.OperationRouter.signalProcessingBatchStart, + ops.OperationRouter.signalProcessingBatchEnd ]), /**@type{!boolean} tells if any local ops have been modifying ops */ hasPushedModificationOps = false, @@ -150,6 +152,8 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () { // take start time startTime = (new Date()).getTime(); + eventNotifier.emit(ops.OperationRouter.signalProcessingBatchStart, {}); + // apply as much as possible in the given time while (unplayedServerOpspecQueue.length > 0) { // time over? @@ -164,11 +168,13 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () { runtime.log(" op in: "+runtime.toJson(opspec)); if (op !== null) { if (!playbackFunction(op)) { + eventNotifier.emit(ops.OperationRouter.signalProcessingBatchEnd, {}); hasError = true; errorCallback("opExecutionFailure"); return; } } else { + eventNotifier.emit(ops.OperationRouter.signalProcessingBatchEnd, {}); hasError = true; runtime.log("ignoring invalid incoming opspec: " + opspec); errorCallback("unknownOpReceived"); @@ -176,6 +182,8 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () { } } + eventNotifier.emit(ops.OperationRouter.signalProcessingBatchEnd, {}); + // still unplayed opspecs? if (unplayedServerOpspecQueue.length > 0) { // let other events be handled. then continue @@ -493,6 +501,8 @@ runtime.log("OperationRouter: instant opsSync requested"); return; } + eventNotifier.emit(ops.OperationRouter.signalProcessingBatchStart, {}); + for (i = 0; i < operations.length; i += 1) { op = operations[i]; opspec = op.spec(); @@ -518,6 +528,8 @@ runtime.log("OperationRouter: instant opsSync requested"); triggerPushingOps(); updateHasLocalUnsyncedOpsState(); + + eventNotifier.emit(ops.OperationRouter.signalProcessingBatchEnd, {}); }; /** diff --git a/js/3rdparty/webodf/editor/widgets/dialogWidgets/alignmentPane.js b/js/3rdparty/webodf/editor/widgets/dialogWidgets/alignmentPane.js index 9e9a149f..24136f05 100644 --- a/js/3rdparty/webodf/editor/widgets/dialogWidgets/alignmentPane.js +++ b/js/3rdparty/webodf/editor/widgets/dialogWidgets/alignmentPane.js @@ -38,10 +38,11 @@ /*global runtime,core,define,require,dijit */ -runtime.loadClass("core.CSSUnits"); - define("webodf/editor/widgets/dialogWidgets/alignmentPane", [], function () { "use strict"; + + runtime.loadClass("core.CSSUnits"); + var AlignmentPane = function (callback) { var self = this, editorSession, diff --git a/js/3rdparty/webodf/editor/widgets/dialogWidgets/editHyperlinkPane.html b/js/3rdparty/webodf/editor/widgets/dialogWidgets/editHyperlinkPane.html new file mode 100644 index 00000000..ad6b5a11 --- /dev/null +++ b/js/3rdparty/webodf/editor/widgets/dialogWidgets/editHyperlinkPane.html @@ -0,0 +1,30 @@ + + + +
+
+
+ + +
+
+
+ +
+
+ + +
+
+ + diff --git a/js/3rdparty/webodf/editor/widgets/dialogWidgets/editHyperlinkPane.js b/js/3rdparty/webodf/editor/widgets/dialogWidgets/editHyperlinkPane.js new file mode 100644 index 00000000..d696e787 --- /dev/null +++ b/js/3rdparty/webodf/editor/widgets/dialogWidgets/editHyperlinkPane.js @@ -0,0 +1,110 @@ +/** + * @license + * 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/ + */ + +/*global runtime,core,define,require,document,dijit */ + +define("webodf/editor/widgets/dialogWidgets/editHyperlinkPane", [ + "dojo", + "dijit/layout/ContentPane"], + + function (dojo, ContentPane) { + "use strict"; + + runtime.loadClass("core.CSSUnits"); + + var EditHyperlinkPane = function () { + var self = this, + editorBase = dojo.config && dojo.config.paths && dojo.config.paths['webodf/editor'], + contentPane, + form, + displayTextField, + initialValue; + + runtime.assert(editorBase, "webodf/editor path not defined in dojoConfig"); + + function onSave() { + if (self.onSave) { + self.onSave(); + } + return false; + } + + function onCancel() { + form.set('value', initialValue); + if (self.onCancel) { + self.onCancel(); + } + } + + contentPane = new ContentPane({ + title: runtime.tr("editLink"), + href: editorBase+"/widgets/dialogWidgets/editHyperlinkPane.html", + preload: true, + onLoad : function () { + form = dijit.byId('editHyperlinkPaneForm'); + form.onSubmit = onSave; + dijit.byId('cancelHyperlinkChangeButton').onClick = onCancel; + displayTextField = dijit.byId('linkDisplayText'); + runtime.translateContent(form.domNode); + if (initialValue) { + form.set('value', initialValue); + displayTextField.set('disabled', initialValue.isReadOnlyText); + initialValue = undefined; + } + } + }); + + this.widget = function () { + return contentPane; + }; + + this.value = function () { + return form && form.get('value'); + }; + + this.set = function (value) { + initialValue = value; + if (form) { + form.set('value', value); + displayTextField.set('disabled', value.isReadOnlyText); + } + }; + }; + + return EditHyperlinkPane; +}); diff --git a/js/3rdparty/webodf/editor/widgets/editHyperlinks.js b/js/3rdparty/webodf/editor/widgets/editHyperlinks.js new file mode 100644 index 00000000..c1fc8401 --- /dev/null +++ b/js/3rdparty/webodf/editor/widgets/editHyperlinks.js @@ -0,0 +1,181 @@ +/** + * @license + * 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/ + */ + +/*global define,require,document,odf */ + +define("webodf/editor/widgets/editHyperlinks", [ + "webodf/editor/EditorSession", + "webodf/editor/widgets/dialogWidgets/editHyperlinkPane", + "dijit/form/Button", + "dijit/form/DropDownButton", + "dijit/TooltipDialog"], + + function (EditorSession, EditHyperlinkPane, Button, DropDownButton, TooltipDialog) { + "use strict"; + + runtime.loadClass("odf.OdfUtils"); + + var EditHyperlinks = function (callback) { + var self = this, + widget = {}, + editorSession, + hyperlinkController, + linkEditorContent, + editHyperlinkButton, + removeHyperlinkButton, + odfUtils = new odf.OdfUtils(), + dialog; + + linkEditorContent = new EditHyperlinkPane(); + dialog = new TooltipDialog({ + title: runtime.tr("Edit link"), + content: linkEditorContent.widget() + }); + + editHyperlinkButton = new DropDownButton({ + label: runtime.tr('Edit link'), + showLabel: false, + iconClass: 'dijitEditorIcon dijitEditorIconCreateLink', + dropDown: dialog + }); + + removeHyperlinkButton = new Button({ + label: runtime.tr('Remove link'), + showLabel: false, + disabled: true, + iconClass: 'dijitEditorIcon dijitEditorIconUnlink', + onClick: function () { + hyperlinkController.removeHyperlinks(); + self.onToolDone(); + } + }); + + linkEditorContent.onSave = function () { + var hyperlinkData = linkEditorContent.value(); + editHyperlinkButton.closeDropDown(false); + if (hyperlinkData.isReadOnlyText == "true") { + hyperlinkController.removeHyperlinks(); + hyperlinkController.addHyperlink(hyperlinkData.linkUrl); + } else { + hyperlinkController.addHyperlink(hyperlinkData.linkUrl, hyperlinkData.linkDisplayText); + } + self.onToolDone(); + }; + + linkEditorContent.onCancel = function () { + editHyperlinkButton.closeDropDown(false); + self.onToolDone(); + }; + + widget.children = [editHyperlinkButton, removeHyperlinkButton]; + widget.startup = function () { + widget.children.forEach(function (element) { + element.startup(); + }); + }; + + widget.placeAt = function (container) { + widget.children.forEach(function (element) { + element.placeAt(container); + }); + return widget; + }; + + function checkHyperlinkButtons() { + var selection = editorSession.getSelectedRange(), + textContent, + linksInSelection = editorSession.getSelectedHyperlinks(), + linkTarget = linksInSelection[0] ? odfUtils.getHyperlinkTarget(linksInSelection[0]) : "http://"; + + if (selection && selection.collapsed && linksInSelection.length === 1) { + // Selection is collapsed within a single hyperlink. Assume user is modifying the hyperlink + textContent = selection.cloneRange(); + textContent.selectNodeContents(linksInSelection[0]); + linkEditorContent.set({ + linkDisplayText: textContent.toString(), + linkUrl: linkTarget, + isReadOnlyText: true + }); + textContent.detach(); + } else if (selection && !selection.collapsed) { + // User has selected part of a hyperlink or a block of text. Assume user is attempting to modify the + // existing hyperlink, or wants to convert the selection into a hyperlink + linkEditorContent.set({ + linkDisplayText: selection.toString(), + linkUrl: linkTarget, + isReadOnlyText: true + }); + } else { + // Selection is collapsed and is not in an existing hyperlink + linkEditorContent.set({ + linkDisplayText: "", + linkUrl: linkTarget, + isReadOnlyText: false + }); + } + + // The 3rd parameter is false to avoid firing onChange when setting the value programmatically. + removeHyperlinkButton.set('disabled', linksInSelection.length === 0, false); + } + + this.setEditorSession = function (session) { + if (editorSession) { + editorSession.unsubscribe(EditorSession.signalCursorMoved, checkHyperlinkButtons); + editorSession.unsubscribe(EditorSession.signalParagraphChanged, checkHyperlinkButtons); + editorSession.unsubscribe(EditorSession.signalParagraphStyleModified, checkHyperlinkButtons); + } + editorSession = session; + hyperlinkController = session && session.sessionController.getHyperlinkController(); + widget.children.forEach(function (element) { + element.setAttribute('disabled', !hyperlinkController); + }); + if (editorSession) { + editorSession.subscribe(EditorSession.signalCursorMoved, checkHyperlinkButtons); + editorSession.subscribe(EditorSession.signalParagraphChanged, checkHyperlinkButtons); + editorSession.subscribe(EditorSession.signalParagraphStyleModified, checkHyperlinkButtons); + checkHyperlinkButtons(); + } + }; + + this.onToolDone = function () {}; + + callback(widget); + }; + + return EditHyperlinks; +}); diff --git a/js/3rdparty/webodf/editor/widgets/paragraphAlignment.js b/js/3rdparty/webodf/editor/widgets/paragraphAlignment.js index ae833b04..afc7958f 100644 --- a/js/3rdparty/webodf/editor/widgets/paragraphAlignment.js +++ b/js/3rdparty/webodf/editor/widgets/paragraphAlignment.js @@ -50,7 +50,7 @@ define("webodf/editor/widgets/paragraphAlignment", [ var self = this, editorSession, widget = {}, - directParagraphStyler, + directFormattingController, justifyLeft, justifyCenter, justifyRight, @@ -65,7 +65,7 @@ define("webodf/editor/widgets/paragraphAlignment", [ checked: false, iconClass: "dijitEditorIcon dijitEditorIconJustifyLeft", onChange: function () { - directParagraphStyler.alignParagraphLeft(); + directFormattingController.alignParagraphLeft(); self.onToolDone(); } }); @@ -77,7 +77,7 @@ define("webodf/editor/widgets/paragraphAlignment", [ checked: false, iconClass: "dijitEditorIcon dijitEditorIconJustifyCenter", onChange: function () { - directParagraphStyler.alignParagraphCenter(); + directFormattingController.alignParagraphCenter(); self.onToolDone(); } }); @@ -89,7 +89,7 @@ define("webodf/editor/widgets/paragraphAlignment", [ checked: false, iconClass: "dijitEditorIcon dijitEditorIconJustifyRight", onChange: function () { - directParagraphStyler.alignParagraphRight(); + directFormattingController.alignParagraphRight(); self.onToolDone(); } }); @@ -101,7 +101,7 @@ define("webodf/editor/widgets/paragraphAlignment", [ checked: false, iconClass: "dijitEditorIcon dijitEditorIconJustifyFull", onChange: function () { - directParagraphStyler.alignParagraphJustified(); + directFormattingController.alignParagraphJustified(); self.onToolDone(); } }); @@ -112,7 +112,7 @@ define("webodf/editor/widgets/paragraphAlignment", [ showLabel: false, iconClass: "dijitEditorIcon dijitEditorIconOutdent", onClick: function () { - directParagraphStyler.outdent(); + directFormattingController.outdent(); self.onToolDone(); } }); @@ -123,7 +123,7 @@ define("webodf/editor/widgets/paragraphAlignment", [ showLabel: false, iconClass: "dijitEditorIcon dijitEditorIconIndent", onClick: function () { - directParagraphStyler.indent(); + directFormattingController.indent(); self.onToolDone(); } }); @@ -174,21 +174,21 @@ define("webodf/editor/widgets/paragraphAlignment", [ } this.setEditorSession = function (session) { - if (directParagraphStyler) { - directParagraphStyler.unsubscribe(gui.DirectParagraphStyler.paragraphStylingChanged, updateStyleButtons); + if (directFormattingController) { + directFormattingController.unsubscribe(gui.DirectFormattingController.paragraphStylingChanged, updateStyleButtons); } - directParagraphStyler = session && session.sessionController.getDirectParagraphStyler(); - if (directParagraphStyler) { - directParagraphStyler.subscribe(gui.DirectParagraphStyler.paragraphStylingChanged, updateStyleButtons); + directFormattingController = session && session.sessionController.getDirectFormattingController(); + if (directFormattingController) { + directFormattingController.subscribe(gui.DirectFormattingController.paragraphStylingChanged, updateStyleButtons); } widget.children.forEach(function (element) { - element.setAttribute('disabled', !directParagraphStyler); + element.setAttribute('disabled', !directFormattingController); }); updateStyleButtons({ - isAlignedLeft: directParagraphStyler ? directParagraphStyler.isAlignedLeft() : false, - isAlignedCenter: directParagraphStyler ? directParagraphStyler.isAlignedCenter() : false, - isAlignedRight: directParagraphStyler ? directParagraphStyler.isAlignedRight() : false, - isAlignedJustified: directParagraphStyler ? directParagraphStyler.isAlignedJustified() : false + isAlignedLeft: directFormattingController ? directFormattingController.isAlignedLeft() : false, + isAlignedCenter: directFormattingController ? directFormattingController.isAlignedCenter() : false, + isAlignedRight: directFormattingController ? directFormattingController.isAlignedRight() : false, + isAlignedJustified: directFormattingController ? directFormattingController.isAlignedJustified() : false }); if (editorSession) { diff --git a/js/3rdparty/webodf/editor/widgets/simpleStyles.js b/js/3rdparty/webodf/editor/widgets/simpleStyles.js index 10a6a107..00595750 100644 --- a/js/3rdparty/webodf/editor/widgets/simpleStyles.js +++ b/js/3rdparty/webodf/editor/widgets/simpleStyles.js @@ -51,7 +51,7 @@ define("webodf/editor/widgets/simpleStyles", [ var self = this, editorSession, widget = {}, - directTextStyler, + directFormattingController, boldButton, italicButton, underlineButton, @@ -67,7 +67,7 @@ define("webodf/editor/widgets/simpleStyles", [ checked: false, iconClass: "dijitEditorIcon dijitEditorIconBold", onChange: function (checked) { - directTextStyler.setBold(checked); + directFormattingController.setBold(checked); self.onToolDone(); } }); @@ -79,7 +79,7 @@ define("webodf/editor/widgets/simpleStyles", [ checked: false, iconClass: "dijitEditorIcon dijitEditorIconItalic", onChange: function (checked) { - directTextStyler.setItalic(checked); + directFormattingController.setItalic(checked); self.onToolDone(); } }); @@ -91,7 +91,7 @@ define("webodf/editor/widgets/simpleStyles", [ checked: false, iconClass: "dijitEditorIcon dijitEditorIconUnderline", onChange: function (checked) { - directTextStyler.setHasUnderline(checked); + directFormattingController.setHasUnderline(checked); self.onToolDone(); } }); @@ -103,7 +103,7 @@ define("webodf/editor/widgets/simpleStyles", [ checked: false, iconClass: "dijitEditorIcon dijitEditorIconStrikethrough", onChange: function (checked) { - directTextStyler.setHasStrikethrough(checked); + directFormattingController.setHasStrikethrough(checked); self.onToolDone(); } }); @@ -117,7 +117,7 @@ define("webodf/editor/widgets/simpleStyles", [ constraints: {min:6, max:96}, intermediateChanges: true, onChange: function (value) { - directTextStyler.setFontSize(value); + directFormattingController.setFontSize(value); }, onClick: function () { self.onToolDone(); @@ -134,7 +134,7 @@ define("webodf/editor/widgets/simpleStyles", [ fontPickerWidget = fontPicker.widget(); fontPickerWidget.setAttribute('disabled', true); fontPickerWidget.onChange = function(value) { - directTextStyler.setFontName(value); + directFormattingController.setFontName(value); self.onToolDone(); }; @@ -183,24 +183,24 @@ define("webodf/editor/widgets/simpleStyles", [ } this.setEditorSession = function(session) { - if (directTextStyler) { - directTextStyler.unsubscribe(gui.DirectTextStyler.textStylingChanged, updateStyleButtons); + if (directFormattingController) { + directFormattingController.unsubscribe(gui.DirectFormattingController.textStylingChanged, updateStyleButtons); } - directTextStyler = session && session.sessionController.getDirectTextStyler(); + directFormattingController = session && session.sessionController.getDirectFormattingController(); fontPicker.setEditorSession(session); - if (directTextStyler) { - directTextStyler.subscribe(gui.DirectTextStyler.textStylingChanged, updateStyleButtons); + if (directFormattingController) { + directFormattingController.subscribe(gui.DirectFormattingController.textStylingChanged, updateStyleButtons); } widget.children.forEach(function (element) { - element.setAttribute('disabled', !directTextStyler); + element.setAttribute('disabled', !directFormattingController); }); updateStyleButtons({ - isBold: directTextStyler ? directTextStyler.isBold() : false, - isItalic: directTextStyler ? directTextStyler.isItalic() : false, - hasUnderline: directTextStyler ? directTextStyler.hasUnderline() : false, - hasStrikeThrough: directTextStyler ? directTextStyler.hasStrikeThrough() : false, - fontSize: directTextStyler ? directTextStyler.fontSize() : undefined, - fontName: directTextStyler ? directTextStyler.fontName() : undefined + isBold: directFormattingController ? directFormattingController.isBold() : false, + isItalic: directFormattingController ? directFormattingController.isItalic() : false, + hasUnderline: directFormattingController ? directFormattingController.hasUnderline() : false, + hasStrikeThrough: directFormattingController ? directFormattingController.hasStrikeThrough() : false, + fontSize: directFormattingController ? directFormattingController.fontSize() : undefined, + fontName: directFormattingController ? directFormattingController.fontName() : undefined }); if (editorSession) { diff --git a/js/3rdparty/webodf/editor/widgets/undoRedoMenu.js b/js/3rdparty/webodf/editor/widgets/undoRedoMenu.js index cf1a57cd..e266615e 100644 --- a/js/3rdparty/webodf/editor/widgets/undoRedoMenu.js +++ b/js/3rdparty/webodf/editor/widgets/undoRedoMenu.js @@ -39,61 +39,57 @@ /*global define,require*/ define("webodf/editor/widgets/undoRedoMenu", - ["webodf/editor/EditorSession"], + ["webodf/editor/EditorSession", "dijit/form/Button"], - function (EditorSession) { + function (EditorSession, Button) { "use strict"; return function UndoRedoMenu(callback) { - var editorSession, + var self = this, + editorSession, undoButton, - redoButton; + redoButton, + widget = {}; - function makeWidget(callback) { - require(["dijit/form/Button"], function (Button) { - var widget = {}; - - undoButton = new Button({ - label: runtime.tr('Undo'), - showLabel: false, - disabled: true, // TODO: get current session state - iconClass: "dijitEditorIcon dijitEditorIconUndo", - onClick: function () { - if (editorSession) { - editorSession.undo(); - } - } - }); - - redoButton = new Button({ - label: runtime.tr('Redo'), - showLabel: false, - disabled: true, // TODO: get current session state - iconClass: "dijitEditorIcon dijitEditorIconRedo", - onClick: function () { - if (editorSession) { - editorSession.redo(); - } - } - }); + undoButton = new Button({ + label: runtime.tr('Undo'), + showLabel: false, + disabled: true, // TODO: get current session state + iconClass: "dijitEditorIcon dijitEditorIconUndo", + onClick: function () { + if (editorSession) { + editorSession.undo(); + self.onToolDone(); + } + } + }); - widget.children = [undoButton, redoButton]; - widget.startup = function () { - widget.children.forEach(function (element) { - element.startup(); - }); - }; + redoButton = new Button({ + label: runtime.tr('Redo'), + showLabel: false, + disabled: true, // TODO: get current session state + iconClass: "dijitEditorIcon dijitEditorIconRedo", + onClick: function () { + if (editorSession) { + editorSession.redo(); + self.onToolDone(); + } + } + }); - widget.placeAt = function (container) { - widget.children.forEach(function (element) { - element.placeAt(container); - }); - return widget; - }; + widget.children = [undoButton, redoButton]; + widget.startup = function () { + widget.children.forEach(function (element) { + element.startup(); + }); + }; - return callback(widget); + widget.placeAt = function (container) { + widget.children.forEach(function (element) { + element.placeAt(container); }); - } + return widget; + }; function checkUndoButtons(e) { if (undoButton) { @@ -115,9 +111,9 @@ define("webodf/editor/widgets/undoRedoMenu", } }; + this.onToolDone = function () {}; + // init - makeWidget(function (widget) { - return callback(widget); - }); + callback(widget); }; }); diff --git a/js/3rdparty/webodf/editor/widgets/zoomSlider.js b/js/3rdparty/webodf/editor/widgets/zoomSlider.js index b445211e..a5e1e370 100644 --- a/js/3rdparty/webodf/editor/widgets/zoomSlider.js +++ b/js/3rdparty/webodf/editor/widgets/zoomSlider.js @@ -38,24 +38,29 @@ /*global define,require*/ -define("webodf/editor/widgets/zoomSlider", [], function () { +define("webodf/editor/widgets/zoomSlider", [ + "webodf/editor/EditorSession"], + function (EditorSession) { "use strict"; return function ZoomSlider(callback) { var self = this, editorSession, - slider; + slider, + extremeZoomFactor = 4; + // The slider zooms from -1 to +1, which corresponds + // to zoom levels of 1/extremeZoomFactor to extremeZoomFactor. function makeWidget(callback) { require(["dijit/form/HorizontalSlider", "dijit/form/NumberTextBox", "dojo"], function (HorizontalSlider, NumberTextBox, dojo) { var widget = {}; slider = new HorizontalSlider({ name: 'zoomSlider', - value: 100, - minimum: 30, - maximum: 150, - discreteValues: 100, + value: 0, + minimum: -1, + maximum: 1, + discreteValues: 0.01, intermediateChanges: true, style: { width: '150px', @@ -66,7 +71,7 @@ define("webodf/editor/widgets/zoomSlider", [], function () { slider.onChange = function (value) { if (editorSession) { - editorSession.getOdfCanvas().setZoomLevel(value / 100.0); + editorSession.getOdfCanvas().getZoomHelper().setZoomLevel(Math.pow(extremeZoomFactor, value)); } self.onToolDone(); }; @@ -75,9 +80,23 @@ define("webodf/editor/widgets/zoomSlider", [], function () { }); } + function updateSlider(zoomLevel) { + if (slider) { + slider.set('value', Math.log(zoomLevel) / Math.log(extremeZoomFactor), false); + } + } + this.setEditorSession = function(session) { + var zoomHelper; + if (editorSession) { + editorSession.getOdfCanvas().getZoomHelper().unsubscribe(gui.ZoomHelper.signalZoomChanged, updateSlider); + } editorSession = session; -// if (slider) { slider.setValue(editorSession.getOdfCanvas().getZoomLevel() ); TODO! + if (editorSession) { + zoomHelper = editorSession.getOdfCanvas().getZoomHelper(); + zoomHelper.subscribe(gui.ZoomHelper.signalZoomChanged, updateSlider); + updateSlider(zoomHelper.getZoomLevel()); + } }; this.onToolDone = function () {}; diff --git a/js/3rdparty/webodf/webodf-debug.js b/js/3rdparty/webodf/webodf-debug.js index 4bb2eff2..fcebfe47 100644 --- a/js/3rdparty/webodf/webodf-debug.js +++ b/js/3rdparty/webodf/webodf-debug.js @@ -1,4 +1,4 @@ -var webodf_version = "0.4.2-1579-gfc3a4e6"; +var webodf_version = "0.4.2-2010-ge06842f"; function Runtime() { } Runtime.prototype.getVariable = function(name) { @@ -49,6 +49,10 @@ Runtime.prototype.exit = function(exitCode) { }; Runtime.prototype.getWindow = function() { }; +Runtime.prototype.requestAnimationFrame = function(callback) { +}; +Runtime.prototype.cancelAnimationFrame = function(requestId) { +}; Runtime.prototype.assert = function(condition, message, callback) { }; var IS_COMPILED_CODE = true; @@ -128,14 +132,29 @@ Runtime.getFunctionName = function getFunctionName(f) { }; function BrowserRuntime(logoutput) { var self = this, cache = {}; - function utf8ByteArrayFromString(string) { - var l = string.length, bytearray, i, n, j = 0; + function getUtf8LengthForString(string) { + var l = string.length, i, n, j = 0; for(i = 0;i < l;i += 1) { n = string.charCodeAt(i); - j += 1 + (n > 128) + (n > 2048) + j += 1 + (n > 128) + (n > 2048); + if(n > 55040 && n < 57344) { + j += 1; + i += 1 + } + } + return j + } + function utf8ByteArrayFromString(string, length, addBOM) { + var l = string.length, bytearray, i, n, j; + bytearray = new Uint8Array(new ArrayBuffer(length)); + if(addBOM) { + bytearray[0] = 239; + bytearray[1] = 187; + bytearray[2] = 191; + j = 3 + }else { + j = 0 } - bytearray = new Uint8Array(new ArrayBuffer(j)); - j = 0; for(i = 0;i < l;i += 1) { n = string.charCodeAt(i); if(n < 128) { @@ -147,15 +166,36 @@ function BrowserRuntime(logoutput) { bytearray[j + 1] = 128 | n & 63; j += 2 }else { - bytearray[j] = 224 | n >>> 12 & 15; - bytearray[j + 1] = 128 | n >>> 6 & 63; - bytearray[j + 2] = 128 | n & 63; - j += 3 + if(n <= 55040 || n >= 57344) { + bytearray[j] = 224 | n >>> 12 & 15; + bytearray[j + 1] = 128 | n >>> 6 & 63; + bytearray[j + 2] = 128 | n & 63; + j += 3 + }else { + i += 1; + n = (n - 55296 << 10 | string.charCodeAt(i) - 56320) + 65536; + bytearray[j] = 240 | n >>> 18 & 7; + bytearray[j + 1] = 128 | n >>> 12 & 63; + bytearray[j + 2] = 128 | n >>> 6 & 63; + bytearray[j + 3] = 128 | n & 63; + j += 4 + } } } } return bytearray } + function utf8ByteArrayFromXHRString(string, wishLength) { + var addBOM = false, length = getUtf8LengthForString(string); + if(typeof wishLength === "number") { + if(wishLength !== length && wishLength !== length + 3) { + return undefined + } + addBOM = length + 3 === wishLength; + length = wishLength + } + return utf8ByteArrayFromString(string, length, addBOM) + } function byteArrayFromString(string) { var l = string.length, a = new Uint8Array(new ArrayBuffer(l)), i; for(i = 0;i < l;i += 1) { @@ -166,7 +206,7 @@ function BrowserRuntime(logoutput) { this.byteArrayFromString = function(string, encoding) { var result; if(encoding === "utf8") { - result = utf8ByteArrayFromString(string) + result = utf8ByteArrayFromString(string, getUtf8LengthForString(string), false) }else { if(encoding !== "binary") { self.log("unknown encoding: " + encoding) @@ -228,8 +268,22 @@ function BrowserRuntime(logoutput) { } return a } + function stringToBinaryWorkaround(xhr) { + var cl, data; + cl = xhr.getResponseHeader("Content-Length"); + if(cl) { + cl = parseInt(cl, 10) + } + if(cl && cl !== xhr.responseText.length) { + data = utf8ByteArrayFromXHRString(xhr.responseText, cl) + } + if(data === undefined) { + data = byteArrayFromString(xhr.responseText) + } + return data + } function handleXHRResult(path, encoding, xhr) { - var data, r, d, a; + var r, d, a, data; if(xhr.status === 0 && !xhr.responseText) { r = {err:"File " + path + " is empty.", data:null} }else { @@ -247,7 +301,7 @@ function BrowserRuntime(logoutput) { a = (new VBArray(xhr.responseBody)).toArray(); data = arrayToUint8Array(a) }else { - data = self.byteArrayFromString(xhr.responseText, "binary") + data = stringToBinaryWorkaround(xhr) } }else { data = xhr.responseText @@ -468,6 +522,25 @@ function BrowserRuntime(logoutput) { }; this.getWindow = function() { return window + }; + this.requestAnimationFrame = function(callback) { + var rAF = window.requestAnimationFrame || (window.webkitRequestAnimationFrame || (window.mozRequestAnimationFrame || window.msRequestAnimationFrame)), requestId = 0; + if(rAF) { + rAF.bind(window); + requestId = (rAF)(callback) + }else { + return setTimeout(callback, 15) + } + return requestId + }; + this.cancelAnimationFrame = function(requestId) { + var cAF = window.cancelAnimationFrame || (window.webkitCancelAnimationFrame || (window.mozCancelAnimationFrame || window.msCancelAnimationFrame)); + if(cAF) { + cAF.bind(window); + (cAF)(requestId) + }else { + clearTimeout(requestId) + } } } function NodeJSRuntime() { @@ -637,6 +710,12 @@ function NodeJSRuntime() { this.getWindow = function() { return null }; + this.requestAnimationFrame = function(callback) { + return setTimeout(callback, 15) + }; + this.cancelAnimationFrame = function(requestId) { + clearTimeout(requestId) + }; function init() { var DOMParser = require("xmldom").DOMParser; parser = new DOMParser; @@ -821,6 +900,12 @@ function RhinoRuntime() { this.exit = quit; this.getWindow = function() { return null + }; + this.requestAnimationFrame = function(callback) { + callback(); + return 0 + }; + this.cancelAnimationFrame = function() { } } Runtime.create = function create() { @@ -843,162 +928,124 @@ var xmldom = {}; var odf = {}; var ops = {}; (function() { - var dependencies = {}, loadedFiles = {}; - function loadManifest(dir, manifests) { + function loadDependenciesFromManifest(dir, dependencies, expectFail) { var path = dir + "/manifest.json", content, list, manifest, m; - if(loadedFiles.hasOwnProperty(path)) { - return - } - loadedFiles[path] = 1; + runtime.log("Loading manifest: " + path); try { content = runtime.readFileSync(path, "utf-8") }catch(e) { - console.log(String(e)); + if(expectFail) { + runtime.log("No loadable manifest found.") + }else { + console.log(String(e)); + throw e; + } return } list = JSON.parse((content)); manifest = (list); for(m in manifest) { if(manifest.hasOwnProperty(m)) { - manifests[m] = {dir:dir, deps:manifest[m]} + dependencies[m] = {dir:dir, deps:manifest[m]} } } } - 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) - } - }); - 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) - } - } - stack.pop(); - sorted.push(path) - } + function loadDependenciesFromManifests() { + var dependencies = [], paths = runtime.libraryPaths(), i; + if(runtime.currentDirectory() && paths.indexOf(runtime.currentDirectory()) === -1) { + loadDependenciesFromManifest(runtime.currentDirectory(), dependencies, true) } - for(i = 0;i < deps.length;i += 1) { - add(deps[i], []) + for(i = 0;i < paths.length;i += 1) { + loadDependenciesFromManifest(paths[i], dependencies) } - return sorted + return dependencies } - function expandDependencies(manifests) { - var path, deps, allDeps = {}; - for(path in manifests) { - if(manifests.hasOwnProperty(path)) { - expandPathDependencies(path, manifests, allDeps) + function getPath(dir, className) { + return dir + "/" + className.replace(".", "/") + ".js" + } + function getLoadList(classNames, dependencies, isDefined) { + var loadList = [], stack = {}, visited = {}; + function visit(n) { + if(visited[n] || isDefined(n)) { + return } - } - for(path in manifests) { - if(manifests.hasOwnProperty(path)) { - deps = (Object.keys(allDeps[path])); - manifests[path].deps = sortDeps(deps, allDeps); - manifests[path].deps.push(path) + if(stack[n]) { + throw"Circular dependency detected for " + n + "."; + } + stack[n] = true; + if(!dependencies[n]) { + throw"Missing dependency information for class " + n + "."; } + var d = dependencies[n], deps = d.deps, i, l = deps.length; + for(i = 0;i < l;i += 1) { + visit(deps[i]) + } + stack[n] = false; + visited[n] = true; + loadList.push(getPath(d.dir, n)) } - dependencies = manifests + classNames.forEach(visit); + return loadList } - function loadManifests() { - if(Object.keys(dependencies).length > 0) { - return - } - var paths = runtime.libraryPaths(), manifests = {}, i; - if(runtime.currentDirectory()) { - loadManifest(runtime.currentDirectory(), manifests) - } + function addContent(path, content) { + content += "\n//# sourceURL=" + path; + content += "\n//@ sourceURL=" + path; + return content + } + function loadFiles(paths) { + var i, content; for(i = 0;i < paths.length;i += 1) { - loadManifest(paths[i], manifests) + content = runtime.readFileSync(paths[i], "utf-8"); + content = addContent(paths[i], (content)); + eval(content) } - 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]) - } + function loadFilesInBrowser(paths, callback) { + var e = document.currentScript || document.documentElement.lastChild, df = document.createDocumentFragment(), script, i; + for(i = 0;i < paths.length;i += 1) { + script = document.createElement("script"); + script.type = "text/javascript"; + script.charset = "utf-8"; + script.async = false; + script.setAttribute("src", paths[i]); + df.appendChild(script) + } + if(callback) { + script.onload = callback } - return deps + e.parentNode.insertBefore(df, e) } - function evalArray(paths, contents) { - var i = 0; - while(i < paths.length && contents[i] !== undefined) { - if(contents[i] !== null) { - eval((contents[i])); - contents[i] = null + var dependencies, packages = {core:core, gui:gui, xmldom:xmldom, odf:odf, ops:ops}; + function isDefined(classname) { + var parts = classname.split("."), i, p = packages, l = parts.length; + for(i = 0;i < l;i += 1) { + if(!p.hasOwnProperty(parts[i])) { + return false } - i += 1 + p = (p[parts[i]]) } + return true } - 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)) - } - }) + runtime.loadClasses = function(classnames, callback) { + if(IS_COMPILED_CODE || classnames.length === 0) { + return callback && callback() } - if(async) { - for(i = 0;i < paths.length;i += 1) { - loadedFiles[paths[i]] = 1; - loadFile(i) - } + dependencies = dependencies || loadDependenciesFromManifests(); + classnames = getLoadList(classnames, dependencies, isDefined); + if(classnames.length === 0) { + return callback && callback() } - 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)) + if(runtime.type() === "BrowserRuntime" && callback) { + loadFilesInBrowser(classnames, callback) + }else { + loadFiles(classnames); + if(callback) { + callback() } } - evalArray(paths, contents) - } - runtime.loadClass = function(classname) { - if(IS_COMPILED_CODE) { - return - } - var classpath = classPath(classname), paths; - if(loadedFiles.hasOwnProperty(classpath)) { - return - } - loadManifests(); - paths = getDependencies(classname); - loadFiles(paths) + }; + runtime.loadClass = function(classname, callback) { + runtime.loadClasses([classname], callback) } })(); (function() { @@ -1032,9 +1079,11 @@ var ops = {}; } var script = argv[0]; runtime.readFile(script, "utf8", function(err, code) { - var path = "", codestring = (code); - if(script.indexOf("/") !== -1) { - path = script.substring(0, script.indexOf("/")) + var path = "", pathEndIndex = script.lastIndexOf("/"), codestring = (code); + if(pathEndIndex !== -1) { + path = script.substring(0, pathEndIndex) + }else { + path = "." } runtime.setCurrentDirectory(path); function inner_run() { @@ -1451,11 +1500,13 @@ core.CSSUnits = function CSSUnits() { (function() { var browserQuirks; function getBrowserQuirks() { - var range, directBoundingRect, rangeBoundingRect, testContainer, testElement, detectedQuirks, window, document; + var range, directBoundingRect, rangeBoundingRect, testContainer, testElement, detectedQuirks, window, document, docElement, body, docOverflow, bodyOverflow, bodyHeight, bodyScroll; if(browserQuirks === undefined) { window = runtime.getWindow(); document = window && window.document; - browserQuirks = {rangeBCRIgnoresElementBCR:false, unscaledRangeClientRects:false}; + docElement = document.documentElement; + body = document.body; + browserQuirks = {rangeBCRIgnoresElementBCR:false, unscaledRangeClientRects:false, elementBCRIgnoresBodyScroll:false}; if(document) { testContainer = document.createElement("div"); testContainer.style.position = "absolute"; @@ -1464,7 +1515,7 @@ core.CSSUnits = function CSSUnits() { testContainer.style["-webkit-transform"] = "scale(2)"; testElement = document.createElement("div"); testContainer.appendChild(testElement); - document.body.appendChild(testContainer); + body.appendChild(testContainer); range = document.createRange(); range.selectNode(testElement); browserQuirks.rangeBCRIgnoresElementBCR = range.getClientRects().length === 0; @@ -1472,8 +1523,23 @@ core.CSSUnits = function CSSUnits() { directBoundingRect = testElement.getBoundingClientRect(); rangeBoundingRect = range.getBoundingClientRect(); browserQuirks.unscaledRangeClientRects = Math.abs(directBoundingRect.height - rangeBoundingRect.height) > 2; + testContainer.style.transform = ""; + testContainer.style["-webkit-transform"] = ""; + docOverflow = docElement.style.overflow; + bodyOverflow = body.style.overflow; + bodyHeight = body.style.height; + bodyScroll = body.scrollTop; + docElement.style.overflow = "visible"; + body.style.overflow = "visible"; + body.style.height = "200%"; + body.scrollTop = body.scrollHeight; + browserQuirks.elementBCRIgnoresBodyScroll = range.getBoundingClientRect().top !== testElement.getBoundingClientRect().top; + body.scrollTop = bodyScroll; + body.style.height = bodyHeight; + body.style.overflow = bodyOverflow; + docElement.style.overflow = docOverflow; range.detach(); - document.body.removeChild(testContainer); + body.removeChild(testContainer); detectedQuirks = Object.keys(browserQuirks).map(function(quirk) { return quirk + ":" + String(browserQuirks[quirk]) }).join(", "); @@ -1509,10 +1575,28 @@ core.CSSUnits = function CSSUnits() { } return{container:c, offset:offset} } + 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 + } function splitBoundaries(range) { - var modifiedNodes = [], end, splitStart, node, text; + var modifiedNodes = [], originalEndContainer, resetToContainerLength, end, splitStart, node, text, offset; if(range.startContainer.nodeType === Node.TEXT_NODE || range.endContainer.nodeType === Node.TEXT_NODE) { - end = range.endContainer && findStablePoint(range.endContainer, range.endOffset); + originalEndContainer = range.endContainer; + resetToContainerLength = range.endContainer.nodeType !== Node.TEXT_NODE ? range.endOffset === range.endContainer.childNodes.length : false; + end = findStablePoint(range.endContainer, range.endOffset); + if(end.container === originalEndContainer) { + originalEndContainer = null + } range.setEnd(end.container, end.offset); node = range.endContainer; if(range.endOffset !== 0 && node.nodeType === Node.TEXT_NODE) { @@ -1532,6 +1616,18 @@ core.CSSUnits = function CSSUnits() { range.setStart(splitStart, 0) } } + if(originalEndContainer !== null) { + node = range.endContainer; + while(node.parentNode && node.parentNode !== originalEndContainer) { + node = node.parentNode + } + if(resetToContainerLength) { + offset = originalEndContainer.childNodes.length + }else { + offset = getPositionInContainingNode(node, originalEndContainer) + } + range.setEnd(originalEndContainer, offset) + } } return modifiedNodes } @@ -1544,26 +1640,43 @@ core.CSSUnits = function CSSUnits() { return range1.compareBoundaryPoints(Range.END_TO_START, range2) <= 0 && range1.compareBoundaryPoints(Range.START_TO_END, range2) >= 0 } 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) + function maximumOffset(node) { + return node.nodeType === Node.TEXT_NODE ? (node).length : node.childNodes.length + } + function getNodesInRange(range, nodeFilter, whatToShow) { + var document = range.startContainer.ownerDocument, elements = [], rangeRoot = range.commonAncestorContainer, root = (rangeRoot.nodeType === Node.TEXT_NODE ? rangeRoot.parentNode : rangeRoot), treeWalker = document.createTreeWalker(root, whatToShow, nodeFilter, false), currentNode, lastNodeInRange, endNodeCompareFlags, comparePositionResult; + if(range.endContainer.childNodes[range.endOffset - 1]) { + lastNodeInRange = (range.endContainer.childNodes[range.endOffset - 1]); + endNodeCompareFlags = Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_CONTAINED_BY + }else { + lastNodeInRange = (range.endContainer); + endNodeCompareFlags = Node.DOCUMENT_POSITION_PRECEDING + } + if(range.startContainer.childNodes[range.startOffset]) { + currentNode = (range.startContainer.childNodes[range.startOffset]); + treeWalker.currentNode = currentNode + }else { + if(range.startOffset === maximumOffset(range.startContainer)) { + currentNode = (range.startContainer); + treeWalker.currentNode = currentNode; + treeWalker.lastChild(); + currentNode = treeWalker.nextNode() }else { - if(filterResult === NodeFilter.FILTER_REJECT) { - break - } + currentNode = (range.startContainer); + treeWalker.currentNode = currentNode } - n = n.parentNode } - elements.reverse(); - n = treeWalker.nextNode(); - while(n) { - elements.push(n); - n = treeWalker.nextNode() + if(currentNode && nodeFilter(currentNode) === NodeFilter.FILTER_ACCEPT) { + elements.push(currentNode) + } + currentNode = treeWalker.nextNode(); + while(currentNode) { + comparePositionResult = lastNodeInRange.compareDocumentPosition(currentNode); + if(comparePositionResult !== 0 && (comparePositionResult & endNodeCompareFlags) === 0) { + break + } + elements.push(currentNode); + currentNode = treeWalker.nextNode() } return elements } @@ -1624,8 +1737,8 @@ core.CSSUnits = function CSSUnits() { removeUnwantedNodes(node, shouldRemove); node = next } - if(shouldRemove(targetNode)) { - parent = mergeIntoParent(targetNode) + if(parent && shouldRemove(targetNode)) { + mergeIntoParent(targetNode) } return parent } @@ -1640,14 +1753,6 @@ core.CSSUnits = function CSSUnits() { return e } 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)) } @@ -1655,19 +1760,6 @@ core.CSSUnits = function CSSUnits() { 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 - } function comparePoints(c1, o1, c2, o2) { if(c1 === c2) { return o2 - o1 @@ -1699,11 +1791,15 @@ core.CSSUnits = function CSSUnits() { } this.adaptRangeDifferenceToZoomLevel = adaptRangeDifferenceToZoomLevel; function getBoundingClientRect(node) { - var doc = (node.ownerDocument), quirks = getBrowserQuirks(), range, element; + var doc = (node.ownerDocument), quirks = getBrowserQuirks(), range, element, rect, body = doc.body; if(quirks.unscaledRangeClientRects === false || quirks.rangeBCRIgnoresElementBCR) { if(node.nodeType === Node.ELEMENT_NODE) { element = (node); - return element.getBoundingClientRect() + rect = element.getBoundingClientRect(); + if(quirks.elementBCRIgnoresBodyScroll) { + return({left:rect.left + body.scrollLeft, right:rect.right + body.scrollLeft, top:rect.top + body.scrollTop, bottom:rect.bottom + body.scrollTop}) + } + return rect } } range = getSharedRange(doc); @@ -1757,17 +1853,20 @@ core.CSSUnits = function CSSUnits() { 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) + var parts = key.split(":"), prefix = parts[0], localName = parts[1], ns = nsResolver(prefix), value = properties[key], valueType = typeof value, element; + if(valueType === "object") { + if(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) } - node.appendChild(element); - mapObjOntoNode(element, (value), nsResolver) }else { if(ns) { + runtime.assert(valueType === "number" || valueType === "string", "attempting to map unsupported type '" + valueType + "' (key: " + key + ")"); node.setAttributeNS(ns, key, String(value)) } } @@ -1790,9 +1889,101 @@ core.CSSUnits = function CSSUnits() { }; return 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) + } + } + } + function removeNode(node) { + if(node.parentNode) { + recentlyModifiedNodes.push(node.previousSibling); + recentlyModifiedNodes.push(node.nextSibling); + node.parentNode.removeChild(node) + } + } + 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)) + } + } + recentlyModifiedNodes.push(node.previousSibling); + recentlyModifiedNodes.push(node.nextSibling) + } + function getStartNode() { + return forwardSelection ? anchorNode : cursorNode + } + function getEndNode() { + return forwardSelection ? cursorNode : anchorNode + } + 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 selectedRange + }; + this.setSelectedRange = function(range, isForwardSelection) { + if(selectedRange && selectedRange !== range) { + selectedRange.detach() + } + 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) + } + 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) + } + 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 @@ -1827,39 +2018,78 @@ core.CSSUnits = function CSSUnits() { @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) - } - }; - 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.') - }; - 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] = [] - } - } +core.Destroyable = function Destroyable() { +}; +core.Destroyable.prototype.destroy = function(callback) { +}; +/* + + 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) + } + }; + this.subscribe = function(eventId, cb) { + runtime.assert(eventListener.hasOwnProperty(eventId), 'tried to subscribe to unknown event "' + eventId + '"'); + eventListener[eventId].push(cb) + }; + 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) + } + }; + 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() }; /* @@ -2076,9 +2306,43 @@ core.PositionIterator = function PositionIterator(root, whatToShow, filter, expa walker.currentNode = currentNode; return sibling }; + function moveToAcceptedNode() { + var node = walker.currentNode, filterResult, moveResult; + filterResult = nodeFilter(node); + if(node !== root) { + node = node.parentNode; + while(node && node !== root) { + if(nodeFilter(node) === FILTER_REJECT) { + walker.currentNode = node; + filterResult = FILTER_REJECT + } + node = node.parentNode + } + } + if(filterResult === FILTER_REJECT) { + currentPos = 1; + moveResult = self.nextPosition() + }else { + if(filterResult === FILTER_ACCEPT) { + moveResult = true + }else { + moveResult = self.nextPosition() + } + } + if(moveResult) { + runtime.assert(nodeFilter(walker.currentNode) === FILTER_ACCEPT, "moveToAcceptedNode did not result in walker being on an accepted node") + } + return moveResult + } + this.setPositionBeforeElement = function(element) { + runtime.assert(Boolean(element), "setPositionBeforeElement called without element"); + walker.currentNode = element; + currentPos = 0; + return moveToAcceptedNode() + }; this.setUnfilteredPosition = function(container, offset) { - var filterResult, node, text; - runtime.assert(container !== null && container !== undefined, "PositionIterator.setUnfilteredPosition called without container"); + var text; + runtime.assert(Boolean(container), "PositionIterator.setUnfilteredPosition called without container"); walker.currentNode = container; if(container.nodeType === TEXT_NODE) { currentPos = offset; @@ -2098,30 +2362,13 @@ core.PositionIterator = function PositionIterator(root, whatToShow, filter, expa } 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 - } - node = node.parentNode - } - if(offset < container.childNodes.length && filterResult !== NodeFilter.FILTER_REJECT) { + if(offset < container.childNodes.length) { 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 + return moveToAcceptedNode() }; this.moveToEnd = function() { walker.currentNode = root; @@ -2137,6 +2384,9 @@ core.PositionIterator = function PositionIterator(root, whatToShow, filter, expa currentPos = 1 } }; + this.isBeforeNode = function() { + return currentPos === 0 + }; this.getNodeFilter = function() { return nodeFilter }; @@ -2149,7 +2399,7 @@ core.PositionIterator = function PositionIterator(root, whatToShow, filter, expa } nodeFilter = (f.acceptNode); nodeFilter.acceptNode = nodeFilter; - whatToShow = whatToShow || 4294967295; + whatToShow = whatToShow || NodeFilter.SHOW_ALL; 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; @@ -2159,6 +2409,29 @@ core.PositionIterator = function PositionIterator(root, whatToShow, filter, expa } init() }; +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 +})(); +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 i; + for(i = 0;i < filterChain.length;i += 1) { + if(filterChain[i].acceptPosition(iterator) === FILTER_REJECT) { + return FILTER_REJECT + } + } + return FILTER_ACCEPT + }; + this.addFilter = function(filterInstance) { + filterChain.push(filterInstance) + } +}; core.zip_HuftNode = function() { this.e = 0; this.b = 0; @@ -2780,8 +3053,126 @@ core.ScheduledTask = function ScheduledTask(fn, delay) { callback() } }; -core.NamedFunction; -core.NamedAsyncFunction; +/* + + Copyright (C) 2014 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.StepIterator = function StepIterator(filter, iterator) { + var FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, cachedContainer, cachedOffset, cachedFilterResult; + function resetCache() { + cachedContainer = null; + cachedOffset = undefined; + cachedFilterResult = undefined + } + function isStep() { + if(cachedFilterResult === undefined) { + cachedFilterResult = filter.acceptPosition(iterator) === FILTER_ACCEPT + } + return(cachedFilterResult) + } + this.isStep = isStep; + function setPosition(newContainer, newOffset) { + resetCache(); + return iterator.setUnfilteredPosition(newContainer, newOffset) + } + this.setPosition = setPosition; + function container() { + if(!cachedContainer) { + cachedContainer = iterator.container() + } + return cachedContainer + } + this.container = container; + function offset() { + if(cachedOffset === undefined) { + cachedOffset = iterator.unfilteredDomOffset() + } + return(cachedOffset) + } + this.offset = offset; + function nextStep() { + resetCache(); + while(iterator.nextPosition()) { + resetCache(); + if(isStep()) { + return true + } + } + return false + } + this.nextStep = nextStep; + function previousStep() { + resetCache(); + while(iterator.previousPosition()) { + resetCache(); + if(isStep()) { + return true + } + } + return false + } + this.previousStep = previousStep; + this.roundToClosestStep = function() { + var currentContainer = container(), currentOffset = offset(), isAtStep = isStep(); + if(!isAtStep) { + isAtStep = previousStep(); + if(!isAtStep) { + setPosition(currentContainer, currentOffset); + isAtStep = nextStep() + } + } + return isAtStep + }; + this.roundToPreviousStep = function() { + var isAtStep = isStep(); + if(!isAtStep) { + isAtStep = previousStep() + } + return isAtStep + }; + this.roundToNextStep = function() { + var isAtStep = isStep(); + if(!isAtStep) { + isAtStep = nextStep() + } + return isAtStep + } +}; +core.TestData; +core.AsyncTestData; core.UnitTest = function UnitTest() { }; core.UnitTest.prototype.setUp = function() { @@ -2818,17 +3209,61 @@ core.UnitTest.createOdtDocument = function(xml, namespaceMap) { xmlDoc += ""; return runtime.parseXML(xmlDoc) }; -core.UnitTestRunner = function UnitTestRunner() { - var failedTests = 0, areObjectsEqual; +core.UnitTestLogger = function UnitTestLogger() { + var messages = [], errors = 0, start = 0, suite = "", test = ""; + this.startTest = function(suiteName, testName) { + messages = []; + errors = 0; + suite = suiteName; + test = testName; + start = (new Date).getTime() + }; + this.endTest = function() { + var end = (new Date).getTime(); + return{description:test, suite:[suite, test], success:errors === 0, log:messages, time:end - start} + }; + this.debug = function(msg) { + messages.push({category:"debug", message:msg}) + }; + this.fail = function(msg) { + errors += 1; + messages.push({category:"fail", message:msg}) + }; + this.pass = function(msg) { + messages.push({category:"pass", message:msg}) + } +}; +core.UnitTestRunner = function UnitTestRunner(resourcePrefix, logger) { + var failedTests = 0, failedTestsOnBeginExpectFail, areObjectsEqual, expectFail = false; + this.resourcePrefix = function() { + return resourcePrefix + }; + this.beginExpectFail = function() { + failedTestsOnBeginExpectFail = failedTests; + expectFail = true + }; + this.endExpectFail = function() { + var hasNoFailedTests = failedTestsOnBeginExpectFail === failedTests; + expectFail = false; + failedTests = failedTestsOnBeginExpectFail; + if(hasNoFailedTests) { + failedTests += 1; + logger.fail("Expected at least one failed test, but none registered.") + } + }; function debug(msg) { - runtime.log(msg) + logger.debug(msg) } function testFailed(msg) { failedTests += 1; - runtime.log("fail", msg) + if(!expectFail) { + logger.fail(msg) + }else { + logger.debug(msg) + } } function testPassed(msg) { - runtime.log("pass", msg) + logger.pass(msg) } function areArraysEqual(a, b) { var i; @@ -2917,6 +3352,9 @@ core.UnitTestRunner = function UnitTestRunner() { if(actual === expected) { return true } + if(actual === null || expected === null) { + return false + } if(typeof expected === "number" && isNaN(expected)) { return typeof actual === "number" && isNaN(actual) } @@ -2925,9 +3363,9 @@ core.UnitTestRunner = function UnitTestRunner() { } if(typeof expected === "object" && typeof actual === "object") { if((expected).constructor === Element || (expected).constructor === Node) { - return areNodesEqual((expected), (actual)) + return areNodesEqual((actual), (expected)) } - return areObjectsEqual((expected), (actual)) + return areObjectsEqual((actual), (expected)) } return false } @@ -2999,6 +3437,7 @@ core.UnitTestRunner = function UnitTestRunner() { this.shouldBeNull = shouldBeNull; this.shouldBeNonNull = shouldBeNonNull; this.shouldBe = shouldBe; + this.testFailed = testFailed; this.countFailedTests = function() { return failedTests }; @@ -3016,12 +3455,32 @@ core.UnitTestRunner = function UnitTestRunner() { } }; core.UnitTester = function UnitTester() { - var failedTests = 0, results = {}; + var self = this, failedTests = 0, logger = new core.UnitTestLogger, results = {}, inBrowser = runtime.type() === "BrowserRuntime"; + this.resourcePrefix = ""; function link(text, code) { return"" + text + "" } + this.reporter = function(r) { + var i, m; + if(inBrowser) { + runtime.log("Running " + link(r.description, 'runTest("' + r.suite[0] + '","' + r.description + '")') + "") + }else { + runtime.log("Running " + r.description) + } + if(!r.success) { + for(i = 0;i < r.log.length;i += 1) { + m = r.log[i]; + runtime.log(m.category, m.message) + } + } + }; + function report(r) { + if(self.reporter) { + self.reporter(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, inBrowser = runtime.type() === "BrowserRuntime"; + var testName = Runtime.getFunctionName(TestClass) || "", tname, runner = new core.UnitTestRunner(self.resourcePrefix, logger), test = new TestClass(runner), testResults = {}, i, t, tests, texpectFail, lastFailCount; if(results.hasOwnProperty(testName)) { runtime.log("Test " + testName + " has already run."); return @@ -3035,17 +3494,25 @@ core.UnitTester = function UnitTester() { for(i = 0;i < tests.length;i += 1) { t = tests[i].f; tname = tests[i].name; + texpectFail = tests[i].expectFail === true; if(testNames.length && testNames.indexOf(tname) === -1) { continue } - if(inBrowser) { - runtime.log("Running " + link(tname, 'runTest("' + testName + '","' + tname + '")') + "") - }else { - runtime.log("Running " + tname) - } lastFailCount = runner.countFailedTests(); test.setUp(); - t(); + logger.startTest(testName, tname); + if(texpectFail) { + runner.beginExpectFail() + } + try { + t() + }catch(e) { + runner.testFailed("Unexpected exception encountered: " + e.toString() + "\n" + e.stack) + } + if(texpectFail) { + runner.endExpectFail() + } + report(logger.endTest()); test.tearDown(); testResults[tname] = lastFailCount === runner.countFailedTests() } @@ -3057,15 +3524,26 @@ core.UnitTester = function UnitTester() { return } t = todo[0].f; - var fname = todo[0].name; - runtime.log("Running " + fname); + var fname = todo[0].name, expectFail = todo[0].expectFail === true; lastFailCount = runner.countFailedTests(); - test.setUp(); - t(function() { - test.tearDown(); - testResults[fname] = lastFailCount === runner.countFailedTests(); + if(testNames.length && testNames.indexOf(fname) === -1) { runAsyncTests(todo.slice(1)) - }) + }else { + test.setUp(); + logger.startTest(testName, fname); + if(expectFail) { + runner.beginExpectFail() + } + t(function() { + if(expectFail) { + runner.endExpectFail() + } + report(logger.endTest()); + test.tearDown(); + testResults[fname] = lastFailCount === runner.countFailedTests(); + runAsyncTests(todo.slice(1)) + }) + } } runAsyncTests(test.asyncTests()) }; @@ -3127,10 +3605,6 @@ core.Utils = function Utils() { 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) { @@ -3504,104 +3978,13 @@ core.Zip = function Zip(url, 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.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() { - 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() +xmldom.LSSerializerFilter = function LSSerializerFilter() { }; -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() +xmldom.LSSerializerFilter.prototype.acceptNode = function(node) { }; /* - 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 @@ -3636,69 +4019,21 @@ gui.EditInfoHandle = function EditInfoHandle(parentElement) { @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 = {} - }; - this.handleEvent = function(e) { - var keyCombo = getKeyCombo(e.keyCode, getModifiers(e)), callback = bindings[keyCombo], handled = false; - if(callback) { - handled = callback() +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(defaultBinding !== null) { - handled = defaultBinding(e) - } - } - if(handled) { - if(e.preventDefault) { - e.preventDefault() + if(node.namespaceURI && node.namespaceURI.match(/^urn:webodf:/)) { + result = NodeFilter.FILTER_REJECT }else { - e.returnValue = false + result = NodeFilter.FILTER_ACCEPT } } + return result } }; -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 @@ -3768,507 +4103,251 @@ odf.Namespaces.lookupPrefix = function lookupPrefix(namespaceURI) { return map.hasOwnProperty(namespaceURI) ? map[namespaceURI] : null }; odf.Namespaces.lookupNamespaceURI.lookupNamespaceURI = odf.Namespaces.lookupNamespaceURI; -/* - - 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.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) { - return e !== null && (e.nodeType === Node.ELEMENT_NODE && (e.localName === "frame" && (e.namespaceURI === drawns && (e).getAttributeNS(textns, "anchor-type") === "as-char"))) - } - this.isCharacterFrame = isCharacterFrame; - function isAnnotation(e) { - var name = e && e.localName; - return name === "annotation" && e.namespaceURI === odf.Namespaces.officens - } - function isAnnotationWrapper(e) { - var name = e && e.localName; - return name === "div" && (e).className === "annotationWrapper" - } - function isInlineRoot(e) { - return isAnnotation(e) || isAnnotationWrapper(e) - } - this.isInlineRoot = isInlineRoot; - 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 - } - return 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)) } - this.getParagraphElement = getParagraphElement; - this.isWithinTrackedChanges = function(node, container) { - while(node && node !== container) { - if(node.namespaceURI === textns && node.localName === "tracked-changes") { - return true + 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 + } } - 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) - } - this.isODFWhitespace = isODFWhitespace; - function isGroupingElement(n) { - if(n === null || n.nodeType !== Node.ELEMENT_NODE) { - return false } - var e = (n), localName = e.localName; - return/^(span|p|h|a|meta)$/.test(localName) && e.namespaceURI === textns || localName === "span" && e.className === "annotationHighlight" + steps.push({location:location, predicates:predicates}); + return pos } - 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") + 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 r + return{steps:steps, value:value} } - this.isCharacterElement = isCharacterElement; - function isAnchoredAsCharacterElement(e) { - return isCharacterElement(e) || (isCharacterFrame(e) || isInlineRoot(e)) - } - this.isAnchoredAsCharacterElement = isAnchoredAsCharacterElement; - function isSpaceElement(e) { - var n = e && e.localName, ns, r = false; - if(n) { - ns = e.namespaceURI; - if(ns === textns) { - r = n === "s" + 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 r - } - this.isSpaceElement = isSpaceElement; - function firstChild(node) { - while(node.firstChild !== null && isGroupingElement(node)) { - node = node.firstChild - } - return node - } - this.firstChild = firstChild; - function lastChild(node) { - while(node.lastChild !== null && isGroupingElement(node)) { - node = node.lastChild - } - return node - } - this.lastChild = lastChild; - function previousNode(node) { - while(!isParagraph(node) && node.previousSibling === null) { - node = (node.parentNode) + return pos + }; + 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 } - return isParagraph(node) ? null : lastChild((node.previousSibling)) } - this.previousNode = previousNode; - function nextNode(node) { - while(!isParagraph(node) && node.nextSibling === null) { - node = (node.parentNode) + 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 } - 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) + 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 { - return!isODFWhitespace(text.data.substr(text.length - 1, 1)) + do { + node = root.firstChild; + if(!node) { + root = it.next() + } + }while(root && !node) } - }else { - if(isAnchoredAsCharacterElement(node)) { - r = isSpaceElement(node) === false; - node = null - }else { - node = previousNode(node) + if(node && node.nodeType === Node.ELEMENT_NODE) { + return node } } + 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() + } + return n } - return r } - this.scanLeftForNonSpace = scanLeftForNonSpace; - function lookLeftForCharacter(node) { - var text, r = 0, tl = 0; - if(node.nodeType === Node.TEXT_NODE) { - tl = (node).length + 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() !== null + }) } - if(tl > 0) { - text = (node).data; - if(!isODFWhitespace(text.substr(tl - 1, 1))) { - r = 1 + return new ConditionIterator(it, function(node) { + nit.setNode(node); + pit.reset(); + var n = pit.next(); + return n ? n.nodeValue === value : false + }) + } + 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 { - if(tl === 1) { - r = scanLeftForNonSpace(previousNode(node)) ? 2 : 0 + 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 { - r = isODFWhitespace(text.substr(tl - 2, 1)) ? 0 : 2 + if(location !== ".") { + it = new AllChildElementIterator(it, false); + if(location.indexOf(":") !== -1) { + it = createNodenameFilter(it, location, namespaceResolver) + } + } } } - }else { - if(isAnchoredAsCharacterElement(node)) { - r = 1 + for(j = 0;j < step.predicates.length;j += 1) { + p = item(step.predicates, j); + it = createPredicateFilteredIterator(it, p, namespaceResolver) } } - return r - } - this.lookLeftForCharacter = lookLeftForCharacter; - function lookRightForCharacter(node) { - var r = false, l = 0; - if(node && node.nodeType === Node.TEXT_NODE) { - l = (node).length + 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(l > 0) { - r = !isODFWhitespace((node).data.substr(0, 1)) + 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 { - if(isAnchoredAsCharacterElement(node)) { - r = true + 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() } } - return r + return elements } - 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 { - l = 0 - } - if(l > 0 && !isODFWhitespace((node).data)) { - r = true; - break - } - if(isAnchoredAsCharacterElement(node)) { - r = true; - break - } - node = previousNode(node) - } - return r - } - 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 - } - if(l > 0 && !isODFWhitespace((node).data)) { - r = true; - break - } - if(isAnchoredAsCharacterElement(node)) { - r = true; - break - } - node = nextNode(node) - } - return r - } - this.scanRightForAnyCharacter = scanRightForAnyCharacter; - function isTrailingWhitespace(textnode, offset) { - if(!isODFWhitespace(textnode.data.substr(offset))) { - return false - } - return!scanRightForAnyCharacter(nextNode(textnode)) - } - this.isTrailingWhitespace = isTrailingWhitespace; - function isSignificantWhitespace(textNode, offset) { - var text = textNode.data, result; - if(!isODFWhitespace(text[offset])) { - return false - } - if(isAnchoredAsCharacterElement(textNode.parentNode)) { - return false - } - if(offset > 0) { - if(!isODFWhitespace(text[offset - 1])) { - result = true - } - }else { - if(scanLeftForNonSpace(previousNode(textNode))) { - result = 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 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 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) - } - } - 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 - } - 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(domUtils.rangesIntersect(range, nodeRange)) { - if(isAcceptedNode(node)) { - return NodeFilter.FILTER_SKIP - } - } - } - 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 - } - if(node.nodeType === Node.TEXT_NODE) { - if(includeNode(range, nodeRange, includePartial)) { - if(includeInsignificantWhitespace || isSignificantTextContent((node))) { - return NodeFilter.FILTER_ACCEPT - } - } - }else { - if(isAnchoredAsCharacterElement(node)) { - if(includeNode(range, nodeRange, includePartial)) { - return NodeFilter.FILTER_ACCEPT - } - }else { - if(isAcceptedNode(node) || isGroupingElement(node)) { - return NodeFilter.FILTER_SKIP - } - } - } - return NodeFilter.FILTER_REJECT - } - 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 - } - }else { - if(isAcceptedNode(node) || isGroupingElement(node)) { - return NodeFilter.FILTER_SKIP - } - } - 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 - } - return NodeFilter.FILTER_SKIP - } - elements = domUtils.getNodesInRange(range, nodeFilter); - nodeRange.detach(); - return elements - } -}; + return{getODFElementsWithXPath:getODFElementsWithXPath} +} +xmldom.XPath = createXPathSingleton(); /* - 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 @@ -4303,400 +4382,489 @@ odf.OdfUtils = function OdfUtils() { @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) { -}; -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 - } - } +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 - }; - 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 + 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) { - if(node.nodeType === Node.ELEMENT_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() !== null - }) + 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 : false - }) } - function item(p, i) { - return p[i] + 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) + } } - 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 { - 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) - } - } + 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 } - for(j = 0;j < step.predicates.length;j += 1) { - p = item(step.predicates, j); - it = createPredicateFilteredIterator(it, p, namespaceResolver) + removeRegExpFromUsedStyleNames(styleElementsRoot, regExp); + if(styleUsingElementsRoot) { + removeRegExpFromUsedStyleNames(styleUsingElementsRoot, regExp) } } - 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() - } - 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() + 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 } } - return elements - } - 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) + 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 + } } } + return usedStyles } - function removeNode(node) { - if(node.parentNode) { - recentlyModifiedNodes.push(node.previousSibling); - recentlyModifiedNodes.push(node.nextSibling); - node.parentNode.removeChild(node) - } - } - 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 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 } - recentlyModifiedNodes.push(node.previousSibling); - recentlyModifiedNodes.push(node.nextSibling) } - function getStartNode() { - return forwardSelection ? anchorNode : cursorNode + function StyleDefinition(key, name, family) { + this.key = key; + this.name = name; + this.family = family; + this.requires = {} } - function getEndNode() { - return forwardSelection ? cursorNode : anchorNode + function getStyleDefinition(stylename, stylefamily, knownStyles) { + var styleKey = stylename + '"' + stylefamily, styleDefinition = knownStyles[styleKey]; + if(!styleDefinition) { + styleDefinition = knownStyles[styleKey] = new StyleDefinition(styleKey, stylename, stylefamily) + } + return styleDefinition } - 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 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) } - return selectedRange - }; - this.setSelectedRange = function(range, isForwardSelection) { - if(selectedRange && selectedRange !== range) { - selectedRange.detach() + 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 + } + } } - 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) + e = element.firstElementChild; + while(e) { + determineDependentStyles(e, styleScope, knownStyles); + e = e.nextElementSibling } - 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) + return knownStyles } - 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 + 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}) } } } - return FILTER_ACCEPT - }; - this.addFilter = function(filterName, filterInstance) { - filterChain[filterName] = filterInstance - }; - this.removeFilter = function(filterName) { - delete filterChain[filterName] + return e } -}; -/* - - 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. + 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, 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 + } + } + e = styleElement && styleElement.firstElementChild; + while(e) { + currentElement = e; + localNames.forEach(collectByAttribute); + collectUsedFontFaces(usedFontFaceDeclMap, currentElement); + e = e.nextElementSibling + } + } + 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]) + } + } + e = styleElement && styleElement.firstElementChild; + while(e) { + currentElement = e; + localNames.forEach(changeFontFaceNameByAttribute); + changeFontFaceNames(currentElement, fontFaceNameChangeMap); + e = e.nextElementSibling + } + } + 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.hasDerivedStyles = hasDerivedStyles; + this.prefixStyleNames = prefixStyleNames; + this.removePrefixFromStyleNames = removePrefixFromStyleNames; + this.determineStylesForNode = determineStylesForNode; + elements = inverse() +}; +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 + } + } + 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 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) { + 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) + } + } + } + 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 + '"' + } + } + } + } + s += attstr + ">"; + return s + } + 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) + } + 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(qname) { + s += ""; + ns.pop() + } + return s + } + this.filter = null; + this.writeToString = function(node, nsmap) { + if(!node) { + return"" + } + var ns = new Namespaces(nsmap); + return serializeNode(ns, node) + } +}; +/* + + 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 . @@ -4723,1545 +4891,697 @@ core.PositionFilterChain = function PositionFilterChain() { @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) +(function() { + var styleInfo = new odf.StyleInfo, domUtils = new core.DomUtils, 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 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) - }) - } - range.detach() - } - 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) + 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 } - container.parentNode.removeChild(container) } + return-1 } - 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 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" + 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 } - 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)" - } - function showAnnotationsPane(show) { - var sizer = canvas.getSizer(); - if(show) { - annotationsPane.style.display = "inline-block"; - sizer.style.paddingRight = window.getComputedStyle(annotationsPane).width - }else { - annotationsPane.style.display = "none"; - sizer.style.paddingRight = 0 - } - canvas.refreshSize() } - function sortAnnotations() { - annotations.sort(function(a, b) { - if(a.node.compareDocumentPosition(b.node) === Node.DOCUMENT_POSITION_FOLLOWING) { - return-1 + 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 1 - }) - } - function rerenderAnnotations() { - var i; - for(i = 0;i < annotations.length;i += 1) { - renderAnnotation(annotations[i]) - } - } - 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() - } - this.addAnnotation = addAnnotation; - function forgetAnnotation(annotation) { - var index = annotations.indexOf(annotation); - unwrapAnnotation(annotation); - unhighlightAnnotation(annotation); - if(index !== -1) { - annotations.splice(index, 1) - } - if(annotations.length === 0) { - showAnnotationsPane(false) - } - } - function forgetAnnotations() { - while(annotations.length) { - forgetAnnotation(annotations[0]) + return result } } - 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 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) - } - if(rectangle) { - simplifiedRectangle = {top:rectangle.top, left:useRightEdge ? rectangle.right : rectangle.left, bottom:rectangle.bottom} + function setChild(node, child) { + if(!child) { + return } - 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) + var childpos = getNodePosition(child), pos, c = node.firstChild; + if(childpos === -1) { + return } - 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(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) - } - } - } + while(c) { + pos = getNodePosition(c); + if(pos !== -1 && pos > childpos) { + break } + c = c.nextSibling } - runtime.assert(Boolean(rectangle), "No visible rectangle found"); - return(rectangle) - } - 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(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) - } - 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 - } - runtime.clearTimeout(timeoutHandle); - timeoutHandle = runtime.setTimeout(function() { - cachedXOffset = undefined - }, 2E3); - range.detach(); - return positions - left + node.insertBefore(child, c) } - this.movePointForward = function(positions, extend) { - return doMove(positions, extend || false, positionIterator.nextPosition) + odf.ODFElement = function ODFElement() { }; - this.movePointBackward = function(positions, extend) { - return doMove(positions, extend || false, positionIterator.previousPosition) + odf.ODFDocumentElement = function ODFDocumentElement() { }; - 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 + 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.AnnotationElement = function AnnotationElement() { + }; + odf.AnnotationElement.prototype.annotationEndElement; + 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 } - } - 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 - } - } - return positions - } - 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 + 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 stepsFilter2 - } - 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 + }; + odf.OdfPart.prototype.load = function() { + }; + odf.OdfPart.prototype.getUrl = function() { + if(this.data) { + return"data:;base64," + base64.toBase64(this.data) + } + 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 { + if(n.nodeType === Node.PROCESSING_INSTRUCTION_NODE) { + element.removeChild(n) + } } + n = next } } - return stepsFilter2 - } - function countStepsPublic(steps, filter) { - var iterator = getIteratorAtCursor(); - return countSteps(iterator, steps, filter) - } - 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 linkAnnotationStartAndEndElements(rootElement) { + var document = rootElement.ownerDocument, annotationStarts = {}, n, name, annotationStart, nodeIterator = document.createNodeIterator(rootElement, NodeFilter.SHOW_ELEMENT, null, false); + n = (nodeIterator.nextNode()); + while(n) { + if(n.namespaceURI === officens) { + if(n.localName === "annotation") { + name = n.getAttributeNS(officens, "name"); + if(name) { + if(annotationStarts.hasOwnProperty(name)) { + runtime.log("Warning: annotation name used more than once with : '" + name + "'") + }else { + annotationStarts[name] = n + } + } + }else { + if(n.localName === "annotation-end") { + name = n.getAttributeNS(officens, "name"); + if(name) { + if(annotationStarts.hasOwnProperty(name)) { + annotationStart = (annotationStarts[name]); + if(!annotationStart.annotationEndElement) { + annotationStart.annotationEndElement = n + }else { + runtime.log("Warning: annotation name used more than once with : '" + name + "'") + } + }else { + runtime.log("Warning: annotation end without an annotation start, name: '" + name + "'") + } + }else { + runtime.log("Warning: annotation end without a name found") + } + } + } + } + n = (nodeIterator.nextNode()) } } - 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 - } - 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 - } + function setAutomaticStylesScope(stylesRootElement, scope) { + var n = stylesRootElement && stylesRootElement.firstChild; + while(n) { + if(n.nodeType === Node.ELEMENT_NODE) { + (n).setAttributeNS(webodfns, "scope", scope) } + n = n.nextSibling } } - if(bestContainer !== null) { - iterator.setUnfilteredPosition(bestContainer, (bestOffset)); - steps = bestCount - }else { - steps = 0 + function getEnsuredMetaElement() { + var root = self.rootElement, meta = root.meta; + if(!meta) { + root.meta = meta = document.createElementNS(officens, "meta"); + setChild(root, meta) + } + return meta } - range.detach(); - return steps - } - 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 getMetaData(metadataNs, metadataLocalName) { + var node = self.rootElement.meta, textNode; + node = node && node.firstChild; + while(node && (node.namespaceURI !== metadataNs || node.localName !== metadataLocalName)) { + node = node.nextSibling } - steps += stepCount; - lines -= 1 + node = node && node.firstChild; + while(node && node.nodeType !== Node.TEXT_NODE) { + node = node.nextSibling + } + if(node) { + textNode = (node); + return textNode.data + } + 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 + 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 } - 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 - } + 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 } - steps += increment; - lastRect = rect + fn = fn.nextSibling } + return result } - 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() - } - 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() - } - 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 + 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 } - }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 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 } } + return copy } - return steps - } - 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) - } - 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 - } - } - var filter = new CursorFilter; - return new core.PositionIterator(rootNode, 5, filter, false) -}; -(function() { - return gui.SelectionMover -})(); -/* - - 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 - } -}; -/* - - 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 { - if(node.namespaceURI === stylens && (node.localName === "page-layout" || node.localName === "default-page-layout")) { - family = "page" - }else { - family = undefined - } - } - } - if(family) { - name = node.getAttributeNS(stylens, "name"); - if(!name) { - name = "" - } - if(stylemap.hasOwnProperty(family)) { - style = stylemap[family] - }else { - stylemap[family] = style = {} - } - style[name] = node - } - node = node.nextElementSibling - } - return stylemap - } - 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 - } - } - } - return style - } - function addStyleToStyleTree(stylename, stylesmap, stylestree) { - var style, parentname, parentstyle; - if(!stylesmap.hasOwnProperty(stylename)) { - return null - } - 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 { - stylestree[stylename] = style - } - delete stylesmap[stylename]; - return style - } - function addStyleMapToStyleTree(stylesmap, stylestree) { - var name; - for(name in stylesmap) { - if(stylesmap.hasOwnProperty(name)) { - addStyleToStyleTree(name, stylesmap, stylestree) - } - } - } - function createSelector(family, name) { - var prefix = familynamespaceprefixes[family], namepart, selector; - if(prefix === undefined) { - return null - } - if(name) { - namepart = "[" + prefix + '|style-name="' + name + '"]' - }else { - namepart = "" - } - if(prefix === "presentation") { - prefix = "draw"; - if(name) { - namepart = '[presentation|style-name="' + name + '"]' - }else { - namepart = "" - } - } - 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) - } - for(n in derivedStyles) { - if(derivedStyles.hasOwnProperty(n)) { - ss = getSelectors(family, n, derivedStyles[n]); - selectors = selectors.concat(ss) - } - } - return selectors - } - function getDirectChild(node, ns, name) { - var e = node && node.firstElementChild; - while(e) { - if(e.namespaceURI === ns && e.localName === name) { - break - } - e = e.nextElementSibling - } - return e - } - 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 = "" - } - width = utils.parseLength(width); - if(width && (width.unit === "pt" && width.value < 0.75)) { - value = "0.75pt" + theRestOfBorderAttributes - } - return value - } - 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) - } - if(r[2]) { - rule += r[2] + ":" + value + ";" - } - } - } - return rule - } - function getFontSize(styleNode) { - var props = getDirectChild(styleNode, stylens, "text-properties"); - if(props) { - return utils.parseFoFontSize(props.getAttributeNS(fons, "font-size")) - } - 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 { - xp = "//style:default-style[@style:family='" + parentStyleFamily + "']" - } - parentStyleNode = xpath.getODFElementsWithXPath((odfRoot), xp, odf.Namespaces.lookupNamespaceURI)[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 - } - fontName = props.getAttributeNS(stylens, "font-name") || props.getAttributeNS(fons, "font-family"); - if(fontName) { - value = fontFaceDeclsMap[fontName]; - rule += "font-family: " + (value || fontName) + ";" - } - 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) - } - if(!fontSizeRule) { - fontSizeRule = "font-size: " + parseFloat(defaultFontSize) * sizeMultiplier + cssUnits.getUnits(defaultFontSize) + ";" - } - rule += fontSizeRule; - return rule - } - 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) - } - } - 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 rule - } - function matchToRgb(m, r, g, b) { - return r + r + g + g + b + b - } - 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 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 + ");" - } - }else { - rule += "background: none;" - } - }else { - if(fill === "none") { - rule += "background: none;" - } - } - return rule - } - function getDrawingPageProperties(props) { - var rule = ""; - rule += applySimpleMapping(props, graphicPropertySimpleMapping); - if(props.getAttributeNS(presentationns, "background-visible") === "true") { - rule += "background: none;" - } - 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;" - } - } - 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) - } - properties = getDirectChild(node.element, stylens, "paragraph-properties"); - if(properties) { - rule += getParagraphProperties(properties) - } - 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 { - if(style) { - content += ' "' + style + '"' - }else { - content += " ''" - } - } - return"content:" + content + ' "' + suffix + '"' - } - 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") - } - 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) - } - } - 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) - } - } - 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 - } - }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 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 - } - } - 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) - } - } - } - 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]) - } - } - } - 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]) - } - } - } - } - } -}; -/* - - 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 + 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 false - } - 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 + 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 } - 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) + function setState(state) { + self.state = state; + if(self.onchange) { + self.onchange(self) + } + if(self.onstatereadychange) { + self.onstatereadychange(self) } } - e = element.firstElementChild; - while(e) { - prefixUsedStyleNames(e, prefix); - e = e.nextElementSibling + 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"); + linkAnnotationStartAndEndElements(root) } - } - 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 + function handleFlatXml(xmldoc) { + var root = importRootNode(xmldoc); + if(!root || (root.localName !== "document" || root.namespaceURI !== officens)) { + setState(OdfContainer.INVALID); + return } + setRootElement((root)); + setState(OdfContainer.DONE) } - if(ns) { - styleElement.setAttributeNS(ns, nsprefixes[ns] + "name", prefix + stylename) + 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) } - } - function prefixStyleNames(styleElementsRoot, prefix, styleUsingElementsRoot) { - var s; - if(styleElementsRoot) { - s = styleElementsRoot.firstChild; - while(s) { - if(s.nodeType === Node.ELEMENT_NODE) { - prefixStyleName((s), prefix) + 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(fontFaceDecls) { + root.fontFaceDecls = fontFaceDecls; + setChild(root, fontFaceDecls) } - s = s.nextSibling } - prefixUsedStyleNames(styleElementsRoot, prefix); - if(styleUsingElementsRoot) { - prefixUsedStyleNames(styleUsingElementsRoot, prefix) + automaticStyles = getDirectChild(node, officens, "automatic-styles"); + setAutomaticStylesScope(automaticStyles, documentContentScope); + if(fontFaceNameChangeMap) { + styleInfo.changeFontFaceNames(automaticStyles, fontFaceNameChangeMap) } - } - } - 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 + 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) + } } - } - 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) + node = getDirectChild(node, officens, "body"); + if(node === null) { + throw" tag is mising."; } + root.body = node; + setChild(root, root.body) } - e = element.firstElementChild; - while(e) { - removeRegExpFromUsedStyleNames(e, regExp); - e = e.nextElementSibling - } - } - 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 + function handleMetaXml(xmldoc) { + var node = importRootNode(xmldoc), root; + if(!node || (node.localName !== "document-meta" || node.namespaceURI !== officens)) { + return } + root = self.rootElement; + root.meta = getDirectChild(node, officens, "meta"); + setChild(root, root.meta) } - if(ns) { - stylename = stylename.replace(regExp, ""); - styleElement.setAttributeNS(ns, nsprefixes[ns] + "name", stylename) + 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 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) + 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") } - s = s.nextSibling - } - removeRegExpFromUsedStyleNames(styleElementsRoot, regExp); - if(styleUsingElementsRoot) { - removeRegExpFromUsedStyleNames(styleUsingElementsRoot, regExp) + e = e.nextElementSibling } } - } - 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 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 { + linkAnnotationStartAndEndElements(self.rootElement); + setState(OdfContainer.DONE) } } - 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 - } - } + 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) } - return usedStyles - } - 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) + function createDocumentElement(name) { + var s = ""; + function defineNamespace(prefix, ns) { + s += " xmlns:" + prefix + '="' + ns + '"' } - i = i.nextSibling + odf.Namespaces.forEachPrefix(defineNamespace); + return'' } - } - 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) + 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 } - 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) + 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 } - if(styleScope) { - elname = elements[element.localName]; - if(elname) { - elns = elname[element.namespaceURI]; - if(elns) { - length = elns.length + 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])) } } - 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 - } + 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; + if(self.rootElement.settings.firstElementChild) { + s += serializer.writeToString(self.rootElement.settings, odf.Namespaces.namespaceMap) } + s += ""; + return s } - e = element.firstElementChild; - while(e) { - determineDependentStyles(e, styleScope, knownStyles); - e = e.nextElementSibling + 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 } - return knownStyles - } - 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}) + 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 } - return e - } - function mergeRequiredStyles(styleDependency, usedStyles) { - var family = usedStyles[styleDependency.family]; - if(!family) { - family = usedStyles[styleDependency.family] = {} + function loadFromXML(url, callback) { + runtime.loadXML(url, function(err, dom) { + if(err) { + callback(err) + }else { + handleFlatXml(dom) + } + }) } - 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) + 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")) } - }) - } - 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 + if(!contentElement) { + throw"Could not find content element in ."; + } + return contentElement + }; + this.getDocumentType = function() { + var content = self.getContentElement(); + return content && content.localName + }; + this.getPart = function(partname) { + return new odf.OdfPart(partname, partMimetypes[partname], self, zip) + }; + this.getPartData = function(url, callback) { + zip.load(url, callback) + }; + function setMetadata(setProperties, removedPropertyNames) { + var metaElement = getEnsuredMetaElement(); + if(setProperties) { + domUtils.mapKeyValObjOntoNode(metaElement, setProperties, odf.Namespaces.lookupNamespaceURI) + } + if(removedPropertyNames) { + domUtils.removeKeyElementsFromNode(metaElement, removedPropertyNames, odf.Namespaces.lookupNamespaceURI) + } + } + this.setMetadata = setMetadata; + this.incrementEditingCycles = function() { + var currentValueString = getMetaData(odf.Namespaces.metans, "editing-cycles"), currentCycles = currentValueString ? parseInt(currentValueString, 10) : 0; + if(isNaN(currentCycles)) { + currentCycles = 0 + } + setMetadata({"meta:editing-cycles":currentCycles + 1}, null) + }; + function updateMetadataForSaving() { + var generatorString, window = runtime.getWindow(); + generatorString = "WebODF/" + (String(typeof webodf_version) !== "undefined" ? webodf_version : "FromSource"); + if(window) { + generatorString = generatorString + " " + window.navigator.userAgent + } + 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); + partMimetypes["/"] = "application/vnd.oasis.opendocument.text"; + partMimetypes["settings.xml"] = "text/xml"; + partMimetypes["meta.xml"] = "text/xml"; + partMimetypes["styles.xml"] = "text/xml"; + partMimetypes["content.xml"] = "text/xml"; + setState(OdfContainer.DONE); + return emptyzip } - e = styleElement && styleElement.firstElementChild; - while(e) { - currentElement = e; - localNames.forEach(collectByAttribute); - collectUsedFontFaces(usedFontFaceDeclMap, currentElement); - e = e.nextElementSibling + 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) } - } - 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]) - } + function createByteArray(successCallback, errorCallback) { + fillZip(); + zip.createByteArray(successCallback, errorCallback) } - e = styleElement && styleElement.firstElementChild; - while(e) { - currentElement = e; - localNames.forEach(changeFontFaceNameByAttribute); - changeFontFaceNames(currentElement, fontFaceNameChangeMap); - e = e.nextElementSibling + this.createByteArray = createByteArray; + function saveAs(newurl, callback) { + fillZip(); + zip.writeAs(newurl, function(err) { + callback(err) + }) } - } - 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 - } + 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.") } - map = usedStyles[keyName]; - return map ? map[name] > 0 : false + partMimetypes[filename] = mimetype }; - determineUsedStyles(styleUsingElementsRoot, usedStyles); - if(automaticStylesRoot) { - mergeUsedAutomaticStyles(automaticStylesRoot, usedStyles) + 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() } }; - this.hasDerivedStyles = hasDerivedStyles; - this.prefixStyleNames = prefixStyleNames; - this.removePrefixFromStyleNames = removePrefixFromStyleNames; - this.determineStylesForNode = determineStylesForNode; - elements = inverse() -}; + 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 @@ -6296,370 +5616,506 @@ odf.StyleInfo = function StyleInfo() { @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 +odf.OdfUtils = function OdfUtils() { + var textns = odf.Namespaces.textns, drawns = odf.Namespaces.drawns, xlinkns = odf.Namespaces.xlinkns, 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) { + return e !== null && (e.nodeType === Node.ELEMENT_NODE && (e.localName === "frame" && (e.namespaceURI === drawns && (e).getAttributeNS(textns, "anchor-type") === "as-char"))) + } + this.isCharacterFrame = isCharacterFrame; + function isAnnotation(e) { + var name = e && e.localName; + return name === "annotation" && e.namespaceURI === odf.Namespaces.officens + } + function isAnnotationWrapper(e) { + var name = e && e.localName; + return name === "div" && (e).className === "annotationWrapper" + } + function isInlineRoot(e) { + return isAnnotation(e) || isAnnotationWrapper(e) + } + this.isInlineRoot = isInlineRoot; + this.isTextSpan = function(e) { + var name = e && e.localName; + return name === "span" && e.namespaceURI === textns + }; + function isHyperlink(node) { + var name = node && node.localName; + return name === "a" && node.namespaceURI === textns + } + this.isHyperlink = isHyperlink; + this.getHyperlinkTarget = function(element) { + return element.getAttributeNS(xlinkns, "href") + }; + 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 + } + return(node) + } + 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 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) + } + this.isODFWhitespace = isODFWhitespace; + function isGroupingElement(n) { + if(n === null || n.nodeType !== Node.ELEMENT_NODE) { + return false + } + 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") + } + } + return r + } + this.isCharacterElement = isCharacterElement; + function isAnchoredAsCharacterElement(e) { + return isCharacterElement(e) || (isCharacterFrame(e) || isInlineRoot(e)) + } + this.isAnchoredAsCharacterElement = isAnchoredAsCharacterElement; + function isSpaceElement(e) { + var n = e && e.localName, ns, r = false; + if(n) { + ns = e.namespaceURI; + if(ns === textns) { + r = n === "s" + } + } + return r + } + this.isSpaceElement = isSpaceElement; + function firstChild(node) { + while(node.firstChild !== null && isGroupingElement(node)) { + node = node.firstChild + } + return node + } + this.firstChild = firstChild; + function lastChild(node) { + while(node.lastChild !== null && isGroupingElement(node)) { + node = node.lastChild + } + return node + } + this.lastChild = lastChild; + function previousNode(node) { + while(!isParagraph(node) && node.previousSibling === null) { + node = (node.parentNode) + } + return isParagraph(node) ? null : lastChild((node.previousSibling)) + } + this.previousNode = previousNode; + function nextNode(node) { + while(!isParagraph(node) && node.nextSibling === null) { + node = (node.parentNode) + } + 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(isAnchoredAsCharacterElement(node)) { + r = isSpaceElement(node) === false; + node = null + }else { + node = previousNode(node) + } } } - if(accept === NodeFilter.FILTER_ACCEPT) { - if(nodeType === Node.ELEMENT_NODE && odfUtils.isParagraph(node)) { - s += "\n" + return r + } + this.scanLeftForNonSpace = scanLeftForNonSpace; + function lookLeftForCharacter(node) { + var text, r = 0, tl = 0; + if(node.nodeType === Node.TEXT_NODE) { + tl = (node).length + } + if(tl > 0) { + text = (node).data; + if(!isODFWhitespace(text.substr(tl - 1, 1))) { + r = 1 }else { - if(nodeType === Node.TEXT_NODE && node.textContent) { - s += node.textContent + if(tl === 1) { + r = scanLeftForNonSpace(previousNode(node)) ? 2 : 0 + }else { + r = isODFWhitespace(text.substr(tl - 2, 1)) ? 0 : 2 } } + }else { + if(isAnchoredAsCharacterElement(node)) { + r = 1 + } } - return s + return r } - this.filter = null; - this.writeToString = function(node) { - var plainText; - if(!node) { - return"" + this.lookLeftForCharacter = lookLeftForCharacter; + function lookRightForCharacter(node) { + var r = false, l = 0; + if(node && node.nodeType === Node.TEXT_NODE) { + l = (node).length } - plainText = serializeNode(node); - if(plainText[plainText.length - 1] === "\n") { - plainText = plainText.substr(0, plainText.length - 1) + if(l > 0) { + r = !isODFWhitespace((node).data.substr(0, 1)) + }else { + if(isAnchoredAsCharacterElement(node)) { + r = true + } } - return plainText + return r } -}; -/* - - 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.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) { - if(odfUtils.isInlineRoot(leftNode) && odfUtils.isGroupingElement(rightNode)) { - return FILTER_REJECT + 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 { + l = 0 } - r = odfUtils.lookLeftForCharacter(leftNode); - if(r === 1) { - return FILTER_ACCEPT + if(l > 0 && !isODFWhitespace((node).data)) { + r = true; + break } - if(r === 2 && (odfUtils.scanRightForAnyCharacter(rightNode) || odfUtils.scanRightForAnyCharacter(odfUtils.nextNode(container)))) { - return FILTER_ACCEPT + if(isAnchoredAsCharacterElement(node)) { + r = true; + break } + node = previousNode(node) } - firstPos = leftNode === null && odfUtils.isParagraph(container); - rightOfChar = odfUtils.lookRightForCharacter(rightNode); - if(firstPos) { - if(rightOfChar) { - return FILTER_ACCEPT + return r + } + 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 odfUtils.scanRightForAnyCharacter(rightNode) ? FILTER_REJECT : FILTER_ACCEPT + if(l > 0 && !isODFWhitespace((node).data)) { + r = true; + break + } + if(isAnchoredAsCharacterElement(node)) { + r = true; + break + } + node = nextNode(node) } - if(!rightOfChar) { - return FILTER_REJECT + return r + } + this.scanRightForAnyCharacter = scanRightForAnyCharacter; + function isTrailingWhitespace(textnode, offset) { + if(!isODFWhitespace(textnode.data.substr(offset))) { + return false } - leftNode = leftNode || odfUtils.previousNode(container); - return odfUtils.scanLeftForAnyCharacter(leftNode) ? FILTER_REJECT : FILTER_ACCEPT + return!scanRightForAnyCharacter(nextNode(textnode)) } - 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.isTrailingWhitespace = isTrailingWhitespace; + function isSignificantWhitespace(textNode, offset) { + var text = textNode.data, result; + if(!isODFWhitespace(text[offset])) { + return false } - 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 + if(isAnchoredAsCharacterElement(textNode.parentNode)) { + return false + } + if(offset > 0) { + if(!isODFWhitespace(text[offset - 1])) { + result = 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) + if(scanLeftForNonSpace(previousNode(textNode))) { + result = true } } - return r + 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 getFirstNonWhitespaceChild(node) { + var child = node && node.firstChild; + while(child && (child.nodeType === Node.TEXT_NODE && whitespaceOnly.test(child.nodeValue))) { + child = child.nextSibling + } + return child } -}; -if(typeof Object.create !== "function") { - Object["create"] = function(o) { - var F = function() { - }; - F.prototype = o; - return new F + 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]} } -} -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.parseLength = parseLength; + function parsePositiveLength(length) { + var result = parseLength(length); + if(result && (result.value <= 0 || result.unit === "%")) { + return null } - 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 + 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 isTextContentContainingNode(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 } - if(current[p] === ns) { - break + break; + case odf.Namespaces.officens: + switch(node.localName) { + case "annotation": + ; + case "binary-data": + ; + case "event-listeners": + return false } - if(!current[p]) { - current[p] = ns; - currentrev[ns] = p; - break + break; + default: + switch(node.localName) { + case "cursor": + ; + case "editinfo": + return false } - p = null - }while(p === null); - return p + ":" + node.localName + break } + return true } - function escapeContent(value) { - return value.replace(/&/g, "&").replace(//g, ">").replace(/'/g, "'").replace(/"/g, """) + this.isTextContentContainingNode = isTextContentContainingNode; + function isSignificantTextContent(textNode) { + return Boolean(getParagraphElement(textNode) && (!isODFWhitespace(textNode.textContent) || isSignificantWhitespace(textNode, 0))) } - function serializeAttribute(qname, attr) { - var escapedValue = typeof attr.value === "string" ? escapeContent(attr.value) : attr.value, s = qname + '="' + escapedValue + '"'; - return s + function removePartiallyContainedNodes(range, nodes) { + while(nodes.length > 0 && !domUtils.rangeContainsNode(range, (nodes[0]))) { + nodes.shift() + } + while(nodes.length > 0 && !domUtils.rangeContainsNode(range, (nodes[nodes.length - 1]))) { + nodes.pop() + } } - 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 getTextNodes(range, includePartial) { + var textNodes; + function nodeFilter(node) { + var result = NodeFilter.FILTER_REJECT; + if(node.nodeType === Node.TEXT_NODE) { + if(isSignificantTextContent((node))) { + result = NodeFilter.FILTER_ACCEPT + } + }else { + if(isTextContentContainingNode(node)) { + result = NodeFilter.FILTER_SKIP } } + return result } - nsmap = ns.getLocalNamespaceDefinitions(); - for(i in nsmap) { - if(nsmap.hasOwnProperty(i)) { - prefix = nsmap[i]; - if(!prefix) { - s += ' xmlns="' + i + '"' + textNodes = domUtils.getNodesInRange(range, nodeFilter, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT); + if(!includePartial) { + removePartiallyContainedNodes(range, textNodes) + } + return textNodes + } + this.getTextNodes = getTextNodes; + function getTextElements(range, includePartial, includeInsignificantWhitespace) { + var elements; + function nodeFilter(node) { + var result = NodeFilter.FILTER_REJECT; + if(isCharacterElement(node.parentNode) || isInlineRoot(node)) { + result = NodeFilter.FILTER_REJECT + }else { + if(node.nodeType === Node.TEXT_NODE) { + if(includeInsignificantWhitespace || isSignificantTextContent((node))) { + result = NodeFilter.FILTER_ACCEPT + } }else { - if(prefix !== "xmlns") { - s += " xmlns:" + nsmap[i] + '="' + i + '"' + if(isAnchoredAsCharacterElement(node)) { + result = NodeFilter.FILTER_ACCEPT + }else { + if(isTextContentContainingNode(node) || isGroupingElement(node)) { + result = NodeFilter.FILTER_SKIP + } } } } + return result } - s += attstr + ">"; - return s - } - 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) - } - 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(qname) { - s += ""; - ns.pop() - } - return s - } - this.filter = null; - this.writeToString = function(node, nsmap) { - if(!node) { - return"" - } - var ns = new Namespaces(nsmap); - return serializeNode(ns, node) - } -}; -/* - - 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 - } - 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 + elements = domUtils.getNodesInRange(range, nodeFilter, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT); + if(!includePartial) { + removePartiallyContainedNodes(range, elements) } - return result + return elements + } + this.getTextElements = getTextElements; + function prependParentContainers(startContainer, elements, filter) { + var container = startContainer; + while(container) { + if(filter(container)) { + if(elements[0] !== container) { + elements.unshift(container) + } + break + } + if(isInlineRoot(container)) { + break + } + container = container.parentNode + } + } + this.getParagraphElements = function(range) { + var elements; + function nodeFilter(node) { + var result = NodeFilter.FILTER_REJECT; + if(isParagraph(node)) { + result = NodeFilter.FILTER_ACCEPT + }else { + if(isTextContentContainingNode(node) || isGroupingElement(node)) { + result = NodeFilter.FILTER_SKIP + } + } + return result + } + elements = domUtils.getNodesInRange(range, nodeFilter, NodeFilter.SHOW_ELEMENT); + prependParentContainers((range.startContainer), elements, isParagraph); + return elements }; - function init() { - xmlSerializer = new xmldom.LSSerializer; - textSerializer = new odf.TextSerializer; - filter = new odf.OdfNodeFilter; - xmlSerializer.filter = filter; - textSerializer.filter = filter + this.getImageElements = function(range) { + var elements; + function nodeFilter(node) { + var result = NodeFilter.FILTER_SKIP; + if(isImage(node)) { + result = NodeFilter.FILTER_ACCEPT + } + return result + } + elements = domUtils.getNodesInRange(range, nodeFilter, NodeFilter.SHOW_ELEMENT); + prependParentContainers((range.startContainer), elements, isImage); + return elements + }; + function getRightNode(container, offset) { + var node = container; + if(offset < node.childNodes.length - 1) { + node = (node.childNodes[offset + 1]) + }else { + while(!node.nextSibling) { + node = node.parentNode + } + node = node.nextSibling + } + while(node.firstChild) { + node = node.firstChild + } + return node + } + this.getHyperlinkElements = function(range) { + var links = [], newRange = (range.cloneRange()), node, textNodes; + if(range.collapsed && range.endContainer.nodeType === Node.ELEMENT_NODE) { + node = getRightNode(range.endContainer, range.endOffset); + if(node.nodeType === Node.TEXT_NODE) { + newRange.setEnd(node, 1) + } + } + textNodes = getTextElements(newRange, true, false); + textNodes.forEach(function(node) { + var parent = node.parentNode; + while(!isParagraph(parent)) { + if(isHyperlink(parent) && links.indexOf(parent) === -1) { + links.push(parent); + break + } + parent = parent.parentNode + } + }); + newRange.detach(); + return links } - init() }; /* @@ -6698,651 +6154,383 @@ gui.Clipboard = function Clipboard() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.Base64"); -runtime.loadClass("core.Zip"); -runtime.loadClass("core.DomUtils"); -runtime.loadClass("xmldom.LSSerializer"); -runtime.loadClass("odf.StyleInfo"); -runtime.loadClass("odf.Namespaces"); -runtime.loadClass("odf.OdfNodeFilter"); -(function() { - var styleInfo = new odf.StyleInfo, domUtils = new core.DomUtils, 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 - } - } - 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 - } - } - return result - } - } - 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 +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, showAnnotationRemoveButton) { + 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; + annotationWrapper.className = "annotationWrapper"; + annotation.parentNode.insertBefore(annotationWrapper, annotation); + annotationNote.className = "annotationNote"; + annotationNote.appendChild(annotation); + if(showAnnotationRemoveButton) { + removeButton = doc.createElement("div"); + removeButton.className = "annotationRemoveButton"; + annotationNote.appendChild(removeButton) } + connectorHorizontal.className = "annotationConnector horizontal"; + connectorAngular.className = "annotationConnector angular"; + annotationWrapper.appendChild(annotationNote); + annotationWrapper.appendChild(connectorHorizontal); + annotationWrapper.appendChild(connectorAngular) } - 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 - } - c = c.nextSibling + function unwrapAnnotation(annotation) { + var annotationWrapper = annotation.parentNode.parentNode; + if(annotationWrapper.localName === "div") { + annotationWrapper.parentNode.insertBefore(annotation, annotationWrapper); + annotationWrapper.parentNode.removeChild(annotationWrapper) } - node.insertBefore(child, c) } - 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) - } - }) - } - }; - odf.OdfPart.prototype.load = function() { - }; - odf.OdfPart.prototype.getUrl = function() { - if(this.data) { - return"data:;base64," + base64.toBase64(this.data) - } - 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 { - if(n.nodeType === Node.PROCESSING_INSTRUCTION_NODE) { - element.removeChild(n) - } - } - n = next - } - } - function setAutomaticStylesScope(stylesRootElement, scope) { - var n = stylesRootElement && stylesRootElement.firstChild; - while(n) { - if(n.nodeType === Node.ELEMENT_NODE) { - (n).setAttributeNS(webodfns, "scope", scope) - } - n = n.nextSibling - } - } - function getEnsuredMetaElement() { - var root = self.rootElement, meta = root.meta; - if(!meta) { - root.meta = meta = document.createElementNS(officens, "meta"); - setChild(root, meta) - } - return meta - } - function getMetaData(metadataNs, metadataLocalName) { - var node = self.rootElement.meta, textNode; - node = node && node.firstChild; - while(node && (node.namespaceURI !== metadataNs || node.localName !== metadataLocalName)) { - node = node.nextSibling - } - node = node && node.firstChild; - while(node && node.nodeType !== Node.TEXT_NODE) { - node = node.nextSibling - } - if(node) { - textNode = (node); - return textNode.data - } - return null - } - 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 highlightAnnotation(annotation) { + var annotationEnd = annotation.annotationEndElement, range = doc.createRange(), annotationName = annotation.getAttributeNS(odf.Namespaces.officens, "name"), textNodes; + if(annotationEnd) { + range.setStart(annotation, annotation.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", annotationName); + n.parentNode.insertBefore(container, n); + container.appendChild(n) + }) } - 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 + range.detach() + } + function unhighlightAnnotation(annotation) { + var annotationName = annotation.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) } - return result + container.parentNode.removeChild(container) } - 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 + } + 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 renderAnnotation(annotation) { + var annotationNote = (annotation.parentNode), connectorHorizontal = annotationNote.nextElementSibling, connectorAngular = connectorHorizontal.nextElementSibling, annotationWrapper = (annotationNote.parentNode), 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.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" } - 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 - } - } - return copy + 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)" + } + function showAnnotationsPane(show) { + var sizer = canvas.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 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 - } + canvas.refreshSize() + } + function sortAnnotations() { + annotations.sort(function(a, b) { + if((a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0) { + return-1 } - return copy + return 1 + }) + } + function rerenderAnnotations() { + var i; + for(i = 0;i < annotations.length;i += 1) { + renderAnnotation(annotations[i]) } - 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 + } + this.rerenderAnnotations = rerenderAnnotations; + function getMinimumHeightForAnnotationPane() { + if(annotationsPane.style.display !== "none" && annotations.length > 0) { + return((annotations[annotations.length - 1].parentNode).getBoundingClientRect().bottom - annotationsPane.getBoundingClientRect().top) / canvas.getZoomLevel() + "px" } - function setState(state) { - self.state = state; - if(self.onchange) { - self.onchange(self) - } - if(self.onstatereadychange) { - self.onstatereadychange(self) - } + return null + } + this.getMinimumHeightForAnnotationPane = getMinimumHeightForAnnotationPane; + function addAnnotation(annotation) { + showAnnotationsPane(true); + annotations.push(annotation); + sortAnnotations(); + wrapAnnotation(annotation); + if(annotation.annotationEndElement) { + highlightAnnotation(annotation) } - 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") + rerenderAnnotations() + } + this.addAnnotation = addAnnotation; + function forgetAnnotation(annotation) { + var index = annotations.indexOf(annotation); + unwrapAnnotation(annotation); + unhighlightAnnotation(annotation); + if(index !== -1) { + annotations.splice(index, 1) } - function handleFlatXml(xmldoc) { - var root = importRootNode(xmldoc); - if(!root || (root.localName !== "document" || root.namespaceURI !== officens)) { - setState(OdfContainer.INVALID); - return - } - setRootElement((root)); - setState(OdfContainer.DONE) + if(annotations.length === 0) { + showAnnotationsPane(false) } - 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) + } + function forgetAnnotations() { + while(annotations.length) { + forgetAnnotation(annotations[0]) } - 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(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 - } + } + this.forgetAnnotations = forgetAnnotations +}; +/* + + Copyright (C) 2014 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/ +*/ +(function() { + function Point(x, y) { + var self = this; + this.getDistance = function(point) { + var xOffset = self.x - point.x, yOffset = self.y - point.y; + return Math.sqrt(xOffset * xOffset + yOffset * yOffset) + }; + this.getCenter = function(point) { + return new Point((self.x + point.x) / 2, (self.y + point.y) / 2) + }; + this.x; + this.y; + function init() { + self.x = x; + self.y = y + } + init() + } + gui.ZoomHelper = function() { + var zoomableElement, panPoint, previousPanPoint, firstPinchDistance, zoom, previousZoom, maxZoom = 4, offsetParent, events = new core.EventNotifier([gui.ZoomHelper.signalZoomChanged]), gestures = {NONE:0, SCROLL:1, PINCH:2}, currentGesture = gestures.NONE, requiresCustomScrollBars = runtime.getWindow().hasOwnProperty("ontouchstart"); + function applyCSSTransform(x, y, scale, is3D) { + var transformCommand; + if(is3D) { + transformCommand = "translate3d(" + x + "px, " + y + "px, 0) scale3d(" + scale + ", " + scale + ", 1)" }else { - if(automaticStyles) { - root.automaticStyles = automaticStyles; - setChild(root, automaticStyles) - } - } - node = getDirectChild(node, officens, "body"); - if(node === null) { - throw" tag is mising."; + transformCommand = "translate(" + x + "px, " + y + "px) scale(" + scale + ")" } - root.body = node; - setChild(root, root.body) + zoomableElement.style.WebkitTransform = transformCommand; + zoomableElement.style.MozTransform = transformCommand; + zoomableElement.style.msTransform = transformCommand; + zoomableElement.style.OTransform = transformCommand; + zoomableElement.style.transform = transformCommand } - function handleMetaXml(xmldoc) { - var node = importRootNode(xmldoc), root; - if(!node || (node.localName !== "document-meta" || node.namespaceURI !== officens)) { - return + function applyTransform(is3D) { + if(is3D) { + applyCSSTransform(-panPoint.x, -panPoint.y, zoom, true) + }else { + applyCSSTransform(0, 0, zoom, true); + applyCSSTransform(0, 0, zoom, false) } - root = self.rootElement; - root.meta = getDirectChild(node, officens, "meta"); - setChild(root, root.meta) } - function handleSettingsXml(xmldoc) { - var node = importRootNode(xmldoc), root; - if(!node || (node.localName !== "document-settings" || node.namespaceURI !== officens)) { + function applyFastTransform() { + applyTransform(true) + } + function applyDetailedTransform() { + applyTransform(false) + } + function enableScrollBars(enable) { + if(!offsetParent || !requiresCustomScrollBars) { 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)) { + var initialOverflow = offsetParent.style.overflow, enabled = offsetParent.classList.contains("customScrollbars"); + if(enable && enabled || !enable && !enabled) { 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") + if(enable) { + offsetParent.classList.add("customScrollbars"); + offsetParent.style.overflow = "hidden"; + runtime.requestAnimationFrame(function() { + offsetParent.style.overflow = initialOverflow + }) + }else { + offsetParent.classList.remove("customScrollbars") + } + } + function removeScroll() { + applyCSSTransform(-panPoint.x, -panPoint.y, zoom, true); + offsetParent.scrollLeft = 0; + offsetParent.scrollTop = 0; + enableScrollBars(false) + } + function restoreScroll() { + applyCSSTransform(0, 0, zoom, true); + offsetParent.scrollLeft = panPoint.x; + offsetParent.scrollTop = panPoint.y; + enableScrollBars(true) + } + function getPoint(touch) { + return new Point(touch.pageX - zoomableElement.offsetLeft, touch.pageY - zoomableElement.offsetTop) + } + function sanitizePointForPan(point) { + return new Point(Math.min(Math.max(point.x, zoomableElement.offsetLeft), (zoomableElement.offsetLeft + zoomableElement.offsetWidth) * zoom - offsetParent.clientWidth), Math.min(Math.max(point.y, zoomableElement.offsetTop), (zoomableElement.offsetTop + zoomableElement.offsetHeight) * zoom - offsetParent.clientHeight)) + } + function processPan(point) { + if(previousPanPoint) { + panPoint.x -= point.x - previousPanPoint.x; + panPoint.y -= point.y - previousPanPoint.y; + panPoint = sanitizePointForPan(panPoint) + } + previousPanPoint = point + } + function processZoom(zoomPoint, incrementalZoom) { + var originalZoom = zoom, actuallyIncrementedZoom, minZoom = Math.min(maxZoom, zoomableElement.offsetParent.clientWidth / zoomableElement.offsetWidth); + zoom = previousZoom * incrementalZoom; + zoom = Math.min(Math.max(zoom, minZoom), maxZoom); + actuallyIncrementedZoom = zoom / originalZoom; + panPoint.x += (actuallyIncrementedZoom - 1) * (zoomPoint.x + panPoint.x); + panPoint.y += (actuallyIncrementedZoom - 1) * (zoomPoint.y + panPoint.y) + } + function processPinch(point1, point2) { + var zoomPoint = point1.getCenter(point2), pinchDistance = point1.getDistance(point2), incrementalZoom = pinchDistance / firstPinchDistance; + processPan(zoomPoint); + processZoom(zoomPoint, incrementalZoom) + } + function prepareGesture(event) { + var fingers = event.touches.length, point1 = fingers > 0 ? getPoint(event.touches[0]) : null, point2 = fingers > 1 ? getPoint(event.touches[1]) : null; + if(point1 && point2) { + firstPinchDistance = point1.getDistance(point2); + previousZoom = zoom; + previousPanPoint = point1.getCenter(point2); + removeScroll(); + currentGesture = gestures.PINCH + }else { + if(point1) { + previousPanPoint = point1; + currentGesture = gestures.SCROLL } - 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) { + function processGesture(event) { + var fingers = event.touches.length, point1 = fingers > 0 ? getPoint(event.touches[0]) : null, point2 = fingers > 1 ? getPoint(event.touches[1]) : null; + if(point1 && point2) { + event.preventDefault(); + if(currentGesture === gestures.SCROLL) { + currentGesture = gestures.PINCH; + removeScroll(); + firstPinchDistance = point1.getDistance(point2); + return + } + processPinch(point1, point2); + applyFastTransform() + }else { + if(point1) { + if(currentGesture === gestures.PINCH) { + currentGesture = gestures.SCROLL; + restoreScroll(); return } - loadNextComponent(remainingComponents) - }) - }else { - setState(OdfContainer.DONE) - } - } - 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 + '"' - } - odf.Namespaces.forEachPrefix(defineNamespace); - 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])) + processPan(point1) } } - 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] - } + function sanitizeGesture() { + if(currentGesture === gestures.PINCH) { + events.emit(gui.ZoomHelper.signalZoomChanged, zoom); + restoreScroll(); + applyDetailedTransform() } - return original - } - function loadFromXML(url, callback) { - runtime.loadXML(url, function(err, dom) { - if(err) { - callback(err) - }else { - handleFlatXml(dom) - } - }) + currentGesture = gestures.NONE } - 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.subscribe = function(eventid, cb) { + events.subscribe(eventid, cb) }; - this.getPart = function(partname) { - return new odf.OdfPart(partname, partMimetypes[partname], self, zip) + this.unsubscribe = function(eventid, cb) { + events.unsubscribe(eventid, cb) }; - this.getPartData = function(url, callback) { - zip.load(url, callback) + this.getZoomLevel = function() { + return zoom }; - function setMetadata(setProperties, removedPropertyNames) { - var metaElement = getEnsuredMetaElement(); - if(setProperties) { - domUtils.mapKeyValObjOntoNode(metaElement, setProperties, odf.Namespaces.lookupNamespaceURI) - } - if(removedPropertyNames) { - domUtils.removeKeyElementsFromNode(metaElement, removedPropertyNames, odf.Namespaces.lookupNamespaceURI) - } - } - this.setMetadata = setMetadata; - this.incrementEditingCycles = function() { - var currentValueString = getMetaData(odf.Namespaces.metans, "editing-cycles"), currentCycles = currentValueString ? parseInt(currentValueString, 10) : 0; - if(isNaN(currentCycles)) { - currentCycles = 0 + this.setZoomLevel = function(zoomLevel) { + if(zoomableElement) { + zoom = zoomLevel; + applyDetailedTransform(); + events.emit(gui.ZoomHelper.signalZoomChanged, zoom) } - setMetadata({"meta:editing-cycles":currentCycles + 1}, null) }; - function updateMetadataForSaving() { - var generatorString, window = runtime.getWindow(); - generatorString = "WebODF/" + (String(typeof webodf_version) !== "undefined" ? webodf_version : "FromSource"); - if(window) { - generatorString = generatorString + " " + window.navigator.userAgent + function registerGestureListeners() { + if(offsetParent) { + offsetParent.addEventListener("touchstart", (prepareGesture), false); + offsetParent.addEventListener("touchmove", (processGesture), false); + offsetParent.addEventListener("touchend", (sanitizeGesture), false) } - 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) + function unregisterGestureListeners() { + if(offsetParent) { + offsetParent.removeEventListener("touchstart", (prepareGesture), false); + offsetParent.removeEventListener("touchmove", (processGesture), false); + offsetParent.removeEventListener("touchend", (sanitizeGesture), false) } - 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); - 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.destroy = function(callback) { + unregisterGestureListeners(); + enableScrollBars(false); + callback() }; - 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() + this.setZoomableElement = function(element) { + unregisterGestureListeners(); + zoomableElement = element; + offsetParent = (zoomableElement.offsetParent); + applyDetailedTransform(); + registerGestureListeners(); + enableScrollBars(true) + }; + function init() { + zoom = 1; + previousZoom = 1; + panPoint = new Point(0, 0) } + init() }; - 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 + gui.ZoomHelper.signalZoomChanged = "zoomChanged" })(); /* @@ -7381,9 +6569,6 @@ runtime.loadClass("odf.OdfNodeFilter"); @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.Base64"); -runtime.loadClass("xmldom.XPath"); -runtime.loadClass("odf.OdfContainer"); (function() { var xpath = xmldom.XPath, base64 = new core.Base64; function getEmbeddedFontDeclarations(fontFaceDecls) { @@ -7402,163 +6587,455 @@ runtime.loadClass("odf.OdfContainer"); decls[name] = {href:href, family:family} } } - 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 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) + } + } + 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/ +*/ +odf.Formatting = function Formatting() { + var 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 = {} + } + 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 + } + 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 + } + } + node = node.nextElementSibling + } + return fontFaceDeclsMap + } + 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 + } + 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 + } + 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 + } + 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 + } + a = styleNode.attributes; + for(i = 0;i < a.length;i += 1) { + ai = (a.item(i)); + propertiesMap[ai.name] = ai.value + } + 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 + } + } + node = getDefaultStyleElement(styleFamily); + if(node) { + propertiesMap = getStyleAttributes(node); + inheritedPropertiesMap = utils.mergeObjects(propertiesMap, inheritedPropertiesMap) + } + if(includeSystemDefault !== false) { + propertiesMap = getSystemDefaultStyleAttributes(styleFamily); + inheritedPropertiesMap = utils.mergeObjects(propertiesMap, inheritedPropertiesMap) } + return inheritedPropertiesMap } - 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.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]) + } + styleElement = getStyleElement(styleName, "paragraph", [styleElementList]); + if(!styleElement) { + return null + } + 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 } + nodeStyles = styleInfo.determineStylesForNode(parent); + if(nodeStyles) { + appliedStyles.push(nodeStyles) + } + parent = (parent.parentNode) } - if(!name) { - if(callback) { - callback() + function chainStyles(usedStyleMap) { + Object.keys(usedStyleMap).forEach(function(styleFamily) { + Object.keys(usedStyleMap[styleFamily]).forEach(function(styleName) { + chainKey += "|" + styleFamily + ":" + styleName + "|" + }) + }) + } + if(foundContainer) { + appliedStyles.forEach(chainStyles); + if(collectedChains) { + collectedChains[chainKey] = appliedStyles } - 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) + return foundContainer ? appliedStyles : undefined + } + function isCommonStyleElement(styleNode) { + return styleNode.parentNode === odfContainer.rootElement.styles + } + function calculateAppliedStyle(styleChain) { + var mergedChildStyle = {orderedStyles:[]}; + styleChain.forEach(function(elementStyleSet) { + Object.keys((elementStyleSet)).forEach(function(styleFamily) { + var styleName = Object.keys(elementStyleSet[styleFamily])[0], styleSummary = {name:styleName, family:styleFamily, displayName:undefined, isCommonStyle:false}, styleElement, parentStyle; + styleElement = getStyleElement(styleName, styleFamily); + if(styleElement) { + parentStyle = getInheritedStyleAttributes((styleElement)); + mergedChildStyle = utils.mergeObjects(parentStyle, mergedChildStyle); + styleSummary.displayName = styleElement.getAttributeNS(stylens, "display-name"); + styleSummary.isCommonStyle = isCommonStyleElement(styleElement) }else { - addFontToCSS(name, embeddedFontDeclarations[name], fontdata, stylesheet) + runtime.log("No style element found for '" + styleName + "' of family '" + styleFamily + "'") } - } - loadFontIntoCSS(embeddedFontDeclarations, odfContainer, pos + 1, stylesheet, callback) - }) - } - function loadFontsIntoCSS(embeddedFontDeclarations, odfContainer, stylesheet) { - loadFontIntoCSS(embeddedFontDeclarations, odfContainer, 0, stylesheet) + mergedChildStyle.orderedStyles.push(styleSummary) + }) + }); + return mergedChildStyle } - 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) + function getAppliedStyles(nodes, calculatedStylesCache) { + var styleChains = {}, styles = []; + if(!calculatedStylesCache) { + calculatedStylesCache = {} + } + nodes.forEach(function(n) { + buildStyleChain(n, styleChains) + }); + Object.keys(styleChains).forEach(function(key) { + if(!calculatedStylesCache[key]) { + calculatedStylesCache[key] = calculateAppliedStyle(styleChains[key]) } + styles.push(calculatedStylesCache[key]) + }); + return styles + } + this.getAppliedStyles = getAppliedStyles; + this.getAppliedStylesForElement = function(node, calculatedStylesCache) { + return getAppliedStyles([node], calculatedStylesCache)[0] + }; + 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 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 + 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(isCommonStyleElement(originalStyleElement)) { + newStyleObject = {"style:parent-style-name":parentStyleName} + }else { + newStyleObject = getStyleAttributes(originalStyleElement) } - } - 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 + 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 + } + if(!tabStopDistance) { + tabStopDistance = "1.25cm" + } + 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 + } + 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) } - e = e.nextElementSibling } } - styleElements.forEach(getStyleNames); - return styleNames + return null } - this.generateStyleName = function() { - if(styleNameGenerator === null) { - styleNameGenerator = new NameGenerator("auto" + memberIdHash + "_", function() { - return getAllStyleNames() - }) + 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 + } } - 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 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") } - 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 - }) + 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 { + defaultOrientedPageWidth = defaultPageFormatSettings.height; + defaultOrientedPageHeight = defaultPageFormatSettings.width + } + 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 + } + 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 imageNameGenerator.generateName() + return{width:pageWidth - marginLeft - marginRight - paddingLeft - paddingRight, height:pageHeight - marginTop - marginBottom - paddingTop - paddingBottom} } }; /* @@ -7598,27 +7075,135 @@ odf.ObjectNameGenerator = function ObjectNameGenerator(odfContainer, memberId) { @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"); -odf.Formatting = function Formatting() { - var 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) +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 { + if(node.namespaceURI === stylens && (node.localName === "page-layout" || node.localName === "default-page-layout")) { + family = "page" + }else { + family = undefined + } + } + } + if(family) { + name = node.getAttributeNS(stylens, "name"); + if(!name) { + name = "" + } + if(stylemap.hasOwnProperty(family)) { + style = stylemap[family] + }else { + stylemap[family] = style = {} + } + style[name] = node + } + node = node.nextElementSibling + } + return stylemap + } + 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 + } + } + } + return style + } + function addStyleToStyleTree(stylename, stylesmap, stylestree) { + var style, parentname, parentstyle; + if(!stylesmap.hasOwnProperty(stylename)) { + return null + } + 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 { - result = {} + stylestree[stylename] = style } - return result + delete stylesmap[stylename]; + return style + } + function addStyleMapToStyleTree(stylesmap, stylestree) { + var name; + for(name in stylesmap) { + if(stylesmap.hasOwnProperty(name)) { + addStyleToStyleTree(name, stylesmap, stylestree) + } + } + } + function createSelector(family, name) { + var prefix = familynamespaceprefixes[family], namepart, selector; + if(prefix === undefined) { + return null + } + if(name) { + namepart = "[" + prefix + '|style-name="' + name + '"]' + }else { + namepart = "" + } + if(prefix === "presentation") { + prefix = "draw"; + if(name) { + namepart = '[presentation|style-name="' + name + '"]' + }else { + namepart = "" + } + } + 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) + } + for(n in derivedStyles) { + if(derivedStyles.hasOwnProperty(n)) { + ss = getSelectors(family, n, derivedStyles[n]); + selectors = selectors.concat(ss) + } + } + return selectors } - this.getSystemDefaultStyleAttributes = getSystemDefaultStyleAttributes; - this.setOdfContainer = function(odfcontainer) { - odfContainer = odfcontainer - }; function getDirectChild(node, ns, name) { var e = node && node.firstElementChild; while(e) { @@ -7627,334 +7212,438 @@ odf.Formatting = function Formatting() { } e = e.nextElementSibling } - return e + return e + } + 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 = "" + } + width = utils.parseLength(width); + if(width && (width.unit === "pt" && width.value < 0.75)) { + value = "0.75pt" + theRestOfBorderAttributes + } + return value + } + 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) + } + if(r[2]) { + rule += r[2] + ":" + value + ";" + } + } + } + return rule + } + function getFontSize(styleNode) { + var props = getDirectChild(styleNode, stylens, "text-properties"); + if(props) { + return utils.parseFoFontSize(props.getAttributeNS(fons, "font-size")) + } + 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 { + xp = "//style:default-style[@style:family='" + parentStyleFamily + "']" + } + parentStyleNode = xpath.getODFElementsWithXPath((odfRoot), xp, odf.Namespaces.lookupNamespaceURI)[0]; + return parentStyleNode } - 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 + 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 + } + fontName = props.getAttributeNS(stylens, "font-name") || props.getAttributeNS(fons, "font-family"); + if(fontName) { + value = fontFaceDeclsMap[fontName]; + rule += "font-family: " + (value || fontName) + ";" + } + parentStyle = (props.parentNode); + 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 } - node = node.nextElementSibling + parentStyle = getParentStyleNode(parentStyle) } - return fontFaceDeclsMap + if(!fontSizeRule) { + fontSizeRule = "font-size: " + parseFloat(defaultFontSize) * sizeMultiplier + cssUnits.getUnits(defaultFontSize) + ";" + } + rule += fontSizeRule; + return rule } - 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}) - } - } + 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) } - node = node.nextElementSibling } - 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 + 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 + ";" } - node = node.nextElementSibling } - return null + return rule } - 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 matchToRgb(m, r, g, b) { + return r + r + g + g + b + b } - 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 + 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 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 + ");" } + }else { + rule += "background: none;" + } + }else { + if(fill === "none") { + rule += "background: none;" } - propertiesNode = propertiesNode.nextElementSibling } - a = styleNode.attributes; - for(i = 0;i < a.length;i += 1) { - ai = (a.item(i)); - propertiesMap[ai.name] = ai.value + return rule + } + function getDrawingPageProperties(props) { + var rule = ""; + rule += applySimpleMapping(props, graphicPropertySimpleMapping); + if(props.getAttributeNS(presentationns, "background-visible") === "true") { + rule += "background: none;" } - return propertiesMap + return rule } - 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 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 = getDefaultStyleElement(styleFamily); - if(node) { - propertiesMap = getStyleAttributes(node); - inheritedPropertiesMap = utils.mergeObjects(propertiesMap, inheritedPropertiesMap) + 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) + } + properties = getDirectChild(node.element, stylens, "paragraph-properties"); + if(properties) { + rule += getParagraphProperties(properties) + } + 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; } - if(includeSystemDefault) { - propertiesMap = getSystemDefaultStyleAttributes(styleFamily); - if(propertiesMap) { - inheritedPropertiesMap = utils.mergeObjects(propertiesMap, inheritedPropertiesMap) + } + 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 { + if(style) { + content += ' "' + style + '"' + }else { + content += " ''" } } - return inheritedPropertiesMap + return"content:" + content + ' "' + suffix + '"' } - 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]) + 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") } - styleElement = getStyleElement(styleName, "paragraph", [styleElementList]); - if(!styleElement) { - return null + if(!bulletIndent) { + bulletIndent = "-0.6cm" } - 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 - } - nodeStyles = styleInfo.determineStylesForNode(parent); - if(nodeStyles) { - appliedStyles.push(nodeStyles) - } - parent = parent.parentElement + if(bulletIndent.charAt(0) === "-") { + bulletWidth = bulletIndent.substring(1) + }else { + bulletWidth = "-" + bulletIndent } - function chainStyles(usedStyleMap) { - Object.keys(usedStyleMap).forEach(function(styleFamily) { - Object.keys(usedStyleMap[styleFamily]).forEach(function(styleName) { - chainKey += "|" + styleFamily + ":" + styleName + "|" - }) - }) + level = level && parseInt(level, 10); + while(level > 1) { + selector += " > text|list-item > text|list"; + level -= 1 } - if(foundContainer) { - appliedStyles.forEach(chainStyles); - if(collectedChains) { - collectedChains[chainKey] = appliedStyles + 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 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 - }; - 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) + 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) } - }; - 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) - }else { - newStyleObject = {"style:parent-style-name":parentStyleName} + function addPageStyleRules(sheet, node) { + var rule = "", imageProps, url, contentLayoutRule = "", pageSizeRule = "", props = getDirectChild(node, stylens, "page-layout-properties"), stylename, masterStyles, e, masterStyleName; + if(!props) { + return } - 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") + 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) } - paragraphProperties = paragraphProperties.nextElementSibling } - if(!tabStopDistance) { - tabStopDistance = "1.25cm" - } - 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 + if(documentType === "presentation") { + masterStyles = getDirectChild((node.parentNode.parentNode), 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; + } } - node = node.previousElementSibling + e = e.nextElementSibling } - 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) + }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 } - 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 + 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 + } + } + 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 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 { - defaultOrientedPageWidth = defaultPageFormatSettings.height; - defaultOrientedPageHeight = defaultPageFormatSettings.width + 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]) } - 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 + } + } + 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) { } - 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 + }); + 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{width:pageWidth - marginLeft - marginRight - paddingLeft - paddingRight, height:pageHeight - marginTop - marginBottom - paddingTop - paddingBottom} } }; /* @@ -7994,14 +7683,49 @@ odf.Formatting = function Formatting() { @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"); +ops.Canvas = function Canvas() { +}; +ops.Canvas.prototype.getZoomLevel = function() { +}; +ops.Canvas.prototype.getElement = 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/ +*/ (function() { function LoadingQueue() { var queue = [], taskRunning = false; @@ -8084,6 +7808,12 @@ runtime.loadClass("gui.AnnotationViewManager"); element.removeChild(element.firstChild) } } + function clearCSSStyleSheet(style) { + var stylesheet = (style.sheet), cssRules = stylesheet.cssRules; + while(cssRules.length) { + stylesheet.deleteRule(cssRules.length - 1) + } + } function handleStyles(odfcontainer, formatting, stylesxmlcss) { var style2css = new odf.Style2CSS; style2css.style2css(odfcontainer.getDocumentType(), (stylesxmlcss.sheet), formatting.getFontMap(), odfcontainer.rootElement.styles, odfcontainer.rootElement.automaticStyles) @@ -8096,20 +7826,21 @@ runtime.loadClass("gui.AnnotationViewManager"); if(!masterPageName) { return null } - var masterStyles = odfContainer.rootElement.masterStyles, masterPageElement = masterStyles.firstElementChild; - while(masterPageElement) { - if(masterPageElement.getAttributeNS(stylens, "name") === masterPageName && (masterPageElement.localName === "master-page" && masterPageElement.namespaceURI === stylens)) { + var masterStyles = odfContainer.rootElement.masterStyles, masterStylesChild = masterStyles.firstElementChild; + while(masterStylesChild) { + if(masterStylesChild.getAttributeNS(stylens, "name") === masterPageName && (masterStylesChild.localName === "master-page" && masterStylesChild.namespaceURI === stylens)) { break } + masterStylesChild = masterStylesChild.nextElementSibling } - return masterPageElement + return masterStylesChild } 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)) { + if(presentationClass && !/^(date-time|footer|header|page-number)$/.test(presentationClass)) { element.parentNode.removeChild(element) } } @@ -8237,39 +7968,6 @@ runtime.loadClass("gui.AnnotationViewManager"); modifyTableCell(node) } } - function modifyLinks(odffragment) { - var i, links, node; - function modifyLink(node) { - var url, clickHandler; - if(!node.hasAttributeNS(xlinkns, "href")) { - return - } - 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) - } - } - 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) { @@ -8315,7 +8013,7 @@ runtime.loadClass("gui.AnnotationViewManager"); node = node.firstElementChild }else { while(node && (node !== odfbody && !node.nextElementSibling)) { - node = node.parentElement + node = (node.parentNode) } if(node && node.nextElementSibling) { node = node.nextElementSibling @@ -8497,24 +8195,49 @@ runtime.loadClass("gui.AnnotationViewManager"); } } } + function findWebODFStyleSheet(head) { + var style = head.firstElementChild; + while(style && !(style.localName === "style" && style.hasAttribute("webodfcss"))) { + style = style.nextElementSibling + } + return(style) + } 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)) + var head = (document.getElementsByTagName("head")[0]), css, style, href, count = document.styleSheets.length; + style = findWebODFStyleSheet(head); + if(style) { + count = parseInt(style.getAttribute("webodfcss"), 10); + style.setAttribute("webodfcss", count + 1); + return style + } + if(String(typeof webodf_css) === "string") { + css = (webodf_css) }else { - style = document.createElementNS(head.namespaceURI, "link"); href = "webodf.css"; if(runtime.currentDirectory) { - href = runtime.currentDirectory() + "/../" + href + href = runtime.currentDirectory(); + if(href.length > 0 && href.substr(-1) !== "/") { + href += "/" + } + href += "../webodf.css" } - style.setAttribute("href", href); - style.setAttribute("rel", "stylesheet") + css = (runtime.readFileSync(href, "utf-8")) } + style = (document.createElementNS(head.namespaceURI, "style")); + style.setAttribute("media", "screen, print, handheld, projection"); style.setAttribute("type", "text/css"); + style.setAttribute("webodfcss", "1"); + style.appendChild(document.createTextNode(css)); head.appendChild(style); - return(style) + return style + } + function removeWebODFStyleSheet(webodfcss) { + var count = parseInt(webodfcss.getAttribute("webodfcss"), 10); + if(count === 1) { + webodfcss.parentNode.removeChild(webodfcss) + }else { + webodfcss.setAttribute("count", count - 1) + } } function addStyleSheet(document) { var head = (document.getElementsByTagName("head")[0]), style = document.createElementNS(head.namespaceURI, "style"), text = ""; @@ -8531,7 +8254,8 @@ runtime.loadClass("gui.AnnotationViewManager"); 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; + var self = this, doc = (element.ownerDocument), async = new core.Async, odfcontainer, formatting = new odf.Formatting, pageSwitcher, sizer = null, annotationsPane = null, allowAnnotations = false, showAnnotationRemoveButton = false, annotationViewManager = null, webodfcss, fontcss, stylesxmlcss, positioncss, shadowContent, eventHandlers = {}, waitingForDoneTimeoutId, redrawContainerTask, shouldRefreshCss = false, shouldRerenderAnnotations = false, loadingQueue = new LoadingQueue, zoomHelper = + new gui.ZoomHelper; function loadImages(container, odffragment, stylesheet) { var i, images, node; function loadImage(name, container, node, stylesheet) { @@ -8579,34 +8303,46 @@ runtime.loadClass("gui.AnnotationViewManager"); } } function fixContainerSize() { - var odfdoc = sizer.firstChild; + var minHeight, odfdoc = sizer.firstChild, zoomLevel = zoomHelper.getZoomLevel(); 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 + ")"; + sizer.style.WebkitTransformOrigin = "0% 0%"; + sizer.style.MozTransformOrigin = "0% 0%"; + sizer.style.msTransformOrigin = "0% 0%"; + sizer.style.OTransformOrigin = "0% 0%"; + sizer.style.transformOrigin = "0% 0%"; + if(annotationViewManager) { + minHeight = annotationViewManager.getMinimumHeightForAnnotationPane(); + if(minHeight) { + sizer.style.minHeight = minHeight + }else { + sizer.style.removeProperty("min-height") + } + } element.style.width = Math.round(zoomLevel * sizer.offsetWidth) + "px"; element.style.height = Math.round(zoomLevel * sizer.offsetHeight) + "px" } + function redrawContainer() { + if(shouldRefreshCss) { + handleStyles(odfcontainer, formatting, stylesxmlcss); + shouldRefreshCss = false + } + if(shouldRerenderAnnotations) { + if(annotationViewManager) { + annotationViewManager.rerenderAnnotations() + } + shouldRerenderAnnotations = false + } + fixContainerSize() + } 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.style.setProperty("float", "left", "important"); sizer.appendChild(odfnode); element.appendChild(sizer); annotationsPane = (doc.createElementNS(element.namespaceURI, "div")); @@ -8620,7 +8356,6 @@ runtime.loadClass("gui.AnnotationViewManager"); 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); @@ -8628,30 +8363,24 @@ runtime.loadClass("gui.AnnotationViewManager"); loadVideos(container, odfnode.body); loadLists(odfnode.body, css, element.namespaceURI); sizer.insertBefore(shadowContent, sizer.firstChild); - fixContainerSize() + zoomHelper.setZoomableElement(sizer) } 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") - } - 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}) - } + var annotationNodes = (domUtils.getElementsByTagNameNS(odffragment, officens, "annotation")); + annotationNodes.forEach(annotationViewManager.addAnnotation); annotationViewManager.rerenderAnnotations() } function handleAnnotations(odfnode) { if(allowAnnotations) { if(!annotationsPane.parentNode) { - sizer.appendChild(annotationsPane); - fixContainerSize() + sizer.appendChild(annotationsPane) } if(annotationViewManager) { annotationViewManager.forgetAnnotations() } - annotationViewManager = new gui.AnnotationViewManager(self, odfnode.body, annotationsPane); - modifyAnnotations(odfnode.body) + annotationViewManager = new gui.AnnotationViewManager(self, odfnode.body, annotationsPane, showAnnotationRemoveButton); + modifyAnnotations(odfnode.body); + fixContainerSize() }else { if(annotationsPane.parentNode) { sizer.removeChild(annotationsPane); @@ -8662,6 +8391,9 @@ runtime.loadClass("gui.AnnotationViewManager"); } function refreshOdf(suppressEvent) { function callback() { + clearCSSStyleSheet(fontcss); + clearCSSStyleSheet(stylesxmlcss); + clearCSSStyleSheet(positioncss); clear(element); element.style.display = "inline-block"; var odfnode = odfcontainer.rootElement; @@ -8679,22 +8411,22 @@ runtime.loadClass("gui.AnnotationViewManager"); callback() }else { runtime.log("WARNING: refreshOdf called but ODF was not DONE."); - runtime.setTimeout(function later_cb() { + waitingForDoneTimeoutId = runtime.setTimeout(function later_cb() { if(odfcontainer.state === odf.OdfContainer.DONE) { callback() }else { runtime.log("will be back later..."); - runtime.setTimeout(later_cb, 500) + waitingForDoneTimeoutId = runtime.setTimeout(later_cb, 500) } }, 100) } } this.refreshCSS = function() { - handleStyles(odfcontainer, formatting, stylesxmlcss); - fixContainerSize() + shouldRefreshCss = true; + redrawContainerTask.trigger() }; this.refreshSize = function() { - fixContainerSize() + redrawContainerTask.trigger() }; this.odfContainer = function() { return odfcontainer @@ -8738,15 +8470,17 @@ runtime.loadClass("gui.AnnotationViewManager"); }; this.rerenderAnnotations = function() { if(annotationViewManager) { - annotationViewManager.rerenderAnnotations() + shouldRerenderAnnotations = true; + redrawContainerTask.trigger() } }; this.getSizer = function() { - return sizer + return(sizer) }; - this.enableAnnotations = function(allow) { + this.enableAnnotations = function(allow, showRemoveButton) { if(allow !== allowAnnotations) { allowAnnotations = allow; + showAnnotationRemoveButton = showRemoveButton; if(odfcontainer) { handleAnnotations(odfcontainer.rootElement) } @@ -8754,36 +8488,39 @@ runtime.loadClass("gui.AnnotationViewManager"); }; this.addAnnotation = function(annotation) { if(annotationViewManager) { - annotationViewManager.addAnnotation(annotation) + annotationViewManager.addAnnotation(annotation); + fixContainerSize() } }; this.forgetAnnotations = function() { if(annotationViewManager) { - annotationViewManager.forgetAnnotations() + annotationViewManager.forgetAnnotations(); + fixContainerSize() } }; + this.getZoomHelper = function() { + return zoomHelper + }; this.setZoomLevel = function(zoom) { - zoomLevel = zoom; - fixContainerSize() + zoomHelper.setZoomLevel(zoom) }; this.getZoomLevel = function() { - return zoomLevel + return zoomHelper.getZoomLevel() }; this.fitToContainingElement = function(width, height) { - var realWidth = element.offsetWidth / zoomLevel, realHeight = element.offsetHeight / zoomLevel; - zoomLevel = width / realWidth; - if(height / realHeight < zoomLevel) { - zoomLevel = height / realHeight + var zoomLevel = zoomHelper.getZoomLevel(), realWidth = element.offsetWidth / zoomLevel, realHeight = element.offsetHeight / zoomLevel, zoom; + zoom = width / realWidth; + if(height / realHeight < zoom) { + zoom = height / realHeight } - fixContainerSize() + zoomHelper.setZoomLevel(zoom) }; this.fitToWidth = function(width) { - var realWidth = element.offsetWidth / zoomLevel; - zoomLevel = width / realWidth; - fixContainerSize() + var realWidth = element.offsetWidth / zoomHelper.getZoomLevel(); + zoomHelper.setZoomLevel(width / realWidth) }; this.fitSmart = function(width, height) { - var realWidth, realHeight, newScale; + var realWidth, realHeight, newScale, zoomLevel = zoomHelper.getZoomLevel(); realWidth = element.offsetWidth / zoomLevel; realHeight = element.offsetHeight / zoomLevel; newScale = width / realWidth; @@ -8792,13 +8529,11 @@ runtime.loadClass("gui.AnnotationViewManager"); newScale = height / realHeight } } - zoomLevel = Math.min(1, newScale); - fixContainerSize() + zoomHelper.setZoomLevel(Math.min(1, newScale)) }; this.fitToHeight = function(height) { - var realHeight = element.offsetHeight / zoomLevel; - zoomLevel = height / realHeight; - fixContainerSize() + var realHeight = element.offsetHeight / zoomHelper.getZoomLevel(); + zoomHelper.setZoomLevel(height / realHeight) }; this.showFirstPage = function() { pageSwitcher.showFirstPage() @@ -8824,169 +8559,102 @@ runtime.loadClass("gui.AnnotationViewManager"); } }; this.destroy = function(callback) { - var head = (doc.getElementsByTagName("head")[0]); + var head = (doc.getElementsByTagName("head")[0]), cleanup = [pageSwitcher.destroy, redrawContainerTask.destroy]; + runtime.clearTimeout(waitingForDoneTimeoutId); if(annotationsPane && annotationsPane.parentNode) { annotationsPane.parentNode.removeChild(annotationsPane) } - if(sizer) { - element.removeChild(sizer); - sizer = null - } - head.removeChild(webodfcss); + zoomHelper.destroy(function() { + if(sizer) { + element.removeChild(sizer); + sizer = null + } + }); + removeWebODFStyleSheet(webodfcss); head.removeChild(fontcss); head.removeChild(stylesxmlcss); head.removeChild(positioncss); - pageSwitcher.destroy(callback) + async.destroyAll(cleanup, callback) }; function init() { webodfcss = addWebODFStyleSheet(doc); pageSwitcher = new PageSwitcher(addStyleSheet(doc)); fontcss = addStyleSheet(doc); stylesxmlcss = addStyleSheet(doc); - positioncss = addStyleSheet(doc) + positioncss = addStyleSheet(doc); + redrawContainerTask = new core.ScheduledTask(redrawContainer, 0); + zoomHelper.subscribe(gui.ZoomHelper.signalZoomChanged, fixContainerSize) } 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("core.DomUtils"); -runtime.loadClass("core.LoopWatchDog"); -runtime.loadClass("odf.Namespaces"); -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) { - function compare(expected, actual) { - if(typeof expected === "object" && typeof actual === "object") { - return Object.keys(expected).every(function(key) { - return compare(expected[key], actual[key]) - }) - } - return expected === actual - } - this.isStyleApplied = function(textNode) { - var appliedStyle = formatting.getAppliedStylesForElement(textNode); - return compare(info, appliedStyle) - } - } - function StyleManager(info) { - var createdStyles = {}; - function createDirectFormat(existingStyleName, document) { - var derivedStyleInfo, derivedStyleNode; - derivedStyleInfo = existingStyleName ? formatting.createDerivedStyleObject(existingStyleName, "text", info) : info; - derivedStyleNode = document.createElementNS(stylens, "style:style"); - formatting.updateStyle(derivedStyleNode, derivedStyleInfo); - derivedStyleNode.setAttributeNS(stylens, "style:name", objectNameGenerator.generateStyleName()); - derivedStyleNode.setAttributeNS(stylens, "style:family", "text"); - derivedStyleNode.setAttributeNS(webodfns, "scope", "document-content"); - automaticStyles.appendChild(derivedStyleNode); - return derivedStyleNode - } - function getDirectStyle(existingStyleName, document) { - existingStyleName = existingStyleName || ""; - if(!createdStyles.hasOwnProperty(existingStyleName)) { - createdStyles[existingStyleName] = createDirectFormat(existingStyleName, document) - } - return createdStyles[existingStyleName].getAttributeNS(stylens, "name") - } - this.applyStyleToContainer = function(container) { - var name = getDirectStyle(container.getAttributeNS(textns, "style-name"), container.ownerDocument); - container.setAttributeNS(textns, "text:style-name", name) - } +ops.MemberProperties = function() { + this.fullName; + this.color; + this.imageUrl +}; +ops.Member = function Member(memberId, properties) { + var props = new ops.MemberProperties; + function getMemberId() { + return memberId } - function isTextSpan(node) { - return node.localName === "span" && node.namespaceURI === textns + function getProperties() { + return props } - function moveToNewSpan(startNode, limits) { - var document = startNode.ownerDocument, originalContainer = (startNode.parentNode), styledContainer, trailingContainer, moveTrailing, node, nextNode, loopGuard = new core.LoopWatchDog(1E4), styledNodes = []; - if(!isTextSpan(originalContainer)) { - styledContainer = document.createElementNS(textns, "text:span"); - originalContainer.insertBefore(styledContainer, startNode); - moveTrailing = false - }else { - if(startNode.previousSibling && !domUtils.rangeContainsNode(limits, (originalContainer.firstChild))) { - styledContainer = originalContainer.cloneNode(false); - originalContainer.parentNode.insertBefore(styledContainer, originalContainer.nextSibling); - moveTrailing = true - }else { - styledContainer = originalContainer; - moveTrailing = true + function setProperties(newProperties) { + Object.keys(newProperties).forEach(function(key) { + props[key] = newProperties[key] + }) + } + function removeProperties(removedProperties) { + Object.keys(removedProperties).forEach(function(key) { + if(key !== "fullName" && (key !== "color" && (key !== "imageUrl" && props.hasOwnProperty(key)))) { + delete props[key] } + }) + } + this.getMemberId = getMemberId; + this.getProperties = getProperties; + this.setProperties = setProperties; + this.removeProperties = removeProperties; + function init() { + runtime.assert(Boolean(memberId), "No memberId was supplied!"); + if(!properties.fullName) { + properties.fullName = runtime.tr("Unknown Author") } - styledNodes.push(startNode); - node = startNode.nextSibling; - while(node && domUtils.rangeContainsNode(limits, node)) { - loopGuard.check(); - styledNodes.push(node); - node = node.nextSibling - } - styledNodes.forEach(function(n) { - if(n.parentNode !== styledContainer) { - styledContainer.appendChild(n) - } - }); - if(node && moveTrailing) { - trailingContainer = styledContainer.cloneNode(false); - styledContainer.parentNode.insertBefore(trailingContainer, styledContainer.nextSibling); - while(node) { - loopGuard.check(); - nextNode = node.nextSibling; - trailingContainer.appendChild(node); - node = nextNode - } + if(!properties.color) { + properties.color = "black" } - return(styledContainer) - } - this.applyStyle = function(textNodes, limits, info) { - var textPropsOnly = {}, isStyled, container, styleCache, styleLookup; - runtime.assert(info && info.hasOwnProperty(textProperties), "applyStyle without any text properties"); - textPropsOnly[textProperties] = info[textProperties]; - styleCache = new StyleManager(textPropsOnly); - styleLookup = new StyleLookup(textPropsOnly); - function apply(n) { - isStyled = styleLookup.isStyleApplied(n); - if(isStyled === false) { - container = moveToNewSpan(n, limits); - styleCache.applyStyleToContainer(container) - } + if(!properties.imageUrl) { + properties.imageUrl = "avatar-joe.png" } - textNodes.forEach(apply) + props = properties } + init() }; /* @@ -9025,1276 +8693,325 @@ odf.TextStyleApplicator = function TextStyleApplicator(objectNameGenerator, form @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.Namespaces"); -runtime.loadClass("odf.OdfUtils"); -gui.StyleHelper = function StyleHelper(formatting) { - var 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 formatting.getAppliedStyles(nodes) - } - this.getAppliedStyles = getAppliedStyles; - 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 - } - } - 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 - } - } - 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(!isDefaultParagraphStyleChecked) { - pickDefaultParagraphStyleElement() - }else { - paragraphStyleElement = undefined - } - } - 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 - } - } - nodes.pop() - } - return true +gui.StepCounter; +gui.SelectionMover = function SelectionMover(cursor, rootNode) { + var odfUtils = new odf.OdfUtils, positionIterator, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT; + function getIteratorAtCursor() { + positionIterator.setUnfilteredPosition(cursor.getNode(), 0); + return positionIterator } - 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"]) + function getMaximumNodePosition(node) { + return node.nodeType === Node.TEXT_NODE ? node.textContent.length : node.childNodes.length } -}; -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") - } - 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 - } - } - } - 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 - } - 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 - } - 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 - } - 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 - } - 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 - } - 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 - } - }; - 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) - } - }; - 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 - } - }; - 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 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 - } - } - if(!zip_eofile) { - n = zip_read_buff(zip_window, zip_strstart + zip_lookahead, more); - if(n <= 0) { - zip_eofile = true - }else { - zip_lookahead += n - } - } - }; - 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 - } - 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 - } - 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] - } - 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 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 - } - zip_heap[k] = zip_heap[j]; - k = j; - j <<= 1 - } - 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 - } - 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) - } - } - if(overflow === 0) { - return - } - 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-- - } - } - }; - 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 - } - tree[n].fc = zip_bi_reverse(next_code[len]++, len) - } - }; - 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 - } - } - 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 - } - } - 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++ - } - } - } - 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 - } - } - } - }; - 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 - } + function getClientRect(clientRectangles, useRightEdge) { + var rectangle, simplifiedRectangle = null; + if(clientRectangles && clientRectangles.length > 0) { + rectangle = useRightEdge ? clientRectangles.item(clientRectangles.length - 1) : clientRectangles.item(0) } - 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) - } - } - 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) + if(rectangle) { + simplifiedRectangle = {top:rectangle.top, left:useRightEdge ? rectangle.right : rectangle.left, bottom:rectangle.bottom} } - 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 - } - 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) + 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(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) + if(container.nodeType === Node.TEXT_NODE && offset > 0) { + rectangle = getVisibleRect(container, offset - 1, range, true) }else { - if(count <= 10) { - zip_SEND_CODE(zip_REPZ_3_10, zip_bl_tree); - zip_send_bits(count - 3, 3) + if(container.previousSibling) { + rectangle = getVisibleRect(container.previousSibling, getMaximumNodePosition(container.previousSibling), range, true) }else { - zip_SEND_CODE(zip_REPZ_11_138, zip_bl_tree); - zip_send_bits(count - 11, 7) + if(container.parentNode && container.parentNode !== rootNode) { + rectangle = getVisibleRect(container.parentNode, 0, range, false) + }else { + range.selectNode(rootNode); + rectangle = getClientRect(range.getClientRects(), false) + } } } } - 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 - } - } - } - }; - 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) - } - 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 - } - for(n = 0;n < zip_D_CODES;n++) { - zip_dyn_dtree[n].fc = 0 - } - for(n = 0;n < zip_BL_CODES;n++) { - zip_bl_tree[n].fc = 0 - } - 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(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 { - 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) - } } - zip_init_block(); - if(eof !== 0) { - zip_bi_windup() - } - }; - 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 + runtime.assert(Boolean(rectangle), "No visible rectangle found"); + return(rectangle) + } + 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 } - }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-- + return stepsFilter2 + } + 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 } } - 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 + } + return stepsFilter2 + } + 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; + left = rect.left; + 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 } - zip_strstart++; - zip_lookahead-- - }else { - zip_match_available = 1; - zip_strstart++; - zip_lookahead-- } } - while(zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) { - zip_fill_window() - } } - }; - var zip_ct_init = function() { - var n; - var bits; - var length; - var code; - var dist; - if(zip_static_dtree[0].dl !== 0) { - return - } - 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 - } - } - 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 - } - } - 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(bits = 0;bits <= zip_MAX_BITS;bits++) { - zip_bl_count[bits] = 0 - } - n = 0; - while(n <= 143) { - zip_static_ltree[n++].dl = 8; - zip_bl_count[8]++ - } - while(n <= 255) { - zip_static_ltree[n++].dl = 9; - zip_bl_count[9]++ - } - while(n <= 279) { - zip_static_ltree[n++].dl = 7; - zip_bl_count[7]++ - } - while(n <= 287) { - zip_static_ltree[n++].dl = 8; - zip_bl_count[8]++ - } - 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 + if(bestContainer !== null) { + iterator.setUnfilteredPosition(bestContainer, (bestOffset)); + steps = bestCount }else { - 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) - } - } - 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 - } + steps = 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 + range.detach(); + return steps + } + 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 } - 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() + 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 { - 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 + fnNextPos = iterator.nextPosition; + increment = 1 } - 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]) + 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 } - aout[aout.length] = cbuf.join(""); - i = zip_deflate_internal(buff, 0, buff.length) } - 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} + range.detach(); + return steps } - 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.getStepCounter = function() { + return{convertForwardStepsBetweenFilters:convertForwardStepsBetweenFilters, convertBackwardStepsBetweenFilters:convertBackwardStepsBetweenFilters, countLinesSteps:countLinesSteps, countStepsToLineBoundary:countStepsToLineBoundary} }; - this.isSelectorElement = function(node) { - var selectorElement = document.getElementById(imageSelectorId); - if(!selectorElement) { - return false - } - return node === selectorElement || node.parentNode === selectorElement + function init() { + positionIterator = gui.SelectionMover.createPositionIterator(rootNode); + var range = rootNode.ownerDocument.createRange(); + range.setStart(positionIterator.container(), positionIterator.unfilteredDomOffset()); + range.collapse(true); + cursor.setSelectedRange(range) } + init() }; -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) - }) - }else { - callback("Document was not completely loaded.") - } - } - var odfcontainer = new odf.OdfContainer(inputfilepath, onready); - return odfcontainer - }; - this.render = function(inputfilepath, document, callback) { - var body = document.getElementsByTagName("body")[0], odfcanvas; - while(body.firstChild) { - body.removeChild(body.firstChild) +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 } - odfcanvas = new odf.OdfCanvas(body); - odfcanvas.addListener("statereadychange", function(err) { - callback(err) - }); - odfcanvas.load(inputfilepath) } + var filter = new CursorFilter; + return new core.PositionIterator(rootNode, 5, filter, false) }; +(function() { + return gui.SelectionMover +})(); /* - 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.Member = function Member(memberId, properties) { - var props = {}; - function getMemberId() { +ops.Document = function Document() { +}; +ops.Document.prototype.getMemberIds = function() { +}; +ops.Document.prototype.removeCursor = function(memberid) { +}; +ops.Document.prototype.getDocumentElement = function() { +}; +ops.Document.prototype.getRootNode = function() { +}; +ops.Document.prototype.getDOMDocument = function() { +}; +ops.Document.prototype.cloneDocumentElement = function() { +}; +ops.Document.prototype.setDocumentElement = function(element) { +}; +ops.Document.prototype.subscribe = function(eventid, cb) { +}; +ops.Document.prototype.unsubscribe = function(eventid, cb) { +}; +ops.Document.prototype.getCanvas = function() { +}; +ops.Document.prototype.createRootFilter = function(inputMemberId) { +}; +ops.Document.signalCursorAdded = "cursor/added"; +ops.Document.signalCursorRemoved = "cursor/removed"; +ops.Document.signalCursorMoved = "cursor/moved"; +ops.Document.signalMemberAdded = "member/added"; +ops.Document.signalMemberUpdated = "member/updated"; +ops.Document.signalMemberRemoved = "member/removed"; +ops.OdtCursor = function OdtCursor(memberId, document) { + var self = this, validSelectionTypes = {}, selectionType, selectionMover, cursor, events = new core.EventNotifier([ops.OdtCursor.signalCursorUpdated]); + this.removeFromDocument = function() { + cursor.remove() + }; + this.subscribe = function(eventid, cb) { + events.subscribe(eventid, cb) + }; + this.unsubscribe = function(eventid, cb) { + events.unsubscribe(eventid, cb) + }; + this.getStepCounter = function() { + return selectionMover.getStepCounter() + }; + this.getMemberId = function() { return memberId - } - function getProperties() { - return props - } - function setProperties(newProperties) { - Object.keys(newProperties).forEach(function(key) { - props[key] = newProperties[key] - }) - } - function removeProperties(removedProperties) { - delete removedProperties.fullName; - delete removedProperties.color; - delete removedProperties.imageUrl; - Object.keys(removedProperties).forEach(function(key) { - if(props.hasOwnProperty(key)) { - delete props[key] - } - }) - } - this.getMemberId = getMemberId; - this.getProperties = getProperties; - this.setProperties = setProperties; - this.removeProperties = removeProperties; - function init() { - runtime.assert(Boolean(memberId), "No memberId was supplied!"); - if(!properties.fullName) { - properties.fullName = runtime.tr("Unknown Author") - } - if(!properties.color) { - properties.color = "black" - } - if(!properties.imageUrl) { - properties.imageUrl = "avatar-joe.png" + }; + 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); + events.emit(ops.OdtCursor.signalCursorUpdated, self) + }; + this.hasForwardSelection = function() { + return cursor.hasForwardSelection() + }; + this.getDocument = function() { + return document + }; + this.getSelectionType = function() { + return selectionType + }; + this.setSelectionType = function(value) { + if(validSelectionTypes.hasOwnProperty(value)) { + selectionType = value + }else { + runtime.log("Invalid selection type: " + value) } - props = properties + }; + this.resetSelectionType = function() { + self.setSelectionType(ops.OdtCursor.RangeSelection) + }; + function init() { + cursor = new core.Cursor(document.getDOMDocument(), memberId); + selectionMover = new gui.SelectionMover(cursor, document.getRootNode()); + validSelectionTypes[ops.OdtCursor.RangeSelection] = true; + validSelectionTypes[ops.OdtCursor.RegionSelection] = true; + self.resetSelectionType() } init() }; +ops.OdtCursor.RangeSelection = "Range"; +ops.OdtCursor.RegionSelection = "Region"; +ops.OdtCursor.signalCursorUpdated = "cursorUpdated"; +(function() { + return ops.OdtCursor +})(); /* Copyright (C) 2012-2013 KO GmbH @@ -10332,26 +9049,52 @@ ops.Member = function Member(memberId, properties) { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils"); -runtime.loadClass("core.PositionFilter"); -runtime.loadClass("odf.OdfUtils"); +ops.Operation = function Operation() { +}; +ops.Operation.prototype.init = function(data) { +}; +ops.Operation.prototype.isEdit; +ops.Operation.prototype.group; +ops.Operation.prototype.execute = function(document) { +}; +ops.Operation.prototype.spec = function() { +}; +/* + + Copyright (C) 2010-2014 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/ +*/ (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) { + var nextNodeId = 0; + ops.StepsCache = function StepsCache(rootElement, filter, bucketSize) { + var coordinatens = "urn:webodf:names:steps", stepToDomPoint = {}, nodeToBookmark = {}, odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils, basePoint, lastUndamagedCacheStep, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, verifyCache; + function ParagraphBookmark(nodeId, steps, paragraphNode) { + this.nodeId = nodeId; this.steps = steps; this.node = paragraphNode; - function positionInContainer(node) { - var position = 0; - while(node && node.previousSibling) { - position += 1; - node = node.previousSibling - } - return position - } + this.nextBookmark = null; + this.previousBookmark = null; this.setIteratorPosition = function(iterator) { - iterator.setUnfilteredPosition(paragraphNode.parentNode, positionInContainer(paragraphNode)); + iterator.setPositionBeforeElement(paragraphNode); do { if(filter.acceptPosition(iterator) === FILTER_ACCEPT) { break @@ -10359,9 +9102,12 @@ runtime.loadClass("odf.OdfUtils"); }while(iterator.nextPosition()) } } - function RootBookmark(steps, rootNode) { + function RootBookmark(nodeId, steps, rootNode) { + this.nodeId = nodeId; this.steps = steps; this.node = rootNode; + this.nextBookmark = null; + this.previousBookmark = null; this.setIteratorPosition = function(iterator) { iterator.setUnfilteredPosition(rootNode, 0); do { @@ -10371,6 +9117,47 @@ runtime.loadClass("odf.OdfUtils"); }while(iterator.nextPosition()) } } + function inspectBookmarks(bookmark1, bookmark2) { + var parts = "[" + bookmark1.nodeId; + if(bookmark2) { + parts += " => " + bookmark2.nodeId + } + return parts + "]" + } + function isUndamagedBookmark(bookmark) { + return lastUndamagedCacheStep === undefined || bookmark.steps <= lastUndamagedCacheStep + } + function verifyCacheImpl() { + var bookmark = basePoint, previousBookmark, nextBookmark, documentPosition, loopCheck = new core.LoopWatchDog(0, 1E5); + while(bookmark) { + loopCheck.check(); + previousBookmark = bookmark.previousBookmark; + if(previousBookmark) { + runtime.assert(previousBookmark.nextBookmark === bookmark, "Broken bookmark link to previous @" + inspectBookmarks(previousBookmark, bookmark)) + }else { + runtime.assert(bookmark === basePoint, "Broken bookmark link @" + inspectBookmarks(bookmark)); + runtime.assert(isUndamagedBookmark(basePoint), "Base point is damaged @" + inspectBookmarks(bookmark)) + } + nextBookmark = bookmark.nextBookmark; + if(nextBookmark) { + runtime.assert(nextBookmark.previousBookmark === bookmark, "Broken bookmark link to next @" + inspectBookmarks(bookmark, nextBookmark)) + } + if(isUndamagedBookmark(bookmark)) { + runtime.assert(domUtils.containsNode(rootElement, bookmark.node), "Disconnected node is being reported as undamaged @" + inspectBookmarks(bookmark)); + if(previousBookmark) { + documentPosition = bookmark.node.compareDocumentPosition(previousBookmark.node); + runtime.assert(documentPosition === 0 || (documentPosition & Node.DOCUMENT_POSITION_PRECEDING) !== 0, "Bookmark order with previous does not reflect DOM order @" + inspectBookmarks(previousBookmark, bookmark)) + } + if(nextBookmark) { + if(domUtils.containsNode(rootElement, nextBookmark.node)) { + documentPosition = bookmark.node.compareDocumentPosition(nextBookmark.node); + runtime.assert(documentPosition === 0 || (documentPosition & Node.DOCUMENT_POSITION_FOLLOWING) !== 0, "Bookmark order with next does not reflect DOM order @" + inspectBookmarks(bookmark, nextBookmark)) + } + } + } + bookmark = bookmark.nextBookmark + } + } function getBucket(steps) { return Math.floor(steps / bucketSize) * bucketSize } @@ -10381,11 +9168,15 @@ runtime.loadClass("odf.OdfUtils"); node.removeAttributeNS(coordinatens, "nodeId") } function getNodeId(node) { - return node.nodeType === Node.ELEMENT_NODE && node.getAttributeNS(coordinatens, "nodeId") + var id = ""; + if(node.nodeType === Node.ELEMENT_NODE) { + id = (node).getAttributeNS(coordinatens, "nodeId") + } + return id } function setNodeId(node) { - var nodeId = nextNodeId; - node.setAttributeNS(coordinatens, "nodeId", nodeId.toString()); + var nodeId = nextNodeId.toString(); + node.setAttributeNS(coordinatens, "nodeId", nodeId); nextNodeId += 1; return nodeId } @@ -10396,159 +9187,258 @@ runtime.loadClass("odf.OdfUtils"); var nodeId = getNodeId(node) || setNodeId(node), existingBookmark; existingBookmark = nodeToBookmark[nodeId]; if(!existingBookmark) { - existingBookmark = nodeToBookmark[nodeId] = new ParagraphBookmark(steps, node) + existingBookmark = nodeToBookmark[nodeId] = new ParagraphBookmark(nodeId, 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) + existingBookmark = nodeToBookmark[nodeId] = new ParagraphBookmark(nodeId, steps, node) }else { existingBookmark.steps = steps } } return existingBookmark } - function isFirstPositionInParagraph(node, offset) { - return offset === 0 && odfUtils.isParagraph(node) + function getClosestBookmark(steps) { + var cacheBucket, cachePoint, loopGuard = new core.LoopWatchDog(0, 1E4); + if(lastUndamagedCacheStep !== undefined && steps > lastUndamagedCacheStep) { + steps = lastUndamagedCacheStep + } + cacheBucket = getBucket(steps); + while(!cachePoint && cacheBucket !== 0) { + cachePoint = stepToDomPoint[cacheBucket]; + cacheBucket -= bucketSize + } + cachePoint = cachePoint || basePoint; + while(cachePoint.nextBookmark && cachePoint.nextBookmark.steps <= steps) { + loopGuard.check(); + cachePoint = cachePoint.nextBookmark + } + return cachePoint } - 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 + function getUndamagedBookmark(bookmark) { + if(lastUndamagedCacheStep !== undefined && bookmark.steps > lastUndamagedCacheStep) { + bookmark = getClosestBookmark(lastUndamagedCacheStep) + } + return bookmark + } + function removeBookmark(currentBookmark) { + if(currentBookmark.previousBookmark) { + currentBookmark.previousBookmark.nextBookmark = currentBookmark.nextBookmark + } + if(currentBookmark.nextBookmark) { + currentBookmark.nextBookmark.previousBookmark = currentBookmark.previousBookmark + } + } + function insertBookmark(previousBookmark, newBookmark) { + var nextBookmark; + if(previousBookmark !== newBookmark && previousBookmark.nextBookmark !== newBookmark) { + removeBookmark(newBookmark); + nextBookmark = previousBookmark.nextBookmark; + newBookmark.nextBookmark = previousBookmark.nextBookmark; + newBookmark.previousBookmark = previousBookmark; + previousBookmark.nextBookmark = newBookmark; + if(nextBookmark) { + nextBookmark.previousBookmark = newBookmark + } + } + } + function repairCacheUpToStep(currentIteratorStep) { + var damagedBookmark, undamagedBookmark, nextBookmark, stepsBucket; + if(lastUndamagedCacheStep !== undefined && lastUndamagedCacheStep < currentIteratorStep) { + undamagedBookmark = getClosestBookmark(lastUndamagedCacheStep); + damagedBookmark = undamagedBookmark.nextBookmark; + while(damagedBookmark && damagedBookmark.steps <= currentIteratorStep) { + nextBookmark = damagedBookmark.nextBookmark; + stepsBucket = getDestinationBucket(damagedBookmark.steps); + if(stepToDomPoint[stepsBucket] === damagedBookmark) { + delete stepToDomPoint[stepsBucket] + } + if(!domUtils.containsNode(rootElement, damagedBookmark.node)) { + removeBookmark(damagedBookmark); + delete nodeToBookmark[damagedBookmark.nodeId] + }else { + damagedBookmark.steps = currentIteratorStep + 1 } + damagedBookmark = nextBookmark } + lastUndamagedCacheStep = currentIteratorStep + }else { + undamagedBookmark = getClosestBookmark(currentIteratorStep) } - if(stablePoint) { - bookmark = getNodeBookmark(node, steps); + return undamagedBookmark + } + this.updateCache = function(steps, iterator, isStep) { + var cacheBucket, existingCachePoint, bookmark, closestPriorBookmark, node = iterator.getCurrentNode(); + if(iterator.isBeforeNode() && odfUtils.isParagraph(node)) { + if(!isStep) { + steps += 1 + } + closestPriorBookmark = repairCacheUpToStep(steps); + bookmark = getNodeBookmark((node), steps); + insertBookmark(closestPriorBookmark, bookmark); cacheBucket = getDestinationBucket(bookmark.steps); existingCachePoint = stepToDomPoint[cacheBucket]; if(!existingCachePoint || bookmark.steps > existingCachePoint.steps) { stepToDomPoint[cacheBucket] = bookmark } + verifyCache() } }; this.setToClosestStep = function(steps, iterator) { - var cacheBucket = getBucket(steps), cachePoint; - while(!cachePoint && cacheBucket !== 0) { - cachePoint = stepToDomPoint[cacheBucket]; - cacheBucket -= bucketSize - } - cachePoint = cachePoint || basePoint; + var cachePoint; + verifyCache(); + cachePoint = getClosestBookmark(steps); 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); + function findBookmarkedAncestor(node) { + var currentNode = node, nodeId, bookmark = null; + while(!bookmark && (currentNode && currentNode !== rootElement)) { + nodeId = getNodeId(currentNode); if(nodeId) { bookmark = nodeToBookmark[nodeId]; - if(bookmark && !isValidBookmarkForNode(node, bookmark)) { + if(bookmark && !isValidBookmarkForNode(currentNode, bookmark)) { runtime.log("Cloned node detected. Creating new bookmark"); bookmark = null; - clearNodeId(node) + clearNodeId((currentNode)) } } - node = node.parentNode + currentNode = currentNode.parentNode } return bookmark } this.setToClosestDomPoint = function(node, offset, iterator) { - var bookmark; - if(node === rootNode && offset === 0) { + var bookmark, b, key; + verifyCache(); + if(node === rootElement && offset === 0) { bookmark = basePoint }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) + if(node === rootElement && offset === rootElement.childNodes.length) { + bookmark = basePoint; + for(key in stepToDomPoint) { + if(stepToDomPoint.hasOwnProperty(key)) { + b = stepToDomPoint[key]; + if(b.steps > bookmark.steps) { + bookmark = b + } + } + } }else { - bookmark = findBookmarkedAncestor(node, offset); + bookmark = findBookmarkedAncestor(node.childNodes.item(offset) || node); if(!bookmark) { iterator.setUnfilteredPosition(node, offset); while(!bookmark && iterator.previousNode()) { - bookmark = findBookmarkedAncestor(iterator.container(), iterator.unfilteredDomOffset()) + bookmark = findBookmarkedAncestor(iterator.getCurrentNode()) } } } } - bookmark = bookmark || basePoint; + bookmark = getUndamagedBookmark(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] + this.damageCacheAfterStep = function(inflectionStep) { + if(inflectionStep < 0) { + inflectionStep = 0 + } + if(lastUndamagedCacheStep === undefined) { + lastUndamagedCacheStep = inflectionStep + }else { + if(inflectionStep < lastUndamagedCacheStep) { + lastUndamagedCacheStep = inflectionStep } - }); - Object.keys(updatedBuckets).forEach(function(cacheBucket) { - stepToDomPoint[cacheBucket] = updatedBuckets[cacheBucket] - }) + } + verifyCache() }; function init() { - basePoint = new RootBookmark(0, rootNode) + var rootElementId = getNodeId(rootElement) || setNodeId(rootElement); + basePoint = new RootBookmark(rootElementId, 0, rootElement); + verifyCache = ops.StepsCache.ENABLE_CACHE_VERIFICATION ? verifyCacheImpl : function() { + } } init() + }; + ops.StepsCache.ENABLE_CACHE_VERIFICATION = false; + ops.StepsCache.Bookmark = function Bookmark() { + }; + ops.StepsCache.Bookmark.prototype.nodeId; + ops.StepsCache.Bookmark.prototype.node; + ops.StepsCache.Bookmark.prototype.steps; + ops.StepsCache.Bookmark.prototype.previousBookmark; + ops.StepsCache.Bookmark.prototype.nextBookmark; + ops.StepsCache.Bookmark.prototype.setIteratorPosition = function(iterator) { } +})(); +/* + + 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 PREVIOUS_STEP = 0, NEXT_STEP = 1; 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; + var rootNode = getRootNode(), stepsCache = new ops.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); + stepsCache = new ops.StepsCache(rootNode, filter, bucketSize); iterator = newIterator(rootNode) } } this.convertStepsToDomPoint = function(steps) { - var stepsFromRoot, isWalkable; + var stepsFromRoot, isStep; + if(isNaN(steps)) { + throw new TypeError("Requested steps is not numeric (" + steps + ")"); + } if(steps < 0) { - runtime.log("warn", "Requested steps were negative (" + steps + ")"); - steps = 0 + throw new RangeError("Requested steps is negative (" + steps + ")"); } verifyRootNode(); stepsFromRoot = stepsCache.setToClosestStep(steps, iterator); while(stepsFromRoot < steps && iterator.nextPosition()) { - isWalkable = filter.acceptPosition(iterator) === FILTER_ACCEPT; - if(isWalkable) { + isStep = filter.acceptPosition(iterator) === FILTER_ACCEPT; + if(isStep) { stepsFromRoot += 1 } - stepsCache.updateCache(stepsFromRoot, iterator.container(), iterator.unfilteredDomOffset(), isWalkable) + stepsCache.updateCache(stepsFromRoot, iterator, isStep) } if(stepsFromRoot !== steps) { - runtime.log("warn", "Requested " + steps + " steps but only " + stepsFromRoot + " are available") + throw new RangeError("Requested steps (" + steps + ") exceeds available steps (" + stepsFromRoot + ")"); } return{node:iterator.container(), offset:iterator.unfilteredDomOffset()} }; @@ -10575,12 +9465,12 @@ runtime.loadClass("odf.OdfUtils"); return false } this.convertDomPointToSteps = function(node, offset, roundDirection) { - var stepsFromRoot, beforeRoot, destinationNode, destinationOffset, rounding = 0, isWalkable; + var stepsFromRoot, beforeRoot, destinationNode, destinationOffset, rounding = 0, isStep; verifyRootNode(); if(!domUtils.containsNode(rootNode, node)) { beforeRoot = domUtils.comparePoints(rootNode, 0, node, offset) < 0; - node = rootNode; - offset = beforeRoot ? 0 : rootNode.childNodes.length + node = (rootNode); + offset = beforeRoot ? 0 : (rootNode).childNodes.length } iterator.setUnfilteredPosition(node, offset); if(!roundToPreferredStep(iterator, roundDirection)) { @@ -10593,448 +9483,163 @@ runtime.loadClass("odf.OdfUtils"); return stepsFromRoot > 0 ? stepsFromRoot - 1 : stepsFromRoot } while(!(iterator.container() === destinationNode && iterator.unfilteredDomOffset() === destinationOffset) && iterator.nextPosition()) { - isWalkable = filter.acceptPosition(iterator) === FILTER_ACCEPT; - if(isWalkable) { + isStep = filter.acceptPosition(iterator) === FILTER_ACCEPT; + if(isStep) { stepsFromRoot += 1 } - stepsCache.updateCache(stepsFromRoot, iterator.container(), iterator.unfilteredDomOffset(), isWalkable) + stepsCache.updateCache(stepsFromRoot, iterator, isStep) } return stepsFromRoot + rounding }; this.prime = function() { - var stepsFromRoot, isWalkable; + var stepsFromRoot, isStep; verifyRootNode(); stepsFromRoot = stepsCache.setToClosestStep(0, iterator); while(iterator.nextPosition()) { - isWalkable = filter.acceptPosition(iterator) === FILTER_ACCEPT; - if(isWalkable) { + isStep = filter.acceptPosition(iterator) === FILTER_ACCEPT; + if(isStep) { stepsFromRoot += 1 } - stepsCache.updateCache(stepsFromRoot, iterator.container(), iterator.unfilteredDomOffset(), isWalkable) + stepsCache.updateCache(stepsFromRoot, iterator, isStep) } }; this.handleStepsInserted = function(eventArgs) { verifyRootNode(); - stepsCache.updateCacheAtPoint(eventArgs.position, function(bucket) { - bucket.steps += eventArgs.length - }) + stepsCache.damageCacheAfterStep(eventArgs.position) }; this.handleStepsRemoved = function(eventArgs) { verifyRootNode(); - stepsCache.updateCacheAtPoint(eventArgs.position, function(bucket) { - bucket.steps -= eventArgs.length; - if(bucket.steps < 0) { - bucket.steps = 0 - } - }) + stepsCache.damageCacheAfterStep(eventArgs.position - 1) } }; 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 - } - } - function splitToDuos(e) { - if(e.e.length <= 2) { - return e - } - 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 { - prefix = r[0] - } - for(i in nsmap) { - if(nsmap[i] === prefix) { - r[0] = i - } - } - 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 { - if(att.namespaceURI === xmlnsns) { - nsmap[att.value] = att.localName - } - } - } - 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 - } - 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 - } - } - return null - } - 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 = "" +/* + + 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.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) { + if(odfUtils.isInlineRoot(leftNode) && odfUtils.isGroupingElement(rightNode)) { + return FILTER_REJECT } - }else { - delete a.ns - } - if(name === "name") { - i = splitQName(text); - a.ns = i[0]; - text = i[1] - } - 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 { - if(e.length > 2 && (name === "choice" || (name === "group" || name === "interleave"))) { - e = splitToDuos({name:name, e:e}).e + r = odfUtils.lookLeftForCharacter(leftNode); + if(r === 1) { + return FILTER_ACCEPT } - } - 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(r === 2 && (odfUtils.scanRightForAnyCharacter(rightNode) || odfUtils.scanRightForAnyCharacter(odfUtils.nextNode(container)))) { + return FILTER_ACCEPT } } - ce = {name:name}; - if(e && e.length > 0) { - ce.e = e - } - for(i in a) { - if(a.hasOwnProperty(i)) { - ce.a = a; - break + 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(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} + if(!rightOfChar) { + return FILTER_REJECT } - 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) - } + leftNode = leftNode || odfUtils.previousNode(container); + return odfUtils.scanLeftForAnyCharacter(leftNode) ? FILTER_REJECT : FILTER_ACCEPT + } + 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 } - 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(nodeType === TEXT_NODE) { + if(!odfUtils.isGroupingElement(container.parentNode) || odfUtils.isWithinTrackedChanges(container.parentNode, getRootNode())) { + return FILTER_REJECT } - } - 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 + 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(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) + 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 { - e = def.e = [e[1]].concat(e[0].e) - } - }else { - if(e[1].name === "interleave") { - e = def.e = [e[0]].concat(e[1].e) + leftNode = odfUtils.previousNode(container); + if(odfUtils.scanLeftForNonSpace(leftNode)) { + r = FILTER_ACCEPT + } } - } - } - } - 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) + if(r === FILTER_ACCEPT) { + return odfUtils.isTrailingWhitespace((container), offset) ? FILTER_REJECT : FILTER_ACCEPT } - } - i += 1 - } - } - 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 + rightChar = (text[offset]); + if(odfUtils.isODFWhitespace(rightChar)) { + return FILTER_REJECT } + return odfUtils.scanLeftForAnyCharacter(odfUtils.previousNode(container)) ? FILTER_REJECT : FILTER_ACCEPT } - } - 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) - } - } - for(i = 0;i < elements.length;i += 1) { - resolveDefines(elements[i], defines) - } - 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) - } - } - 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 + leftNode = iterator.leftNode(); + rightNode = container; + container = (container.parentNode); + r = checkLeftRight(container, leftNode, rightNode) }else { - runtime.log("Invalid selection type: " + value) - } - }; - 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() + if(!odfUtils.isGroupingElement(container) || odfUtils.isWithinTrackedChanges(container, getRootNode())) { + r = FILTER_REJECT + }else { + leftNode = iterator.leftNode(); + rightNode = iterator.rightNode(); + r = checkLeftRight(container, leftNode, rightNode) + } + } + return r } - init() }; -ops.OdtCursor.RangeSelection = "Range"; -ops.OdtCursor.RegionSelection = "Region"; -(function() { - return ops.OdtCursor -})(); /* Copyright (C) 2012-2013 KO GmbH @@ -11072,27 +9677,39 @@ ops.OdtCursor.RegionSelection = "Region"; @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; + var self = this, odfUtils, domUtils, cursors = {}, members = {}, eventNotifier = new core.EventNotifier([ops.Document.signalMemberAdded, ops.Document.signalMemberUpdated, ops.Document.signalMemberRemoved, ops.Document.signalCursorAdded, ops.Document.signalCursorRemoved, ops.Document.signalCursorMoved, ops.OdtDocument.signalParagraphChanged, ops.OdtDocument.signalParagraphStyleModified, ops.OdtDocument.signalCommonStyleCreated, ops.OdtDocument.signalCommonStyleDeleted, ops.OdtDocument.signalTableAdded, + ops.OdtDocument.signalOperationStart, ops.OdtDocument.signalOperationEnd, ops.OdtDocument.signalProcessingBatchStart, ops.OdtDocument.signalProcessingBatchEnd, 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.getDocumentElement = function() { + return odfCanvas.odfContainer().rootElement + }; + this.getDOMDocument = function() { + return(this.getDocumentElement().ownerDocument) + }; + this.cloneDocumentElement = function() { + var rootElement = self.getDocumentElement(), annotationViewManager = odfCanvas.getAnnotationViewManager(), initialDoc; + if(annotationViewManager) { + annotationViewManager.forgetAnnotations() + } + initialDoc = rootElement.cloneNode(true); + odfCanvas.refreshAnnotations(); + return initialDoc + }; + this.setDocumentElement = function(documentElement) { + var odfContainer = odfCanvas.odfContainer(); + odfContainer.setRootElement(documentElement); + odfCanvas.setOdfContainer(odfContainer, true); + odfCanvas.refreshCSS() + }; + function getDOMDocument() { + return(self.getDocumentElement().ownerDocument) } - this.getDOM = getDOM; + this.getDOMDocument = getDOMDocument; function isRoot(node) { if(node.namespaceURI === odf.Namespaces.officens && node.localName === "text" || node.namespaceURI === odf.Namespaces.officens && node.localName === "annotation") { return true @@ -11120,6 +9737,19 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { return FILTER_REJECT } } + function createStepIterator(container, offset, filters, subTree) { + var positionIterator = gui.SelectionMover.createPositionIterator(subTree), filterOrChain, stepIterator; + if(filters.length === 1) { + filterOrChain = filters[0] + }else { + filterOrChain = new core.PositionFilterChain; + filters.forEach(filterOrChain.addFilter) + } + stepIterator = new core.StepIterator(filterOrChain, positionIterator); + stepIterator.setPosition(container, offset); + return stepIterator + } + this.createStepIterator = createStepIterator; function getIteratorAtPosition(position) { var iterator = gui.SelectionMover.createPositionIterator(getRootNode()), point = stepsTranslator.convertStepsToDomPoint(position); iterator.setUnfilteredPosition(point.node, point.offset); @@ -11130,18 +9760,18 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { return stepsTranslator.convertDomPointToSteps(node, offset, roundDirection) }; this.convertDomToCursorRange = function(selection, constraint) { - var point1, point2, anchorConstraint = constraint(selection.anchorNode, selection.anchorOffset), focusConstraint; + var point1, point2, anchorConstraint = constraint && 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); + focusConstraint = constraint && constraint(selection.focusNode, selection.focusOffset); point2 = stepsTranslator.convertDomPointToSteps(selection.focusNode, selection.focusOffset, focusConstraint) } return{position:point1, length:point2 - point1} }; this.convertCursorToDomRange = function(position, length) { - var range = getDOM().createRange(), point1, point2; + var range = getDOMDocument().createRange(), point1, point2; point1 = stepsTranslator.convertStepsToDomPoint(position); if(length) { point2 = stepsTranslator.convertStepsToDomPoint(position + length); @@ -11158,7 +9788,7 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { return range }; function getTextNodeAtStep(steps, memberid) { - var iterator = getIteratorAtPosition(steps), node = iterator.container(), lastTextNode, nodeOffset = 0, cursorNode = null; + var iterator = getIteratorAtPosition(steps), node = iterator.container(), lastTextNode, nodeOffset = 0, cursorNode = null, text; if(node.nodeType === Node.TEXT_NODE) { lastTextNode = (node); nodeOffset = (iterator.unfilteredDomOffset()); @@ -11166,12 +9796,12 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { if(nodeOffset > 0) { lastTextNode = lastTextNode.splitText(nodeOffset) } - lastTextNode.parentNode.insertBefore(getDOM().createTextNode(""), lastTextNode); + lastTextNode.parentNode.insertBefore(getDOMDocument().createTextNode(""), lastTextNode); lastTextNode = (lastTextNode.previousSibling); nodeOffset = 0 } }else { - lastTextNode = getDOM().createTextNode(""); + lastTextNode = getDOMDocument().createTextNode(""); nodeOffset = 0; node.insertBefore(lastTextNode, iterator.rightNode()) } @@ -11182,7 +9812,7 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { cursorNode.parentNode.insertBefore(cursorNode.nextSibling, cursorNode) } if(lastTextNode.length > 0 && lastTextNode.nextSibling !== cursorNode) { - lastTextNode = getDOM().createTextNode(""); + lastTextNode = getDOMDocument().createTextNode(""); nodeOffset = 0 } cursorNode.parentNode.insertBefore(lastTextNode, cursorNode) @@ -11193,14 +9823,16 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { } } while(lastTextNode.previousSibling && lastTextNode.previousSibling.nodeType === Node.TEXT_NODE) { - lastTextNode.previousSibling.appendData(lastTextNode.data); - nodeOffset = (lastTextNode.previousSibling.length); - lastTextNode = (lastTextNode.previousSibling); + text = (lastTextNode.previousSibling); + text.appendData(lastTextNode.data); + nodeOffset = text.length; + lastTextNode = text; lastTextNode.parentNode.removeChild(lastTextNode.nextSibling) } while(lastTextNode.nextSibling && lastTextNode.nextSibling.nodeType === Node.TEXT_NODE) { - lastTextNode.appendData(lastTextNode.nextSibling.data); - lastTextNode.parentNode.removeChild(lastTextNode.nextSibling) + text = (lastTextNode.nextSibling); + lastTextNode.appendData(text.data); + lastTextNode.parentNode.removeChild(text) } return{textNode:lastTextNode, offset:nodeOffset} } @@ -11217,7 +9849,7 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { function getParagraphStyleAttributes(styleName) { var node = getParagraphStyleElement(styleName); if(node) { - return odfCanvas.getFormatting().getInheritedStyleAttributes(node) + return odfCanvas.getFormatting().getInheritedStyleAttributes(node, false) } return null } @@ -11237,13 +9869,20 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { } 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"); + var space = textNode.ownerDocument.createElementNS(odf.Namespaces.textns, "text:s"), container = textNode.parentNode, adjacentNode = textNode; space.appendChild(textNode.ownerDocument.createTextNode(" ")); - textNode.deleteData(offset, 1); - if(offset > 0) { - textNode = (textNode.splitText(offset)) + if(textNode.length === 1) { + container.replaceChild(space, textNode) + }else { + textNode.deleteData(offset, 1); + if(offset > 0) { + if(offset < textNode.length) { + textNode.splitText(offset) + } + adjacentNode = textNode.nextSibling + } + container.insertBefore(space, adjacentNode) } - textNode.parentNode.insertBefore(space, textNode); return space } function upgradeWhitespacesAtPosition(position) { @@ -11253,7 +9892,7 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { 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))) { + if(container.nodeType === Node.TEXT_NODE && (container.data[offset] === " " && odfUtils.isSignificantWhitespace((container), offset))) { container = upgradeWhitespaceToElement((container), offset); iterator.moveToEndOfNode(container) } @@ -11265,12 +9904,12 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { var iterator = getIteratorAtPosition(position), container, offset, firstSpaceElementChild, lastSpaceElementChild; container = iterator.container(); offset = iterator.unfilteredDomOffset(); - while(!odfUtils.isSpaceElement(container) && container.childNodes[offset]) { - container = container.childNodes[offset]; + while(!odfUtils.isSpaceElement(container) && container.childNodes.item(offset)) { + container = (container.childNodes.item(offset)); offset = 0 } if(container.nodeType === Node.TEXT_NODE) { - container = container.parentNode + container = (container.parentNode) } if(odfUtils.isDowngradableSpaceElement(container)) { firstSpaceElementChild = container.firstChild; @@ -11286,50 +9925,46 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { this.getParagraphElement = getParagraphElement; this.getParagraphStyleAttributes = getParagraphStyleAttributes; this.getTextNodeAtStep = getTextNodeAtStep; + function paragraphOrRoot(container, offset, root) { + var node = container.childNodes.item(offset) || container, paragraph = getParagraphElement(node); + if(paragraph && domUtils.containsNode(root, paragraph)) { + return(paragraph) + } + return root + } 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)) { + var cursor = cursors[memberId], root = getRoot(cursor.getNode()), rootFilter = self.createRootFilter(root), subTree, startPoint, endPoint, selectedRange, cursorMoved = false; + selectedRange = cursor.getSelectedRange(); + subTree = paragraphOrRoot((selectedRange.startContainer), selectedRange.startOffset, root); + startPoint = createStepIterator((selectedRange.startContainer), selectedRange.startOffset, [filter, rootFilter], subTree); + if(!selectedRange.collapsed) { + subTree = paragraphOrRoot((selectedRange.endContainer), selectedRange.endOffset, root); + endPoint = createStepIterator((selectedRange.endContainer), selectedRange.endOffset, [filter, rootFilter], subTree) + }else { + endPoint = startPoint + } + if(!startPoint.isStep() || !endPoint.isStep()) { 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) - } + runtime.assert(startPoint.roundToClosestStep(), "No walkable step found for cursor owned by " + memberId); + selectedRange.setStart(startPoint.container(), startPoint.offset()); + runtime.assert(endPoint.roundToClosestStep(), "No walkable step found for cursor owned by " + memberId); + selectedRange.setEnd(endPoint.container(), endPoint.offset()) }else { - if(stepsSelectionLength === 0) { - cursorMoved = true; - cursor.move(0) + if(startPoint.container() === endPoint.container() && startPoint.offset() === endPoint.offset()) { + if(!selectedRange.collapsed || cursor.getAnchorNode() !== cursor.getNode()) { + cursorMoved = true; + selectedRange.setStart(startPoint.container(), startPoint.offset()); + selectedRange.collapse(true) + } } } if(cursorMoved) { - self.emit(ops.OdtDocument.signalCursorMoved, cursor) + cursor.setSelectedRange(selectedRange, cursor.hasForwardSelection()); + self.emit(ops.Document.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 @@ -11348,6 +9983,9 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { this.getOdfCanvas = function() { return odfCanvas }; + this.getCanvas = function() { + return odfCanvas + }; this.getRootNode = getRootNode; this.addMember = function(member) { runtime.assert(members[member.getMemberId()] === undefined, "This member already exists"); @@ -11362,36 +10000,36 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { this.getCursor = function(memberid) { return cursors[memberid] }; - this.getCursors = function() { + this.getMemberIds = function() { var list = [], i; for(i in cursors) { if(cursors.hasOwnProperty(i)) { - list.push(cursors[i]) + list.push(cursors[i].getMemberId()) } } return list }; this.addCursor = function(cursor) { runtime.assert(Boolean(cursor), "OdtDocument::addCursor without cursor"); - var distanceToFirstTextNode = cursor.getStepCounter().countSteps(1, filter), memberid = cursor.getMemberId(); + var memberid = cursor.getMemberId(), initialSelection = self.convertCursorToDomRange(0, 0); 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); + cursor.setSelectedRange(initialSelection, true); cursors[memberid] = cursor }; this.removeCursor = function(memberid) { var cursor = cursors[memberid]; if(cursor) { - cursor.removeFromOdtDocument(); + cursor.removeFromDocument(); delete cursors[memberid]; - self.emit(ops.OdtDocument.signalCursorRemoved, memberid); + self.emit(ops.Document.signalCursorRemoved, memberid); return true } return false }; this.moveCursor = function(memberid, position, length, selectionType) { var cursor = cursors[memberid], selectionRange = self.convertCursorToDomRange(position, length); - if(cursor && selectionRange) { + if(cursor) { cursor.setSelectedRange(selectionRange, length >= 0); cursor.setSelectionType(selectionType || ops.OdtCursor.RangeSelection) } @@ -11417,35 +10055,257 @@ ops.OdtDocument = function OdtDocument(odfCanvas) { 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) + 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.signalOperationEnd, handleOperationExecuted) + } + init() +}; +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.signalOperationStart = "operation/start"; +ops.OdtDocument.signalOperationEnd = "operation/end"; +ops.OdtDocument.signalProcessingBatchStart = "router/batchstart"; +ops.OdtDocument.signalProcessingBatchEnd = "router/batchend"; +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 + + @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, 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; + this.group = undefined; + 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 createAnnotationEnd() { + var annotationEnd; + 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) + } + parentNode.insertBefore(node, previousNode.nextSibling); + if(previousNode.length === 0) { + parentNode.removeChild(previousNode) + } + } + } + this.execute = function(document) { + var odtDocument = (document), annotation, annotationEnd, cursor = odtDocument.getCursor(memberid), selectedRange, paragraphElement, domUtils = new core.DomUtils; + doc = odtDocument.getDOMDocument(); + annotation = createAnnotationNode(odtDocument, new Date(timestamp)); + if(length) { + annotationEnd = createAnnotationEnd(); + annotation.annotationEndElement = annotationEnd; + insertNodeAtPosition(odtDocument, annotationEnd, position + length) + } + insertNodeAtPosition(odtDocument, annotation, position); + odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:length}); + if(cursor) { + selectedRange = doc.createRange(); + paragraphElement = domUtils.getElementsByTagNameNS(annotation, odf.Namespaces.textns, "p")[0]; + selectedRange.selectNodeContents(paragraphElement); + cursor.setSelectedRange(selectedRange, false); + odtDocument.emit(ops.Document.signalCursorMoved, cursor) + } + odtDocument.getOdfCanvas().addAnnotation(annotation); + odtDocument.fixCursorPositions(); + return true + }; + this.spec = function() { + return{optype:"AddAnnotation", memberid:memberid, timestamp:timestamp, position:position, length:length, name:name} + } +}; +ops.OpAddAnnotation.Spec; +ops.OpAddAnnotation.InitSpec; +/* + + 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.group = undefined; + this.execute = function(document) { + var odtDocument = (document), cursor = odtDocument.getCursor(memberid); + if(cursor) { + return false + } + cursor = new ops.OdtCursor(memberid, odtDocument); + odtDocument.addCursor(cursor); + odtDocument.emit(ops.Document.signalCursorAdded, cursor); + return true + }; + this.spec = function() { + return{optype:"AddCursor", memberid:memberid, timestamp:timestamp} + } +}; +ops.OpAddCursor.Spec; +ops.OpAddCursor.InitSpec; +/* + + 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.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.group = undefined; + this.execute = function(document) { + var odtDocument = (document), member; + if(odtDocument.getMember(memberid)) { + return false + } + member = new ops.Member(memberid, setProperties); + odtDocument.addMember(member); + odtDocument.emit(ops.Document.signalMemberAdded, member); + return true + }; + this.spec = function() { + return{optype:"AddMember", memberid:memberid, timestamp:timestamp, setProperties:setProperties} } - 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 -})(); +ops.OpAddMember.Spec; +ops.OpAddMember.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -11483,1145 +10343,452 @@ ops.OdtDocument.signalStepsRemoved = "steps/removed"; @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() { -}; -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]) - } - } - } - } - 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", 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 valueMatch(context, pattern, text) { - return pattern.nullable && /^\s+$/.test(text) || pattern.textDeriv(context, text).nullable - } - 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 - }} - } - 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 - } - } - return result - }; - function attsDeriv(context, pattern, attributes, position) { - if(pattern === notAllowed) { - return notAllowed - } - if(position >= attributes.length) { - return pattern - } - 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(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 { - p = p.textDeriv(context, childNode) - } - }else { - walker.currentNode = childNode; - p = childDeriv(context, p, walker) - } - } - walker.currentNode = element; - return p - } - 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(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 { - result = {hash:"anyName", contains:function() { - return true - }} - } - } - 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] - } - } - 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 - } - 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.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) - } - }; - 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 - } - } - 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 err - } - 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 validateNonEmptyPattern(elementdef, walker, element, data) - } - 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 - } - if(att === undefined) { - return[new RelaxNGParseError("Attribute not found: " + elementdef.names, element)] - } - return validatePattern(elementdef.e[1], walker, element, att) - } - 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; - } - 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 - } - if(!node) { - return[new RelaxNGParseError("Missing element " + elementdef.names)] - } - 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(walker.parentNode() !== node) { - return[new RelaxNGParseError("Implementation error.")] - } - }else { - error = validateTop(elementdef.e[1], walker, node) - } - node = walker.nextSibling(); - return error - } - 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 - } - return null - } - err = validatePattern(elementdef.e[0], walker, element, data); - if(err) { - walker.currentNode = node; - err = validateNonEmptyPattern(elementdef.e[1], walker, element, data) - } - 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)] - } - } - return null - } - todo = 0; - for(i = 0;i < l;i += 1) { - if(n[i] !== true) { - todo += 1 - } - } - } - return null - } - function validateGroup(elementdef, walker, element) { - if(elementdef.e.length !== 2) { - throw"Group with wrong # of members: " + elementdef.e.length; - } - 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(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 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 - } -}; -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 - } - 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 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) - } - 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 - } - } - 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 - } - } +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.group = undefined; + this.execute = function(document) { + var odtDocument = (document), odfContainer = odtDocument.getOdfCanvas().odfContainer(), formatting = odtDocument.getFormatting(), dom = odtDocument.getDOMDocument(), styleNode = dom.createElementNS(stylens, "style:style"); + if(!styleNode) { + return false } - 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" + if(setProperties) { + formatting.updateStyle(styleNode, setProperties) } - 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} - } - span.style.height = domUtils.adaptRangeDifferenceToZoomLevel(selectionRect.height, zoomLevel) + "px"; - span.style.top = domUtils.adaptRangeDifferenceToZoomLevel(selectionRect.top - caretRect.top, zoomLevel) + "px" + styleNode.setAttributeNS(stylens, "style:family", styleFamily); + styleNode.setAttributeNS(stylens, "style:name", styleName); + if(isAutomaticStyle) { + odfContainer.rootElement.automaticStyles.appendChild(styleNode) }else { - span.style.height = DEFAULT_CARET_HEIGHT; - span.style.top = DEFAULT_CARET_TOP + odfContainer.rootElement.styles.appendChild(styleNode) } - } - this.handleUpdate = handleUpdate; - this.refreshCursorBlinking = function() { - if(blinkOnRangeSelect || cursor.getSelectedRange().collapsed) { - shouldBlink = true; - blink(true) - }else { - shouldBlink = false; - span.style.opacity = "0" + odtDocument.getOdfCanvas().refreshCSS(); + if(!isAutomaticStyle) { + odtDocument.emit(ops.OdtDocument.signalCommonStyleCreated, {name:styleName, family:styleFamily}) } + return true }; - 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.spec = function() { + return{optype:"AddStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, styleFamily:styleFamily, isAutomaticStyle:isAutomaticStyle, setProperties:setProperties} + } +}; +ops.OpAddStyle.Spec; +ops.OpAddStyle.InitSpec; +/* + + 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.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 } - }; - 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 + } + 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 } } - if(caretRect.left < canvasContainerRect.left) { - canvasContainerElement.scrollLeft -= canvasContainerRect.left - caretRect.left - }else { - if(caretRect.right > canvasContainerRect.right) { - canvasContainerElement.scrollLeft += caretRect.right - canvasContainerRect.right - } + styleElements.forEach(getStyleNames); + return styleNames + } + this.generateStyleName = function() { + if(styleNameGenerator === null) { + styleNameGenerator = new NameGenerator("auto" + memberIdHash + "_", function() { + return getAllStyleNames() + }) } - handleUpdate() + return styleNameGenerator.generateName() }; - this.destroy = function(callback) { - avatar.destroy(function(err) { - if(err) { - callback(err) - }else { - cursorNode.removeChild(span); - callback() - } - }) + 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() }; - 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 window = (runtime.getWindow()), bindToDirectHandler = {"beforecut":true, "beforepaste":true}, bindToWindow, eventTrap; - function getCanvasElement() { - return odtDocument.getOdfCanvas().getElement() - } - 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) - } + 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() } - function WindowScrollState(window) { - var x = window.scrollX, y = window.scrollY; - this.restore = function() { - if(window.scrollX !== x || window.scrollY !== y) { - window.scrollTo(x, y) +}; +/* + + 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.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) { + var cachedAppliedStyles = {}; + function compare(expected, actual) { + if(typeof expected === "object" && typeof actual === "object") { + return Object.keys(expected).every(function(key) { + return compare(expected[key], actual[key]) + }) } + return expected === actual } - } - 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 - } + this.isStyleApplied = function(textNode) { + var appliedStyle = formatting.getAppliedStylesForElement(textNode, cachedAppliedStyles); + return compare(info, appliedStyle) } } - function listenEvent(eventTarget, eventType, eventHandler) { - var onVariant = "on" + eventType, bound = false; - if(eventTarget.attachEvent) { - bound = eventTarget.attachEvent(onVariant, eventHandler) + function StyleManager(info) { + var createdStyles = {}; + function createDirectFormat(existingStyleName, document) { + var derivedStyleInfo, derivedStyleNode; + derivedStyleInfo = existingStyleName ? formatting.createDerivedStyleObject(existingStyleName, "text", info) : info; + derivedStyleNode = document.createElementNS(stylens, "style:style"); + formatting.updateStyle(derivedStyleNode, derivedStyleInfo); + derivedStyleNode.setAttributeNS(stylens, "style:name", objectNameGenerator.generateStyleName()); + derivedStyleNode.setAttributeNS(stylens, "style:family", "text"); + derivedStyleNode.setAttributeNS(webodfns, "scope", "document-content"); + automaticStyles.appendChild(derivedStyleNode); + return derivedStyleNode } - if(!bound && eventTarget.addEventListener) { - eventTarget.addEventListener(eventType, eventHandler, false); - bound = true + function getDirectStyle(existingStyleName, document) { + existingStyleName = existingStyleName || ""; + if(!createdStyles.hasOwnProperty(existingStyleName)) { + createdStyles[existingStyleName] = createDirectFormat(existingStyleName, document) + } + return createdStyles[existingStyleName].getAttributeNS(stylens, "name") } - if((!bound || bindToDirectHandler[eventType]) && eventTarget.hasOwnProperty(onVariant)) { - eventTarget[onVariant] = eventHandler + this.applyStyleToContainer = function(container) { + var name = getDirectStyle(container.getAttributeNS(textns, "style-name"), container.ownerDocument); + container.setAttributeNS(textns, "text:style-name", name) } } - 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 isTextSpan(node) { + return node.localName === "span" && node.namespaceURI === textns } - this.subscribe = function(eventName, handler) { - var delegate = bindToWindow[eventName], canvasElement = getCanvasElement(); - if(delegate) { - delegate.handlers.push(handler); - if(!delegate.isSubscribed) { - delegate.isSubscribed = true; - listenEvent((window), eventName, delegate.handleEvent); - listenEvent(canvasElement, eventName, delegate.handleEvent); - listenEvent(eventTrap, eventName, delegate.handleEvent) - } + function moveToNewSpan(startNode, limits) { + var document = startNode.ownerDocument, originalContainer = (startNode.parentNode), styledContainer, trailingContainer, moveTrailing, node, nextNode, loopGuard = new core.LoopWatchDog(1E4), styledNodes = []; + if(!isTextSpan(originalContainer)) { + styledContainer = document.createElementNS(textns, "text:span"); + originalContainer.insertBefore(styledContainer, startNode); + moveTrailing = false }else { - listenEvent(canvasElement, eventName, handler) - } - }; - this.unsubscribe = function(eventName, handler) { - var delegate = bindToWindow[eventName], handlerIndex = delegate && delegate.handlers.indexOf(handler), canvasElement = getCanvasElement(); - if(delegate) { - if(handlerIndex !== -1) { - delegate.handlers.splice(handlerIndex, 1) + if(startNode.previousSibling && !domUtils.rangeContainsNode(limits, (originalContainer.firstChild))) { + styledContainer = originalContainer.cloneNode(false); + originalContainer.parentNode.insertBefore(styledContainer, originalContainer.nextSibling); + moveTrailing = true + }else { + styledContainer = originalContainer; + moveTrailing = true } - }else { - removeEvent(canvasElement, eventName, handler) } - }; - function hasFocus() { - return odtDocument.getDOM().activeElement === getCanvasElement() - } - function findScrollableParent(element) { - while(element && (!element.scrollTop && !element.scrollLeft)) { - element = (element.parentNode) - } - if(element) { - return new ElementScrollState(element) + styledNodes.push(startNode); + node = startNode.nextSibling; + while(node && domUtils.rangeContainsNode(limits, node)) { + loopGuard.check(); + styledNodes.push(node); + node = node.nextSibling } - return new WindowScrollState(window) - } - this.focus = function() { - var scrollParent, canvasElement = getCanvasElement(), selection = window.getSelection(); - if(!hasFocus()) { - scrollParent = findScrollableParent(canvasElement); - canvasElement.focus(); - if(scrollParent) { - scrollParent.restore() + styledNodes.forEach(function(n) { + if(n.parentNode !== styledContainer) { + styledContainer.appendChild(n) + } + }); + if(node && moveTrailing) { + trailingContainer = styledContainer.cloneNode(false); + styledContainer.parentNode.insertBefore(trailingContainer, styledContainer.nextSibling); + while(node) { + loopGuard.check(); + nextNode = node.nextSibling; + trailingContainer.appendChild(node); + node = nextNode } } - if(selection && selection.extend) { - if(eventTrap.parentNode !== canvasElement) { - canvasElement.appendChild(eventTrap) + return(styledContainer) + } + this.applyStyle = function(textNodes, limits, info) { + var textPropsOnly = {}, isStyled, container, styleCache, styleLookup; + runtime.assert(info && info.hasOwnProperty(textProperties), "applyStyle without any text properties"); + textPropsOnly[textProperties] = info[textProperties]; + styleCache = new StyleManager(textPropsOnly); + styleLookup = new StyleLookup(textPropsOnly); + function apply(n) { + isStyled = styleLookup.isStyleApplied(n); + if(isStyled === false) { + container = moveToNewSpan(n, limits); + styleCache.applyStyleToContainer(container) } - selection.collapse(eventTrap.firstChild, 0); - selection.extend(eventTrap, eventTrap.childNodes.length) } - }; - function init() { - var canvasElement = getCanvasElement(), doc = canvasElement.ownerDocument; - runtime.assert(Boolean(window), "EventManager requires a window object to operate correctly"); - bindToWindow = {"mousedown":new EventDelegate, "mouseup":new EventDelegate, "focus":new EventDelegate}; - eventTrap = doc.createElement("div"); - eventTrap.id = "eventTrap"; - eventTrap.setAttribute("contenteditable", "true"); - eventTrap.style.position = "absolute"; - eventTrap.style.left = "-10000px"; - eventTrap.appendChild(doc.createTextNode("dummy content")); - canvasElement.appendChild(eventTrap) + textNodes.forEach(apply) } - 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 +/* + + 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.OpApplyDirectStyling = function OpApplyDirectStyling() { + var memberid, timestamp, position, length, setProperties, odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = parseInt(data.position, 10); + length = parseInt(data.length, 10); + setProperties = data.setProperties }; - this.hasForwardSelection = function() { - return forwardSelection + this.isEdit = true; + this.group = undefined; + function applyStyle(odtDocument, range, info) { + var odfCanvas = odtDocument.getOdfCanvas(), odfContainer = odfCanvas.odfContainer(), nextTextNodes = domUtils.splitBoundaries(range), textNodes = odfUtils.getTextNodes(range, false), limits, textStyles; + limits = {startContainer:range.startContainer, startOffset:range.startOffset, endContainer:range.endContainer, endOffset:range.endOffset}; + textStyles = new odf.TextStyleApplicator(new odf.ObjectNameGenerator((odfContainer), memberid), odtDocument.getFormatting(), odfContainer.rootElement.automaticStyles); + textStyles.applyStyle(textNodes, limits, info); + nextTextNodes.forEach(domUtils.normalizeTextNodes) + } + this.execute = function(document) { + var odtDocument = (document), range = odtDocument.convertCursorToDomRange(position, length), impactedParagraphs = odfUtils.getParagraphElements(range); + applyStyle(odtDocument, 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.getOdtDocument = function() { - return odtDocument + this.spec = function() { + return{optype:"ApplyDirectStyling", memberid:memberid, timestamp:timestamp, position:position, length:length, setProperties:setProperties} + } +}; +ops.OpApplyDirectStyling.Spec; +ops.OpApplyDirectStyling.InitSpec; +/* + + 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.OpApplyHyperlink = function OpApplyHyperlink() { + var memberid, timestamp, position, length, hyperlink, domUtils = new core.DomUtils, odfUtils = new odf.OdfUtils; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = data.position; + length = data.length; + hyperlink = data.hyperlink }; - this.getSelectionType = function() { - return ops.OdtCursor.RangeSelection + this.isEdit = true; + this.group = undefined; + function createHyperlink(document, hyperlink) { + var node = document.createElementNS(odf.Namespaces.textns, "text:a"); + node.setAttributeNS(odf.Namespaces.xlinkns, "xlink:type", "simple"); + node.setAttributeNS(odf.Namespaces.xlinkns, "xlink:href", hyperlink); + return node + } + function isPartOfLink(node) { + while(node) { + if(odfUtils.isHyperlink(node)) { + return true + } + node = node.parentNode + } + return false + } + this.execute = function(document) { + var odtDocument = (document), ownerDocument = odtDocument.getDOMDocument(), range = odtDocument.convertCursorToDomRange(position, length), boundaryNodes = domUtils.splitBoundaries(range), modifiedParagraphs = [], textNodes = odfUtils.getTextNodes(range, false); + if(textNodes.length === 0) { + return false + } + textNodes.forEach(function(node) { + var linkNode, paragraph = odfUtils.getParagraphElement(node); + runtime.assert(isPartOfLink(node) === false, "The given range should not contain any link."); + linkNode = createHyperlink(ownerDocument, hyperlink); + node.parentNode.insertBefore(linkNode, node); + linkNode.appendChild(node); + if(modifiedParagraphs.indexOf(paragraph) === -1) { + modifiedParagraphs.push(paragraph) + } + }); + boundaryNodes.forEach(domUtils.normalizeTextNodes); + range.detach(); + odtDocument.getOdfCanvas().refreshSize(); + odtDocument.getOdfCanvas().rerenderAnnotations(); + modifiedParagraphs.forEach(function(paragraph) { + odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:paragraph, memberId:memberid, timeStamp:timestamp}) + }); + return true }; - function init() { - selectedRange.setStart(odtDocument.getRootNode(), 0) + this.spec = function() { + return{optype:"ApplyHyperlink", memberid:memberid, timestamp:timestamp, position:position, length:length, hyperlink:hyperlink} } - init() }; -gui.ShadowCursor.ShadowCursorMemberId = ""; -(function() { - return gui.ShadowCursor -})(); +ops.OpApplyHyperlink.Spec; +ops.OpApplyHyperlink.InitSpec; /* - 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 @@ -12656,36 +10823,60 @@ gui.ShadowCursor.ShadowCursorMemberId = ""; @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) { +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; + this.group = undefined; + 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(document) { + var odtDocument = (document), 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.getDOMDocument()); + 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} + } }; -gui.UndoManager.signalUndoStackChanged = "undoStackChanged"; -gui.UndoManager.signalUndoStateCreated = "undoStateCreated"; -gui.UndoManager.signalUndoStateModified = "undoStateModified"; -(function() { - return gui.UndoManager -})(); +ops.OpInsertImage.Spec; +ops.OpInsertImage.InitSpec; /* Copyright (C) 2013 KO GmbH @@ -12723,58 +10914,109 @@ 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 - } - 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 - } - } - 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 +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; + this.group = undefined; + 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(isSameDirectionOfTravel(recentEditOps, thisOp)) { - return true + } + 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 false + return rowStyles[column] } - function isPartOfOperationSet(operation, lastOperations) { - if(isEditOperation(operation)) { - if(lastOperations.length === 0) { - return true + 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) } - return isEditOperation(lastOperations[lastOperations.length - 1]) && isContinuousOperation(lastOperations.filter(isEditOperation), operation) + tableNode.appendChild(row) } - return true + return tableNode + } + this.execute = function(document) { + var odtDocument = (document), domPosition = odtDocument.getTextNodeAtStep(position), rootNode = odtDocument.getRootNode(), previousSibling, tableNode; + if(domPosition) { + tableNode = createTableNode(odtDocument.getDOMDocument()); + 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} } - this.isPartOfOperationSet = isPartOfOperationSet }; +ops.OpInsertTable.Spec; +ops.OpInsertTable.InitSpec; /* - 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 @@ -12809,51 +11051,96 @@ gui.UndoStateRules = function UndoStateRules() { @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}) +ops.OpInsertText = function OpInsertText() { + var space = " ", tab = "\t", memberid, timestamp, position, moveCursor, text; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp; + position = data.position; + text = data.text; + moveCursor = data.moveCursor === "true" || data.moveCursor === true + }; + this.isEdit = true; + this.group = undefined; + 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(document) { + var odtDocument = (document), domPosition, previousNode, parentElement, nextNode = null, ownerDocument = odtDocument.getDOMDocument(), paragraphElement, textns = "urn:oasis:names:tc:opendocument:xmlns:text:1.0", toInsertIndex = 0, spaceTag, spaceElement, cursor = odtDocument.getCursor(memberid), i; + function insertTextNode(toInsertText) { + parentElement.insertBefore(ownerDocument.createTextNode(toInsertText), nextNode) + } + odtDocument.upgradeWhitespacesAtPosition(position); + domPosition = odtDocument.getTextNodeAtStep(position); + 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(cursor && moveCursor) { + odtDocument.moveCursor(memberid, position + text.length, 0); + odtDocument.emit(ops.Document.signalCursorMoved, cursor) + } + 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 } - 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() + return false }; - function init() { - var editInfons = "urn:webodf:names:editinfo", dom = odtDocument.getDOM(); - editInfoNode = dom.createElementNS(editInfons, "editinfo"); - container.insertBefore(editInfoNode, container.firstChild) + this.spec = function() { + return{optype:"InsertText", memberid:memberid, timestamp:timestamp, position:position, text:text, moveCursor:moveCursor} } - init() }; +ops.OpInsertText.Spec; +ops.OpInsertText.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -12891,87 +11178,34 @@ ops.EditInfo = function EditInfo(container, odtDocument) { @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; +ops.OpMoveCursor = function OpMoveCursor() { + var memberid, timestamp, position, length, selectionType; 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 + timestamp = data.timestamp; + position = data.position; + length = data.length || 0; + selectionType = data.selectionType || ops.OdtCursor.RangeSelection }; - 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 createAnnotationEnd() { - var annotationEnd; - 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) - } - parentNode.insertBefore(node, previousNode.nextSibling); - if(previousNode.length === 0) { - parentNode.removeChild(previousNode) - } - } - } - 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) { + this.isEdit = false; + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document), cursor = odtDocument.getCursor(memberid), selectedRange; + if(!cursor) { return false } - if(length) { - annotation.end = createAnnotationEnd(); - if(!annotation.end) { - return false - } - insertNodeAtPosition(odtDocument, annotation.end, position + length) - } - 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) - } - odtDocument.getOdfCanvas().addAnnotation(annotation); - odtDocument.fixCursorPositions(); + selectedRange = odtDocument.convertCursorToDomRange(position, length); + cursor.setSelectedRange(selectedRange, length >= 0); + cursor.setSelectionType(selectionType); + odtDocument.emit(ops.Document.signalCursorMoved, cursor); return true }; this.spec = function() { - return{optype:"AddAnnotation", memberid:memberid, timestamp:timestamp, position:position, length:length, name:name} + return{optype:"MoveCursor", memberid:memberid, timestamp:timestamp, position:position, length:length, selectionType:selectionType} } }; +ops.OpMoveCursor.Spec; +ops.OpMoveCursor.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -13009,73 +11243,48 @@ ops.OpAddAnnotation = function OpAddAnnotation() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpAddCursor = function OpAddCursor() { - var memberid, timestamp; +ops.OpRemoveAnnotation = function OpRemoveAnnotation() { + var memberid, timestamp, position, length, domUtils; this.init = function(data) { memberid = data.memberid; - timestamp = data.timestamp + timestamp = data.timestamp; + position = parseInt(data.position, 10); + length = parseInt(data.length, 10); + domUtils = new core.DomUtils }; - this.isEdit = false; - this.execute = function(odtDocument) { - var cursor = odtDocument.getCursor(memberid); - if(cursor) { - return false + this.isEdit = true; + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document), iterator = odtDocument.getIteratorAtPosition(position), container = iterator.container(), annotationNode, annotationEnd; + while(!(container.namespaceURI === odf.Namespaces.officens && container.localName === "annotation")) { + container = container.parentNode } - 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) 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)) { + if(container === null) { return false } - var member = new ops.Member(memberid, setProperties); - odtDocument.addMember(member); - odtDocument.emit(ops.OdtDocument.signalMemberAdded, member); + annotationNode = (container); + annotationEnd = annotationNode.annotationEndElement; + odtDocument.getOdfCanvas().forgetAnnotations(); + function insert(node) { + (annotationNode).parentNode.insertBefore(node, annotationNode) + } + domUtils.getElementsByTagNameNS(annotationNode, "urn:webodf:names:cursor", "cursor").forEach(insert); + domUtils.getElementsByTagNameNS(annotationNode, "urn:webodf:names:cursor", "anchor").forEach(insert); + annotationNode.parentNode.removeChild(annotationNode); + if(annotationEnd) { + annotationEnd.parentNode.removeChild(annotationEnd) + } + 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:"AddMember", memberid:memberid, timestamp:timestamp, setProperties:setProperties} + return{optype:"RemoveAnnotation", memberid:memberid, timestamp:timestamp, position:position, length:length} } }; +ops.OpRemoveAnnotation.Spec; +ops.OpRemoveAnnotation.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -13113,44 +11322,26 @@ ops.OpAddMember = function OpAddMember() { @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; +ops.OpRemoveBlob = function OpRemoveBlob() { + var memberid, timestamp, filename; 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 + filename = data.filename }; 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(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 { - odfContainer.rootElement.styles.appendChild(styleNode) - } - odtDocument.getOdfCanvas().refreshCSS(); - if(!isAutomaticStyle) { - odtDocument.emit(ops.OdtDocument.signalCommonStyleCreated, {name:styleName, family:styleFamily}) - } + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document); + odtDocument.getOdfCanvas().odfContainer().removeBlob(filename); return true }; this.spec = function() { - return{optype:"AddStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, styleFamily:styleFamily, isAutomaticStyle:isAutomaticStyle, setProperties:setProperties} + return{optype:"RemoveBlob", memberid:memberid, timestamp:timestamp, filename:filename} } }; -ops.OpAddStyle.Spec; +ops.OpRemoveBlob.Spec; +ops.OpRemoveBlob.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -13188,43 +11379,27 @@ ops.OpAddStyle.Spec; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils"); -runtime.loadClass("odf.OdfUtils"); -runtime.loadClass("odf.TextStyleApplicator"); -ops.OpApplyDirectStyling = function OpApplyDirectStyling() { - var memberid, timestamp, position, length, setProperties, odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils; - 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 applyStyle(odtDocument, range, info) { - var odfCanvas = odtDocument.getOdfCanvas(), odfContainer = odfCanvas.odfContainer(), nextTextNodes = domUtils.splitBoundaries(range), textNodes = odfUtils.getTextNodes(range, false), limits, textStyles; - limits = {startContainer:range.startContainer, startOffset:range.startOffset, endContainer:range.endContainer, endOffset:range.endOffset}; - textStyles = new odf.TextStyleApplicator(new odf.ObjectNameGenerator((odfContainer), memberid), odtDocument.getFormatting(), odfContainer.rootElement.automaticStyles); - textStyles.applyStyle(textNodes, limits, info); - nextTextNodes.forEach(domUtils.normalizeTextNodes) - } - this.execute = function(odtDocument) { - var range = odtDocument.convertCursorToDomRange(position, length), impactedParagraphs = odfUtils.getImpactedParagraphs(range); - applyStyle(odtDocument, 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(); +ops.OpRemoveCursor = function OpRemoveCursor() { + var memberid, timestamp; + this.init = function(data) { + memberid = data.memberid; + timestamp = data.timestamp + }; + this.isEdit = false; + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document); + if(!odtDocument.removeCursor(memberid)) { + return false + } return true }; this.spec = function() { - return{optype:"ApplyDirectStyling", memberid:memberid, timestamp:timestamp, position:position, length:length, setProperties:setProperties} + return{optype:"RemoveCursor", memberid:memberid, timestamp:timestamp} } }; -ops.OpApplyDirectStyling.Spec; +ops.OpRemoveCursor.Spec; +ops.OpRemoveCursor.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -13262,60 +11437,82 @@ ops.OpApplyDirectStyling.Spec; @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; +ops.OpRemoveHyperlink = function OpRemoveHyperlink() { + var memberid, timestamp, position, length, domUtils = new core.DomUtils, odfUtils = new odf.OdfUtils; 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 + length = data.length }; 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.group = undefined; + this.execute = function(document) { + var odtDocument = (document), range = odtDocument.convertCursorToDomRange(position, length), links = odfUtils.getHyperlinkElements(range), node; + runtime.assert(links.length === 1, "The given range should only contain a single link."); + node = domUtils.mergeIntoParent((links[0])); + range.detach(); + odtDocument.getOdfCanvas().refreshSize(); + odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:odfUtils.getParagraphElement(node), memberId:memberid, timeStamp:timestamp}); + odtDocument.getOdfCanvas().rerenderAnnotations(); + return true + }; + this.spec = function() { + return{optype:"RemoveHyperlink", memberid:memberid, timestamp:timestamp, position:position, length:length} } - this.execute = function(odtDocument) { - var odfCanvas = odtDocument.getOdfCanvas(), domPosition = odtDocument.getTextNodeAtStep(position, memberid), textNode, refNode, paragraphElement, frameElement; - if(!domPosition) { +}; +ops.OpRemoveHyperlink.Spec; +ops.OpRemoveHyperlink.InitSpec; +/* + + 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.OpRemoveMember = function OpRemoveMember() { + var memberid, timestamp; + this.init = function(data) { + memberid = data.memberid; + timestamp = parseInt(data.timestamp, 10) + }; + this.isEdit = false; + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document); + if(!odtDocument.getMember(memberid)) { 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(); + odtDocument.removeMember(memberid); + odtDocument.emit(ops.Document.signalMemberRemoved, memberid); return true }; this.spec = function() { - return{optype:"InsertImage", memberid:memberid, timestamp:timestamp, filename:filename, position:position, frameWidth:frameWidth, frameHeight:frameHeight, frameStyleName:frameStyleName, frameName:frameName} + return{optype:"RemoveMember", memberid:memberid, timestamp:timestamp} } }; +ops.OpRemoveMember.Spec; +ops.OpRemoveMember.InitSpec; /* - 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 @@ -13350,103 +11547,32 @@ ops.OpInsertImage = function OpInsertImage() { @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"; +ops.OpRemoveStyle = function OpRemoveStyle() { + var memberid, timestamp, styleName, styleFamily; 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 + styleName = data.styleName; + styleFamily = data.styleFamily }; 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 + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document), styleNode = odtDocument.getStyleElement(styleName, styleFamily); + if(!styleNode) { + return false } - return false + styleNode.parentNode.removeChild(styleNode); + odtDocument.getOdfCanvas().refreshCSS(); + odtDocument.emit(ops.OdtDocument.signalCommonStyleDeleted, {name:styleName, family:styleFamily}); + return true }; this.spec = function() { - return{optype:"InsertTable", memberid:memberid, timestamp:timestamp, position:position, initialRows:initialRows, initialColumns:initialColumns, tableName:tableName, tableStyleName:tableStyleName, tableColumnStyleName:tableColumnStyleName, tableCellStyleMatrix:tableCellStyleMatrix} + return{optype:"RemoveStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, styleFamily:styleFamily} } }; +ops.OpRemoveStyle.Spec; +ops.OpRemoveStyle.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -13484,94 +11610,134 @@ ops.OpInsertTable = function OpInsertTable() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpInsertText = function OpInsertText() { - var space = " ", tab = "\t", memberid, timestamp, position, text, moveCursor; +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 = data.position; - text = data.text; - moveCursor = data.moveCursor === "true" || data.moveCursor === true + 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 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, cursor = odtDocument.getCursor(memberid), i; - function insertTextNode(toInsertText) { - parentElement.insertBefore(ownerDocument.createTextNode(toInsertText), nextNode) + this.group = undefined; + function CollapsingRules(rootNode) { + function isOdfNode(node) { + return odfNodeNamespaceMap.hasOwnProperty(node.namespaceURI) } - odtDocument.upgradeWhitespacesAtPosition(position); - domPosition = odtDocument.getTextNodeAtStep(position); - 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) + 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(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 } - if(toInsertIndex === 0) { - previousNode.insertData(domPosition.offset, text) + return true + } + this.isEmpty = isEmpty; + function isCollapsibleContainer(node) { + return!odfUtils.isParagraph(node) && (node !== rootNode && isEmpty(node)) + } + function mergeChildrenIntoParent(targetNode) { + var parent; + if(targetNode.nodeType === Node.TEXT_NODE) { + parent = targetNode.parentNode; + parent.removeChild(targetNode) }else { - if(toInsertIndex < text.length) { - insertTextNode(text.substring(toInsertIndex)) - } + parent = domUtils.removeUnwantedNodes(targetNode, shouldRemove) } - triggerLayoutInWebkit(previousNode); - if(previousNode.length === 0) { - previousNode.parentNode.removeChild(previousNode) + if(parent && isCollapsibleContainer(parent)) { + return mergeChildrenIntoParent(parent) } - odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:text.length}); - if(cursor && moveCursor) { - odtDocument.moveCursor(memberid, position + text.length, 0); - odtDocument.emit(ops.OdtDocument.signalCursorMoved, cursor) + return parent + } + this.mergeChildrenIntoParent = mergeChildrenIntoParent + } + function mergeParagraphs(first, second, collapseRules) { + var child, destination = first, source = second, secondParent, insertionPoint = null; + if(collapseRules.isEmpty(first)) { + if(second.parentNode !== first.parentNode) { + secondParent = second.parentNode; + first.parentNode.insertBefore(second, first.nextSibling) } - if(position > 0) { - if(position > 1) { - odtDocument.downgradeWhitespacesAtPosition(position - 2) - } - odtDocument.downgradeWhitespacesAtPosition(position - 1) + source = first; + destination = second; + insertionPoint = destination.getElementsByTagNameNS(editinfons, "editinfo").item(0) || destination.firstChild + } + while(source.firstChild) { + child = source.firstChild; + source.removeChild(child); + if(child.localName !== "editinfo") { + destination.insertBefore(child, insertionPoint) } - 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 + if(secondParent && collapseRules.isEmpty(secondParent)) { + collapseRules.mergeChildrenIntoParent(secondParent) + } + collapseRules.mergeChildrenIntoParent(source); + return destination + } + this.execute = function(document) { + var odtDocument = (document), 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) + }); + function merge(destination, paragraph) { + return mergeParagraphs(destination, paragraph, collapseRules) + } + destinationParagraph = paragraphs.reduce(merge); + 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.Document.signalCursorMoved, cursor) + } + odtDocument.getOdfCanvas().rerenderAnnotations(); + return true }; this.spec = function() { - return{optype:"InsertText", memberid:memberid, timestamp:timestamp, position:position, text:text, moveCursor:moveCursor} + return{optype:"RemoveText", memberid:memberid, timestamp:timestamp, position:position, length:length} } }; -ops.OpInsertText.Spec; +ops.OpRemoveText.Spec; +ops.OpRemoveText.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -13609,32 +11775,28 @@ ops.OpInsertText.Spec; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpMoveCursor = function OpMoveCursor() { - var memberid, timestamp, position, length, selectionType; +ops.OpSetBlob = function OpSetBlob() { + var memberid, timestamp, filename, mimetype, content; this.init = function(data) { memberid = data.memberid; timestamp = data.timestamp; - position = data.position; - length = data.length || 0; - selectionType = data.selectionType || ops.OdtCursor.RangeSelection + filename = data.filename; + mimetype = data.mimetype; + content = data.content }; - 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); + this.isEdit = true; + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document); + odtDocument.getOdfCanvas().odfContainer().setBlob(filename, mimetype, content); return true }; this.spec = function() { - return{optype:"MoveCursor", memberid:memberid, timestamp:timestamp, position:position, length:length, selectionType:selectionType} + return{optype:"SetBlob", memberid:memberid, timestamp:timestamp, filename:filename, mimetype:mimetype, content:content} } }; -ops.OpMoveCursor.Spec; +ops.OpSetBlob.Spec; +ops.OpSetBlob.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -13672,51 +11834,39 @@ ops.OpMoveCursor.Spec; @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; +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 = parseInt(data.position, 10); - length = parseInt(data.length, 10); - domUtils = new core.DomUtils + position = data.position; + styleName = data.styleName }; 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 - } - if(container === null) { - return false - } - 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 - } - odtDocument.getOdfCanvas().forgetAnnotations(); - cursors = domUtils.getElementsByTagNameNS(annotationNode, "urn:webodf:names:cursor", "cursor"); - while(cursors.length) { - annotationNode.parentNode.insertBefore(cursors.pop(), annotationNode) - } - annotationNode.parentNode.removeChild(annotationNode); - if(annotationEnd) { - annotationEnd.parentNode.removeChild(annotationEnd) + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document), 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 } - odtDocument.emit(ops.OdtDocument.signalStepsRemoved, {position:position > 0 ? position - 1 : position, length:length}); - odtDocument.fixCursorPositions(); - odtDocument.getOdfCanvas().refreshAnnotations(); - return true + return false }; this.spec = function() { - return{optype:"RemoveAnnotation", memberid:memberid, timestamp:timestamp, position:position, length:length} + return{optype:"SetParagraphStyle", memberid:memberid, timestamp:timestamp, position:position, styleName:styleName} } }; +ops.OpSetParagraphStyle.Spec; +ops.OpSetParagraphStyle.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -13754,77 +11904,156 @@ ops.OpRemoveAnnotation = function OpRemoveAnnotation() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpRemoveBlob = function OpRemoveBlob() { - var memberid, timestamp, filename; +ops.OpSplitParagraph = function OpSplitParagraph() { + var memberid, timestamp, position, moveCursor, odfUtils; this.init = function(data) { memberid = data.memberid; timestamp = data.timestamp; - filename = data.filename + position = data.position; + moveCursor = data.moveCursor === "true" || data.moveCursor === true; + odfUtils = new odf.OdfUtils }; this.isEdit = true; - this.execute = function(odtDocument) { - odtDocument.getOdfCanvas().odfContainer().removeBlob(filename); + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document), domPosition, paragraphNode, targetNode, node, splitNode, splitChildNode, keptChildNode, cursor = odtDocument.getCursor(memberid); + odtDocument.upgradeWhitespacesAtPosition(position); + domPosition = odtDocument.getTextNodeAtStep(position); + if(!domPosition) { + return false + } + paragraphNode = odtDocument.getParagraphElement(domPosition.textNode); + if(!paragraphNode) { + return false + } + if(odfUtils.isListItem(paragraphNode.parentNode)) { + targetNode = paragraphNode.parentNode + }else { + targetNode = paragraphNode + } + 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)) + } + } + 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 + } + if(odfUtils.isListItem(splitChildNode)) { + splitChildNode = splitChildNode.childNodes.item(0) + } + if(domPosition.textNode.length === 0) { + domPosition.textNode.parentNode.removeChild(domPosition.textNode) + } + odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:1}); + if(cursor && moveCursor) { + odtDocument.moveCursor(memberid, position + 1, 0); + odtDocument.emit(ops.Document.signalCursorMoved, cursor) + } + 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:"RemoveBlob", memberid:memberid, timestamp:timestamp, filename:filename} + return{optype:"SplitParagraph", memberid:memberid, timestamp:timestamp, position:position, moveCursor:moveCursor} } }; +ops.OpSplitParagraph.Spec; +ops.OpSplitParagraph.InitSpec; /* - 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 . - - 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 file is part of WebODF. - This license applies to this entire compilation. + 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.OpRemoveCursor = function OpRemoveCursor() { - var memberid, timestamp; +ops.OpUpdateMember = function OpUpdateMember() { + var memberid, timestamp, setProperties, removedProperties; this.init = function(data) { memberid = data.memberid; - timestamp = data.timestamp + timestamp = parseInt(data.timestamp, 10); + setProperties = data.setProperties; + removedProperties = data.removedProperties }; this.isEdit = false; - this.execute = function(odtDocument) { - if(!odtDocument.removeCursor(memberid)) { + this.group = undefined; + function updateCreators(doc) { + 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(document) { + var odtDocument = (document), member = odtDocument.getMember(memberid); + if(!member) { return false } + if(removedProperties) { + member.removeProperties(removedProperties) + } + if(setProperties) { + member.setProperties(setProperties); + if(setProperties.fullName) { + updateCreators(odtDocument) + } + } + odtDocument.emit(ops.Document.signalMemberUpdated, member); return true }; this.spec = function() { - return{optype:"RemoveCursor", memberid:memberid, timestamp:timestamp} + return{optype:"UpdateMember", memberid:memberid, timestamp:timestamp, setProperties:setProperties, removedProperties:removedProperties} } }; -ops.OpRemoveCursor.Spec; +ops.OpUpdateMember.Spec; +ops.OpUpdateMember.InitSpec; /* Copyright (C) 2013 KO GmbH @@ -13849,26 +12078,30 @@ ops.OpRemoveCursor.Spec; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("ops.Member"); -ops.OpRemoveMember = function OpRemoveMember() { - var memberid, timestamp; +ops.OpUpdateMetadata = function OpUpdateMetadata() { + var memberid, timestamp, setProperties, removedProperties; this.init = function(data) { memberid = data.memberid; - timestamp = parseInt(data.timestamp, 10) + timestamp = parseInt(data.timestamp, 10); + setProperties = data.setProperties; + removedProperties = data.removedProperties }; - this.isEdit = false; - this.execute = function(odtDocument) { - if(!odtDocument.getMember(memberid)) { - return false + this.isEdit = true; + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document), odfContainer = odtDocument.getOdfCanvas().odfContainer(), removedPropertiesArray = []; + if(removedProperties) { + removedPropertiesArray = removedProperties.attributes.split(",") } - odtDocument.removeMember(memberid); - odtDocument.emit(ops.OdtDocument.signalMemberRemoved, memberid); + odfContainer.setMetadata(setProperties, removedPropertiesArray); return true }; this.spec = function() { - return{optype:"RemoveMember", memberid:memberid, timestamp:timestamp} + return{optype:"UpdateMetadata", memberid:memberid, timestamp:timestamp, setProperties:setProperties, removedProperties:removedProperties} } }; +ops.OpUpdateMetadata.Spec; +ops.OpUpdateMetadata.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -13906,30 +12139,67 @@ ops.OpRemoveMember = function OpRemoveMember() { @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpRemoveStyle = function OpRemoveStyle() { - var memberid, timestamp, styleName, styleFamily; +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.init = function(data) { memberid = data.memberid; timestamp = data.timestamp; styleName = data.styleName; - styleFamily = data.styleFamily + setProperties = data.setProperties; + removedProperties = data.removedProperties }; this.isEdit = true; - this.execute = function(odtDocument) { - var styleNode = odtDocument.getStyleElement(styleName, styleFamily); - if(!styleNode) { - return false + this.group = undefined; + this.execute = function(document) { + var odtDocument = (document), formatting = odtDocument.getFormatting(), styleNode, object, paragraphPropertiesNode, textPropertiesNode; + if(styleName !== "") { + styleNode = odtDocument.getParagraphStyleElement(styleName) + }else { + styleNode = formatting.getDefaultStyleElement("paragraph") } - styleNode.parentNode.removeChild(styleNode); - odtDocument.getOdfCanvas().refreshCSS(); - odtDocument.emit(ops.OdtDocument.signalCommonStyleDeleted, {name:styleName, family:styleFamily}); - return true + if(styleNode) { + paragraphPropertiesNode = (styleNode.getElementsByTagNameNS(stylens, "paragraph-properties").item(0)); + textPropertiesNode = (styleNode.getElementsByTagNameNS(stylens, "text-properties").item(0)); + if(setProperties) { + formatting.updateStyle(styleNode, setProperties) + } + if(removedProperties) { + object = (removedProperties[paragraphPropertiesName]); + if(paragraphPropertiesNode && object) { + removedAttributesFromStyleNode(paragraphPropertiesNode, object.attributes); + if(paragraphPropertiesNode.attributes.length === 0) { + styleNode.removeChild(paragraphPropertiesNode) + } + } + object = (removedProperties[textPropertiesName]); + if(textPropertiesNode && object) { + removedAttributesFromStyleNode(textPropertiesNode, object.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 false }; this.spec = function() { - return{optype:"RemoveStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, styleFamily:styleFamily} + return{optype:"UpdateParagraphStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, setProperties:setProperties, removedProperties:removedProperties} } }; -ops.OpRemoveStyle.Spec; +ops.OpUpdateParagraphStyle.Spec; +ops.OpUpdateParagraphStyle.InitSpec; /* Copyright (C) 2012-2013 KO GmbH @@ -13967,138 +12237,85 @@ ops.OpRemoveStyle.Spec; @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 +ops.OperationFactory = function OperationFactory() { + var specs; + this.register = function(specName, specConstructor) { + specs[specName] = specConstructor }; - 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(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 - } - this.isEmpty = isEmpty; - function isCollapsibleContainer(node) { - return!odfUtils.isParagraph(node) && (node !== rootNode && isEmpty(node)) - } - function mergeChildrenIntoParent(targetNode) { - var parent; - if(targetNode.nodeType === Node.TEXT_NODE) { - parent = targetNode.parentNode; - parent.removeChild(targetNode) - }else { - parent = domUtils.removeUnwantedNodes(targetNode, shouldRemove) - } - if(isCollapsibleContainer(parent)) { - return mergeChildrenIntoParent(parent) - } - return parent - } - this.mergeChildrenIntoParent = mergeChildrenIntoParent - } - 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) - } - 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) - } - } - 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) + this.create = function(spec) { + var op = null, Constructor = specs[spec.optype]; + if(Constructor) { + op = new Constructor; + op.init(spec) } - odtDocument.getOdfCanvas().rerenderAnnotations(); - return true + return op }; - this.spec = function() { - return{optype:"RemoveText", memberid:memberid, timestamp:timestamp, position:position, length:length} + function init() { + specs = {AddMember:ops.OpAddMember, UpdateMember:ops.OpUpdateMember, RemoveMember:ops.OpRemoveMember, AddCursor:ops.OpAddCursor, ApplyDirectStyling:ops.OpApplyDirectStyling, SetBlob:ops.OpSetBlob, RemoveBlob:ops.OpRemoveBlob, InsertImage:ops.OpInsertImage, InsertTable:ops.OpInsertTable, InsertText:ops.OpInsertText, RemoveText:ops.OpRemoveText, SplitParagraph:ops.OpSplitParagraph, SetParagraphStyle:ops.OpSetParagraphStyle, UpdateParagraphStyle:ops.OpUpdateParagraphStyle, AddStyle:ops.OpAddStyle, + RemoveStyle:ops.OpRemoveStyle, MoveCursor:ops.OpMoveCursor, RemoveCursor:ops.OpRemoveCursor, AddAnnotation:ops.OpAddAnnotation, RemoveAnnotation:ops.OpRemoveAnnotation, UpdateMetadata:ops.OpUpdateMetadata, ApplyHyperlink:ops.OpApplyHyperlink, RemoveHyperlink:ops.OpRemoveHyperlink} } + 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.OperationRouter = function OperationRouter() { }; -ops.OpRemoveText.Spec; +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() { +}; +ops.OperationRouter.signalProcessingBatchStart = "router/batchstart"; +ops.OperationRouter.signalProcessingBatchEnd = "router/batchend"; /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2012 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -14133,22 +12350,40 @@ ops.OpRemoveText.Spec; @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 +ops.TrivialOperationRouter = function TrivialOperationRouter() { + var events = new core.EventNotifier([ops.OperationRouter.signalProcessingBatchStart, ops.OperationRouter.signalProcessingBatchEnd]), operationFactory, playbackFunction, groupIdentifier = 0; + this.setOperationFactory = function(f) { + operationFactory = f }; - this.isEdit = true; - this.execute = function(odtDocument) { - odtDocument.getOdfCanvas().odfContainer().setBlob(filename, mimetype, content); - return true + this.setPlaybackFunction = function(playback_func) { + playbackFunction = playback_func }; - this.spec = function() { - return{optype:"SetBlob", memberid:memberid, timestamp:timestamp, filename:filename, mimetype:mimetype, content:content} + this.push = function(operations) { + groupIdentifier += 1; + events.emit(ops.OperationRouter.signalProcessingBatchStart, {}); + operations.forEach(function(op) { + var timedOp, opspec = op.spec(); + opspec.timestamp = (new Date).getTime(); + timedOp = operationFactory.create(opspec); + timedOp.group = "g" + groupIdentifier; + playbackFunction(timedOp) + }); + events.emit(ops.OperationRouter.signalProcessingBatchEnd, {}) + }; + this.close = function(cb) { + cb() + }; + this.subscribe = function(eventId, cb) { + events.subscribe(eventId, cb) + }; + this.unsubscribe = function(eventId, cb) { + events.unsubscribe(eventId, cb) + }; + this.hasLocalUnsyncedOps = function() { + return false + }; + this.hasSessionHostConnection = function() { + return true } }; /* @@ -14188,220 +12423,515 @@ ops.OpSetBlob = function OpSetBlob() { @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 +ops.Session = function Session(odfCanvas) { + var self = this, operationFactory = new ops.OperationFactory, odtDocument = new ops.OdtDocument(odfCanvas), operationRouter = null; + function forwardBatchStart(args) { + odtDocument.emit(ops.OdtDocument.signalProcessingBatchStart, args) + } + function forwardBatchEnd(args) { + odtDocument.emit(ops.OdtDocument.signalProcessingBatchEnd, args) + } + this.setOperationFactory = function(opFactory) { + operationFactory = opFactory; + if(operationRouter) { + operationRouter.setOperationFactory(operationFactory) + } }; - 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) + this.setOperationRouter = function(opRouter) { + if(operationRouter) { + operationRouter.unsubscribe(ops.OperationRouter.signalProcessingBatchStart, forwardBatchStart); + operationRouter.unsubscribe(ops.OperationRouter.signalProcessingBatchEnd, forwardBatchEnd) + } + operationRouter = opRouter; + operationRouter.subscribe(ops.OperationRouter.signalProcessingBatchStart, forwardBatchStart); + operationRouter.subscribe(ops.OperationRouter.signalProcessingBatchEnd, forwardBatchEnd); + opRouter.setPlaybackFunction(function(op) { + odtDocument.emit(ops.OdtDocument.signalOperationStart, op); + if(op.execute(odtDocument)) { + odtDocument.emit(ops.OdtDocument.signalOperationEnd, op); + return true + } + return false + }); + 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) }else { - paragraphNode.removeAttributeNS(textns, "style-name") + odtDocument.close(callback) } - odtDocument.getOdfCanvas().refreshSize(); - odtDocument.emit(ops.OdtDocument.signalParagraphChanged, {paragraphElement:paragraphNode, timeStamp:timestamp, memberId:memberid}); - odtDocument.getOdfCanvas().rerenderAnnotations(); - return true + }) + }; + this.destroy = function(callback) { + odtDocument.destroy(callback) + }; + function init() { + self.setOperationRouter(new ops.TrivialOperationRouter) + } + init() +}; +/* + + 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/ +*/ +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 = false; + if(cursorNode) { + newIsAnnotatable = !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.Document.signalCursorAdded, onCursorAdded); + odtDocument.unsubscribe(ops.Document.signalCursorRemoved, onCursorRemoved); + odtDocument.unsubscribe(ops.Document.signalCursorMoved, onCursorMoved); + callback() + }; + function init() { + odtDocument.subscribe(ops.Document.signalCursorAdded, onCursorAdded); + odtDocument.subscribe(ops.Document.signalCursorRemoved, onCursorRemoved); + odtDocument.subscribe(ops.Document.signalCursorMoved, onCursorMoved); + updatedCachedValues() + } + init() +}; +gui.AnnotationController.annotatableChanged = "annotatable/changed"; +(function() { + return gui.AnnotationController +})(); +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 + }; + 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) { + if(isFocussed) { + handle.classList.add("active") + }else { + handle.classList.remove("active") } - return false }; - this.spec = function() { - return{optype:"SetParagraphStyle", memberid:memberid, timestamp:timestamp, position:position, styleName:styleName} + 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; + handle.className = "handle"; + parentElement.appendChild(handle) } + init() }; -ops.OpSetParagraphStyle.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.OpSplitParagraph = function OpSplitParagraph() { - var memberid, timestamp, position, moveCursor, odfUtils; - this.init = function(data) { - memberid = data.memberid; - timestamp = data.timestamp; - position = data.position; - moveCursor = data.moveCursor === "true" || data.moveCursor === true; - odfUtils = new odf.OdfUtils - }; - this.isEdit = true; - this.execute = function(odtDocument) { - var domPosition, paragraphNode, targetNode, node, splitNode, splitChildNode, keptChildNode, cursor = odtDocument.getCursor(memberid); - odtDocument.upgradeWhitespacesAtPosition(position); - domPosition = odtDocument.getTextNodeAtStep(position); - if(!domPosition) { - return false +gui.Caret = function Caret(cursor, avatarInitiallyVisible, blinkOnRangeSelect) { + var MIN_CARET_HEIGHT_PX = 8, DEFAULT_CARET_TOP = "5%", DEFAULT_CARET_HEIGHT = "1em", BLINK_PERIOD_MS = 500, span, avatar, cursorNode, overlayElement, domUtils = new core.DomUtils, async = new core.Async, redrawTask, blinkTask, shouldResetBlink = false, shouldCheckCaretVisibility = false, shouldUpdateCaretSize = false, state = {isFocused:false, isShown:true, visibility:"hidden"}, lastState = {isFocused:!state.isFocused, isShown:!state.isShown, visibility:"hidden"}; + function blinkCaret() { + span.style.opacity = span.style.opacity === "0" ? "1" : "0"; + blinkTask.trigger() + } + 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) } - paragraphNode = odtDocument.getParagraphElement(domPosition.textNode); - if(!paragraphNode) { - return false + 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 + } } - if(odfUtils.isListItem(paragraphNode.parentNode)) { - targetNode = paragraphNode.parentNode - }else { - targetNode = paragraphNode + 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(domPosition.offset === 0) { - keptChildNode = domPosition.textNode.previousSibling; - splitChildNode = null + return selectionRectangle + } + function updateCaretHeightAndPosition() { + var selectionRect = getSelectionRect(), canvas = (cursor.getDocument().getCanvas()), zoomLevel = canvas.getZoomLevel(), rootRect = domUtils.getBoundingClientRect(canvas.getSizer()), caretRect, caretStyle; + 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} + } + span.style.height = domUtils.adaptRangeDifferenceToZoomLevel(selectionRect.height, zoomLevel) + "px"; + span.style.top = domUtils.adaptRangeDifferenceToZoomLevel(selectionRect.top - caretRect.top, zoomLevel) + "px" }else { - keptChildNode = domPosition.textNode; - if(domPosition.offset >= domPosition.textNode.length) { - splitChildNode = null + span.style.height = DEFAULT_CARET_HEIGHT; + span.style.top = DEFAULT_CARET_TOP + } + if(overlayElement) { + caretStyle = runtime.getWindow().getComputedStyle(span, null); + caretRect = domUtils.getBoundingClientRect(span); + overlayElement.style.bottom = domUtils.adaptRangeDifferenceToZoomLevel(rootRect.bottom - caretRect.bottom, zoomLevel) + "px"; + overlayElement.style.left = domUtils.adaptRangeDifferenceToZoomLevel(caretRect.right - rootRect.left, zoomLevel) + "px"; + if(caretStyle.font) { + overlayElement.style.font = caretStyle.font }else { - splitChildNode = (domPosition.textNode.splitText(domPosition.offset)) + overlayElement.style.fontStyle = caretStyle.fontStyle; + overlayElement.style.fontVariant = caretStyle.fontVariant; + overlayElement.style.fontWeight = caretStyle.fontWeight; + overlayElement.style.fontSize = caretStyle.fontSize; + overlayElement.style.lineHeight = caretStyle.lineHeight; + overlayElement.style.fontFamily = caretStyle.fontFamily } } - node = domPosition.textNode; - while(node !== targetNode) { - node = node.parentNode; - splitNode = node.cloneNode(false); - if(splitChildNode) { - splitNode.appendChild(splitChildNode) + } + function ensureVisible() { + var canvasElement = cursor.getDocument().getCanvas().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 } - if(keptChildNode) { - while(keptChildNode && keptChildNode.nextSibling) { - splitNode.appendChild(keptChildNode.nextSibling) - } + } + if(caretRect.left < canvasContainerRect.left) { + canvasContainerElement.scrollLeft -= canvasContainerRect.left - caretRect.left + }else { + if(caretRect.right > canvasContainerRect.right) { + canvasContainerElement.scrollLeft += caretRect.right - canvasContainerRect.right + } + } + } + function hasStateChanged(property) { + return lastState[property] !== state[property] + } + function saveState() { + Object.keys(state).forEach(function(key) { + lastState[key] = state[key] + }) + } + function updateCaret() { + if(state.isShown === false || (cursor.getSelectionType() !== ops.OdtCursor.RangeSelection || !blinkOnRangeSelect && !cursor.getSelectedRange().collapsed)) { + state.visibility = "hidden"; + span.style.visibility = "hidden"; + blinkTask.cancel() + }else { + state.visibility = "visible"; + span.style.visibility = "visible"; + if(state.isFocused === false) { + span.style.opacity = "1"; + blinkTask.cancel() }else { - while(node.firstChild) { - splitNode.appendChild(node.firstChild) + if(shouldResetBlink || hasStateChanged("visibility")) { + span.style.opacity = "1"; + blinkTask.cancel() } + blinkTask.trigger() + } + if(shouldUpdateCaretSize || (shouldCheckCaretVisibility || hasStateChanged("visibility"))) { + updateCaretHeightAndPosition() + } + if(shouldCheckCaretVisibility) { + ensureVisible() } - node.parentNode.insertBefore(splitNode, node.nextSibling); - keptChildNode = node; - splitChildNode = splitNode } - if(odfUtils.isListItem(splitChildNode)) { - splitChildNode = splitChildNode.childNodes[0] + if(hasStateChanged("isFocused")) { + avatar.markAsFocussed(state.isFocused) } - if(domPosition.textNode.length === 0) { - domPosition.textNode.parentNode.removeChild(domPosition.textNode) + saveState(); + shouldResetBlink = false; + shouldCheckCaretVisibility = false; + shouldUpdateCaretSize = false + } + this.handleUpdate = function() { + shouldUpdateCaretSize = true; + if(state.visibility !== "hidden") { + state.visibility = "hidden"; + span.style.visibility = "hidden" } - odtDocument.emit(ops.OdtDocument.signalStepsInserted, {position:position, length:1}); - if(cursor && moveCursor) { - odtDocument.moveCursor(memberid, position + 1, 0); - odtDocument.emit(ops.OdtDocument.signalCursorMoved, cursor) + redrawTask.trigger() + }; + this.refreshCursorBlinking = function() { + shouldResetBlink = true; + redrawTask.trigger() + }; + this.setFocus = function() { + state.isFocused = true; + redrawTask.trigger() + }; + this.removeFocus = function() { + state.isFocused = false; + redrawTask.trigger() + }; + this.show = function() { + state.isShown = true; + redrawTask.trigger() + }; + this.hide = function() { + state.isShown = false; + redrawTask.trigger() + }; + 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() } - 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, moveCursor:moveCursor} + this.showHandle = function() { + avatar.show() + }; + this.hideHandle = function() { + avatar.hide() + }; + this.setOverlayElement = function(element) { + overlayElement = element; + shouldUpdateCaretSize = true; + redrawTask.trigger() + }; + this.ensureVisible = function() { + shouldCheckCaretVisibility = true; + redrawTask.trigger() + }; + function destroy(callback) { + cursorNode.removeChild(span); + callback() + } + this.destroy = function(callback) { + var cleanup = [redrawTask.destroy, blinkTask.destroy, avatar.destroy, destroy]; + async.destroyAll(cleanup, callback) + }; + function init() { + var dom = cursor.getDocument().getDOMDocument(), htmlns = dom.documentElement.namespaceURI; + span = (dom.createElementNS(htmlns, "span")); + span.className = "caret"; + span.style.top = DEFAULT_CARET_TOP; + cursorNode = cursor.getNode(); + cursorNode.appendChild(span); + avatar = new gui.Avatar(cursorNode, avatarInitiallyVisible); + redrawTask = new core.ScheduledTask(updateCaret, 0); + blinkTask = new core.ScheduledTask(blinkCaret, BLINK_PERIOD_MS); + redrawTask.triggerImmediate() } + init() }; -ops.OpSplitParagraph.Spec; /* Copyright (C) 2013 KO GmbH - @licstart - This file is part of WebODF. + @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 . - 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/ */ -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 = xmldom.XPath, xp = "//dc:creator[@editinfo:memberid='" + memberid + "']", creators = xpath.getODFElementsWithXPath(doc.getRootNode(), xp, function(prefix) { - if(prefix === "editinfo") { - return"urn:webodf:names:editinfo" +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) && odfUtils.isTextContentContainingNode(node)) { + child = node.firstChild; + while(child) { + s += serializeNode(child); + child = child.nextSibling } - 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(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 + } + } } - if(removedProperties) { - member.removeProperties(removedProperties) + return s + } + this.filter = null; + this.writeToString = function(node) { + var plainText; + if(!node) { + return"" } - if(setProperties) { - member.setProperties(setProperties); - if(setProperties.fullName) { - updateCreators() - } + plainText = serializeNode(node); + if(plainText[plainText.length - 1] === "\n") { + plainText = plainText.substr(0, plainText.length - 1) } - odtDocument.emit(ops.OdtDocument.signalMemberUpdated, member); - return true - }; - this.spec = function() { - return{optype:"UpdateMember", memberid:memberid, timestamp:timestamp, setProperties:setProperties, removedProperties:removedProperties} + return plainText } }; /* @@ -14428,42 +12958,29 @@ ops.OpUpdateMember = function OpUpdateMember() { @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 odfContainer = odtDocument.getOdfCanvas().odfContainer(), 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(",") +gui.MimeDataExporter = function MimeDataExporter() { + var textSerializer, filter; + this.exportRangeToDataTransfer = function(dataTransfer, range) { + var document = range.startContainer.ownerDocument, serializedFragment, fragmentContainer; + fragmentContainer = document.createElement("span"); + fragmentContainer.appendChild(range.cloneContents()); + serializedFragment = textSerializer.writeToString(fragmentContainer); + try { + dataTransfer.setData("text/plain", serializedFragment) + }catch(e) { + dataTransfer.setData("Text", serializedFragment) } - odfContainer.setMetadata(setProperties, removedPropertiesArray); - return true }; - this.spec = function() { - return{optype:"UpdateMetadata", memberid:memberid, timestamp:timestamp, setProperties:setProperties, removedProperties:removedProperties} + function init() { + textSerializer = new odf.TextSerializer; + filter = new odf.OdfNodeFilter; + textSerializer.filter = filter } + 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 @@ -14498,67 +13015,25 @@ ops.OpUpdateMetadata = function OpUpdateMetadata() { @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.lookupNamespaceURI(attributeNameParts[0]), attributeNameParts[1]) +gui.Clipboard = function Clipboard(mimeDataExporter) { + this.setDataFromRange = function(e, range) { + var result, clipboard = e.clipboardData, window = runtime.getWindow(); + if(!clipboard && window) { + clipboard = window.clipboardData } - } - 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(clipboard) { + result = true; + mimeDataExporter.exportRangeToDataTransfer((clipboard), range); + e.preventDefault() }else { - 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 + result = false } - return false - }; - this.spec = function() { - return{optype:"UpdateParagraphStyle", memberid:memberid, timestamp:timestamp, styleName:styleName, setProperties:setProperties, removedProperties:removedProperties} + return result } }; -ops.OpUpdateParagraphStyle.Spec; /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2012-2014 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -14593,50 +13068,53 @@ ops.OpUpdateParagraphStyle.Spec; @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) +gui.StyleSummary = function StyleSummary(styles) { + var propertyValues = {}; + function getPropertyValues(section, propertyName) { + var cacheKey = section + "|" + propertyName, values; + if(!propertyValues.hasOwnProperty(cacheKey)) { + values = []; + styles.forEach(function(style) { + var styleSection = style[section], value = styleSection && styleSection[propertyName]; + if(values.indexOf(value) === -1) { + values.push(value) + } + }); + propertyValues[cacheKey] = values } - return op - }; - function constructor(OperationType) { + return propertyValues[cacheKey] + } + this.getPropertyValues = getPropertyValues; + function lazilyLoaded(section, propertyName, acceptedPropertyValues) { return function() { - return new OperationType + var existingPropertyValues = getPropertyValues(section, propertyName); + return acceptedPropertyValues.length >= existingPropertyValues.length && existingPropertyValues.every(function(v) { + return acceptedPropertyValues.indexOf(v) !== -1 + }) } } - 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)} + function getCommonValue(section, propertyName) { + var values = getPropertyValues(section, propertyName); + return values.length === 1 ? values[0] : undefined } - init() + this.getCommonValue = getCommonValue; + this.isBold = lazilyLoaded("style:text-properties", "fo:font-weight", ["bold"]); + this.isItalic = lazilyLoaded("style:text-properties", "fo:font-style", ["italic"]); + this.hasUnderline = lazilyLoaded("style:text-properties", "style:text-underline-style", ["solid"]); + this.hasStrikeThrough = lazilyLoaded("style:text-properties", "style:text-line-through-style", ["solid"]); + this.fontSize = function() { + var stringFontSize = getCommonValue("style:text-properties", "fo:font-size"); + return(stringFontSize && parseFloat(stringFontSize)) + }; + this.fontName = function() { + return getCommonValue("style:text-properties", "style:font-name") + }; + this.isAlignedLeft = lazilyLoaded("style:paragraph-properties", "fo:text-align", ["left", "start"]); + this.isAlignedCenter = lazilyLoaded("style:paragraph-properties", "fo:text-align", ["center"]); + this.isAlignedRight = lazilyLoaded("style:paragraph-properties", "fo:text-align", ["right", "end"]); + this.isAlignedJustified = lazilyLoaded("style:paragraph-properties", "fo:text-align", ["justify"]); + this.text = {isBold:this.isBold, isItalic:this.isItalic, hasUnderline:this.hasUnderline, hasStrikeThrough:this.hasStrikeThrough, fontSize:this.fontSize, fontName:this.fontName}; + this.paragraph = {isAlignedLeft:this.isAlignedLeft, isAlignedCenter:this.isAlignedCenter, isAlignedRight:this.isAlignedRight, isAlignedJustified:this.isAlignedJustified} }; /* @@ -14675,709 +13153,1105 @@ ops.OperationFactory = function OperationFactory() { @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) { -}; -ops.OperationRouter.prototype.subscribe = function(eventId, cb) { -}; -ops.OperationRouter.prototype.unsubscribe = function(eventId, cb) { -}; -ops.OperationRouter.prototype.hasLocalUnsyncedOps = function() { -}; -ops.OperationRouter.prototype.hasSessionHostConnection = function() { -}; -/* - - 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.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) - } - return isBackwards - } - 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) - } - }) +gui.DirectFormattingController = function DirectFormattingController(session, inputMemberId, objectNameGenerator, directParagraphStylingEnabled) { + var self = this, odtDocument = session.getOdtDocument(), utils = new core.Utils, odfUtils = new odf.OdfUtils, eventNotifier = new core.EventNotifier([gui.DirectFormattingController.textStylingChanged, gui.DirectFormattingController.paragraphStylingChanged]), textns = odf.Namespaces.textns, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, directCursorStyleProperties, selectionAppliedStyles = [], selectionStylesSummary = new gui.StyleSummary(selectionAppliedStyles); + function getNodes(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 attributes + return nodes } - function dropStyleReferencingAttributes(setProperties, deletedStyleName) { - if(setProperties) { - ["style:parent-style-name", "style:next-style-name"].forEach(function(attributeName) { - if(setProperties[attributeName] === deletedStyleName) { - delete setProperties[attributeName] - } - }) + function getSelectionAppliedStyles() { + var cursor = odtDocument.getCursor(inputMemberId), range = cursor && cursor.getSelectedRange(), nodes = range ? getNodes(range) : [], selectionStyles = odtDocument.getFormatting().getAppliedStyles(nodes); + if(selectionStyles[0] && directCursorStyleProperties) { + selectionStyles[0] = utils.mergeObjects(selectionStyles[0], (directCursorStyleProperties)) } + return selectionStyles } - 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] + function createDiff(oldSummary, newSummary) { + var diffMap = {}; + Object.keys(oldSummary).forEach(function(funcName) { + var oldValue = oldSummary[funcName](), newValue = newSummary[funcName](); + if(oldValue !== newValue) { + diffMap[funcName] = newValue } }); - return result + return diffMap } - 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 - } - } - } - }) + function updateSelectionStylesInfo() { + var textStyleDiff, paragraphStyleDiff, newSelectionStylesSummary; + selectionAppliedStyles = getSelectionAppliedStyles(); + newSelectionStylesSummary = new gui.StyleSummary(selectionAppliedStyles); + textStyleDiff = createDiff(selectionStylesSummary.text, newSelectionStylesSummary.text); + paragraphStyleDiff = createDiff(selectionStylesSummary.paragraph, newSelectionStylesSummary.paragraph); + selectionStylesSummary = newSelectionStylesSummary; + if(Object.keys(textStyleDiff).length > 0) { + eventNotifier.emit(gui.DirectFormattingController.textStylingChanged, textStyleDiff) } - 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 - } + if(Object.keys(paragraphStyleDiff).length > 0) { + eventNotifier.emit(gui.DirectFormattingController.paragraphStylingChanged, paragraphStyleDiff) } - return{majorChanged:majorChanged, minorChanged:minorChanged} } - function hasProperties(properties) { - var key; - for(key in properties) { - if(properties.hasOwnProperty(key)) { - return true - } + function onCursorEvent(cursorOrId) { + var cursorMemberId = typeof cursorOrId === "string" ? cursorOrId : cursorOrId.getMemberId(); + if(cursorMemberId === inputMemberId) { + updateSelectionStylesInfo() } - return false } - function hasRemovedProperties(properties) { - var key; - for(key in properties) { - if(properties.hasOwnProperty(key)) { - if(key !== "attributes" || properties.attributes.length > 0) { - return true - } - } - } - return false + function onParagraphStyleModified() { + updateSelectionStylesInfo() } - 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] + function onParagraphChanged(args) { + var cursor = odtDocument.getCursor(inputMemberId), p = args.paragraphElement; + if(cursor && odtDocument.getParagraphElement(cursor.getNode()) === p) { + updateSelectionStylesInfo() } - if(majorRP && !hasRemovedProperties(majorRP)) { - delete majorOpspec.removedProperties[propertiesName] + } + function toggle(predicate, toggleMethod) { + toggleMethod(!predicate()); + 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); + updateSelectionStylesInfo() } - 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) + this.formatTextSelection = formatTextSelection; + function applyTextPropertyToSelection(propertyName, propertyValue) { + var textProperties = {}; + textProperties[propertyName] = propertyValue; + formatTextSelection(textProperties) + } + this.createCursorStyleOp = function(position, length, useCachedStyle) { + var styleOp = null, properties = useCachedStyle ? selectionAppliedStyles[0] : directCursorStyleProperties; + if(properties && properties["style:text-properties"]) { + styleOp = new ops.OpApplyDirectStyling; + styleOp.init({memberid:inputMemberId, position:position, length:length, setProperties:{"style:text-properties":properties["style:text-properties"]}}); + directCursorStyleProperties = null; + updateSelectionStylesInfo() + } + return styleOp + }; + function clearCursorStyle(op) { + var spec = op.spec(); + if(directCursorStyleProperties && spec.memberid === inputMemberId) { + if(spec.optype !== "SplitParagraph") { + directCursorStyleProperties = null; + updateSelectionStylesInfo() } - dropStyleReferencingAttributes(addStyleSpec.setProperties, removeStyleSpec.styleName) } - 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) + 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 selectionAppliedStyles + }; + this.toggleBold = toggle.bind(self, function() { + return selectionStylesSummary.isBold() + }, setBold); + this.toggleItalic = toggle.bind(self, function() { + return selectionStylesSummary.isItalic() + }, setItalic); + this.toggleUnderline = toggle.bind(self, function() { + return selectionStylesSummary.hasUnderline() + }, setHasUnderline); + this.toggleStrikethrough = toggle.bind(self, function() { + return selectionStylesSummary.hasStrikeThrough() + }, setHasStrikethrough); + this.isBold = function() { + return selectionStylesSummary.isBold() + }; + this.isItalic = function() { + return selectionStylesSummary.isItalic() + }; + this.hasUnderline = function() { + return selectionStylesSummary.hasUnderline() + }; + this.hasStrikeThrough = function() { + return selectionStylesSummary.hasStrikeThrough() + }; + this.fontSize = function() { + return selectionStylesSummary.fontSize() + }; + this.fontName = function() { + return selectionStylesSummary.fontName() + }; + this.isAlignedLeft = function() { + return selectionStylesSummary.isAlignedLeft() + }; + this.isAlignedCenter = function() { + return selectionStylesSummary.isAlignedCenter() + }; + this.isAlignedRight = function() { + return selectionStylesSummary.isAlignedRight() + }; + this.isAlignedJustified = function() { + return selectionStylesSummary.isAlignedJustified() + }; + function roundUp(step) { + return step === ops.StepsTranslator.NEXT_STEP + } + function getOwnProperty(obj, key) { + return obj.hasOwnProperty(key) ? obj[key] : undefined + } + function applyParagraphDirectStyling(applyDirectStyling) { + var range = odtDocument.getCursor(inputMemberId).getSelectedRange(), paragraphs = odfUtils.getParagraphElements(range), formatting = odtDocument.getFormatting(), operations = [], derivedStyleNames = {}, defaultStyleName; + paragraphs.forEach(function(paragraph) { + var paragraphStartPoint = odtDocument.convertDomPointToCursorStep(paragraph, 0, roundUp), paragraphStyleName = paragraph.getAttributeNS(odf.Namespaces.textns, "style-name"), newParagraphStyleName, opAddStyle, opSetParagraphStyle, paragraphProperties; + if(paragraphStyleName) { + newParagraphStyleName = getOwnProperty(derivedStyleNames, paragraphStyleName) + }else { + newParagraphStyleName = defaultStyleName } - 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 - } + if(!newParagraphStyleName) { + newParagraphStyleName = objectNameGenerator.generateStyleName(); + if(paragraphStyleName) { + derivedStyleNames[paragraphStyleName] = newParagraphStyleName; + paragraphProperties = formatting.createDerivedStyleObject(paragraphStyleName, "paragraph", {}) }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 + defaultStyleName = newParagraphStyleName; + paragraphProperties = {} } + paragraphProperties = applyDirectStyling(paragraphProperties); + opAddStyle = new ops.OpAddStyle; + opAddStyle.init({memberid:inputMemberId, styleName:newParagraphStyleName.toString(), styleFamily:"paragraph", isAutomaticStyle:true, setProperties:paragraphProperties}); + operations.push(opAddStyle) + } + opSetParagraphStyle = new ops.OpSetParagraphStyle; + opSetParagraphStyle.init({memberid:inputMemberId, styleName:newParagraphStyleName.toString(), position:paragraphStartPoint}); + operations.push(opSetParagraphStyle) + }); + session.enqueue(operations) + } + 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, indent, newIndent; + if(paragraphProperties) { + indentValue = paragraphProperties["fo:margin-left"]; + if(indentValue) { + indent = odfUtils.parseLength(indentValue) } } - return{opSpecsA:applyDirectStylingSpecAResult, opSpecsB:applyDirectStylingSpecBResult} - } - function transformApplyDirectStylingInsertText(applyDirectStylingSpec, insertTextSpec) { - if(insertTextSpec.position <= applyDirectStylingSpec.position) { - applyDirectStylingSpec.position += insertTextSpec.text.length + if(indent && indent.unit === tabStopDistance.unit) { + newIndent = indent.value + direction * tabStopDistance.value + indent.unit }else { - if(insertTextSpec.position <= applyDirectStylingSpec.position + applyDirectStylingSpec.length) { - applyDirectStylingSpec.length += insertTextSpec.text.length - } + newIndent = direction * tabStopDistance.value + tabStopDistance.unit } - return{opSpecsA:[applyDirectStylingSpec], opSpecsB:[insertTextSpec]} + return utils.mergeObjects(paragraphStyle, {"style:paragraph-properties":{"fo:margin-left":newIndent}}) } - 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 = [] - } - } + this.indent = function() { + applyParagraphDirectStyling(modifyParagraphIndent.bind(null, 1)); + return true + }; + this.outdent = function() { + applyParagraphDirectStyling(modifyParagraphIndent.bind(null, -1)); + return true + }; + function isSelectionAtTheEndOfLastParagraph(range, paragraphNode) { + var iterator = gui.SelectionMover.createPositionIterator(paragraphNode), rootConstrainedFilter = new core.PositionFilterChain; + rootConstrainedFilter.addFilter(odtDocument.getPositionFilter()); + rootConstrainedFilter.addFilter(odtDocument.createRootFilter(inputMemberId)); + iterator.setUnfilteredPosition((range.endContainer), range.endOffset); + while(iterator.nextPosition()) { + if(rootConstrainedFilter.acceptPosition(iterator) === FILTER_ACCEPT) { + return odtDocument.getParagraphElement(iterator.getCurrentNode()) !== paragraphNode } } - return{opSpecsA:applyDirectStylingSpecResult, opSpecsB:removeTextSpecResult} + return true } - function transformApplyDirectStylingSplitParagraph(applyDirectStylingSpec, splitParagraphSpec) { - if(splitParagraphSpec.position < applyDirectStylingSpec.position) { - applyDirectStylingSpec.position += 1 - }else { - if(splitParagraphSpec.position < applyDirectStylingSpec.position + applyDirectStylingSpec.length) { - applyDirectStylingSpec.length += 1 - } + function isTextStyleDifferentFromFirstParagraph(range, paragraphNode) { + var textNodes = getNodes(range), textStyle = odtDocument.getFormatting().getAppliedStyles(textNodes)[0], paragraphStyle = odtDocument.getFormatting().getAppliedStylesForElement(paragraphNode); + if(!textStyle || (textStyle["style:family"] !== "text" || !textStyle["style:text-properties"])) { + return false } - return{opSpecsA:[applyDirectStylingSpec], opSpecsB:[splitParagraphSpec]} + if(!paragraphStyle || !paragraphStyle["style:text-properties"]) { + return true + } + textStyle = (textStyle["style:text-properties"]); + paragraphStyle = (paragraphStyle["style:text-properties"]); + return!Object.keys(textStyle).every(function(key) { + return textStyle[key] === paragraphStyle[key] + }) } - function transformInsertTextInsertText(insertTextSpecA, insertTextSpecB, hasAPriority) { - if(insertTextSpecA.position < insertTextSpecB.position) { - insertTextSpecB.position += insertTextSpecA.text.length + this.createParagraphStyleOps = function(position) { + var cursor = odtDocument.getCursor(inputMemberId), range = cursor.getSelectedRange(), operations = [], op, startNode, endNode, paragraphNode, properties, parentStyleName, styleName; + if(cursor.hasForwardSelection()) { + startNode = cursor.getAnchorNode(); + endNode = cursor.getNode() }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 - } + startNode = cursor.getNode(); + endNode = cursor.getAnchorNode() + } + paragraphNode = (odtDocument.getParagraphElement(endNode)); + runtime.assert(Boolean(paragraphNode), "DirectFormattingController: Cursor outside paragraph"); + if(!isSelectionAtTheEndOfLastParagraph(range, paragraphNode)) { + return operations + } + if(endNode !== startNode) { + paragraphNode = (odtDocument.getParagraphElement(startNode)) + } + if(!directCursorStyleProperties && !isTextStyleDifferentFromFirstParagraph(range, paragraphNode)) { + return operations + } + properties = selectionAppliedStyles[0]; + if(!properties) { + return operations + } + parentStyleName = paragraphNode.getAttributeNS(textns, "style-name"); + if(parentStyleName) { + properties = {"style:text-properties":properties["style:text-properties"]}; + properties = odtDocument.getFormatting().createDerivedStyleObject(parentStyleName, "paragraph", properties) + } + styleName = objectNameGenerator.generateStyleName(); + op = new ops.OpAddStyle; + op.init({memberid:inputMemberId, styleName:styleName, styleFamily:"paragraph", isAutomaticStyle:true, setProperties:properties}); + operations.push(op); + op = new ops.OpSetParagraphStyle; + op.init({memberid:inputMemberId, styleName:styleName, position:position}); + operations.push(op); + return operations + }; + 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.Document.signalCursorAdded, onCursorEvent); + odtDocument.unsubscribe(ops.Document.signalCursorRemoved, onCursorEvent); + odtDocument.unsubscribe(ops.Document.signalCursorMoved, onCursorEvent); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); + odtDocument.unsubscribe(ops.OdtDocument.signalOperationEnd, clearCursorStyle); + callback() + }; + function emptyFunction() { + } + function init() { + odtDocument.subscribe(ops.Document.signalCursorAdded, onCursorEvent); + odtDocument.subscribe(ops.Document.signalCursorRemoved, onCursorEvent); + odtDocument.subscribe(ops.Document.signalCursorMoved, onCursorEvent); + odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified); + odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); + odtDocument.subscribe(ops.OdtDocument.signalOperationEnd, clearCursorStyle); + updateSelectionStylesInfo(); + if(!directParagraphStylingEnabled) { + self.alignParagraphCenter = emptyFunction; + self.alignParagraphJustified = emptyFunction; + self.alignParagraphLeft = emptyFunction; + self.alignParagraphRight = emptyFunction; + self.createParagraphStyleOps = function() { + return[] + }; + self.indent = emptyFunction; + self.outdent = emptyFunction + } + } + init() +}; +gui.DirectFormattingController.textStylingChanged = "textStyling/changed"; +gui.DirectFormattingController.paragraphStylingChanged = "paragraphStyling/changed"; +(function() { + return gui.DirectFormattingController +})(); +gui.HyperlinkClickHandler = function HyperlinkClickHandler(getRootNode) { + var webodfns = "urn:webodf:names:helper", links = "links", inactive = "inactive", None = gui.HyperlinkClickHandler.Modifier.None, Ctrl = gui.HyperlinkClickHandler.Modifier.Ctrl, Meta = gui.HyperlinkClickHandler.Modifier.Meta, odfUtils = new odf.OdfUtils, xpath = xmldom.XPath, modifier = None; + function getHyperlinkElement(node) { + while(node !== null) { + if(odfUtils.isHyperlink(node)) { + return(node) + } + if(odfUtils.isParagraph(node)) { + break } + node = node.parentNode } - return{opSpecsA:[insertTextSpecA], opSpecsB:[insertTextSpecB]} + return null } - function transformInsertTextMoveCursor(insertTextSpec, moveCursorSpec) { - var isMoveCursorSpecRangeInverted = invertMoveCursorSpecRangeOnNegativeLength(moveCursorSpec); - if(insertTextSpec.position < moveCursorSpec.position) { - moveCursorSpec.position += insertTextSpec.text.length + this.handleClick = function(e) { + var target = e.target || e.srcElement, modifierPressed, linkElement, url, rootNode, bookmarks; + if(e.ctrlKey) { + modifierPressed = Ctrl }else { - if(insertTextSpec.position < moveCursorSpec.position + moveCursorSpec.length) { - moveCursorSpec.length += insertTextSpec.text.length + if(e.metaKey) { + modifierPressed = Meta } } - if(isMoveCursorSpecRangeInverted) { - invertMoveCursorSpecRange(moveCursorSpec) + if(modifier !== None && modifier !== modifierPressed) { + return } - 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 + linkElement = getHyperlinkElement((target)); + if(!linkElement) { + return + } + url = odfUtils.getHyperlinkTarget(linkElement); + if(url === "") { + return + } + if(url[0] === "#") { + url = url.substring(1); + rootNode = (getRootNode()); + bookmarks = xpath.getODFElementsWithXPath(rootNode, "//text:bookmark-start[@text:name='" + url + "']", odf.Namespaces.lookupNamespaceURI); + if(bookmarks.length === 0) { + bookmarks = xpath.getODFElementsWithXPath(rootNode, "//text:bookmark[@text:name='" + url + "']", odf.Namespaces.lookupNamespaceURI) } + if(bookmarks.length > 0) { + bookmarks[0].scrollIntoView(true) + } + }else { + runtime.getWindow().open(url) } - return{opSpecsA:insertTextSpecResult, opSpecsB:removeTextSpecResult} + if(e.preventDefault) { + e.preventDefault() + }else { + e.returnValue = false + } + }; + function showPointerCursor() { + getRootNode().removeAttributeNS(webodfns, links) } - function transformInsertTextSplitParagraph(insertTextSpec, splitParagraphSpec, hasAPriority) { - if(insertTextSpec.position < splitParagraphSpec.position) { - splitParagraphSpec.position += insertTextSpec.text.length + this.showPointerCursor = showPointerCursor; + function showTextCursor() { + getRootNode().setAttributeNS(webodfns, links, inactive) + } + this.showTextCursor = showTextCursor; + this.setModifier = function(value) { + modifier = value; + if(modifier !== None) { + showTextCursor() }else { - if(insertTextSpec.position > splitParagraphSpec.position) { - insertTextSpec.position += 1 - }else { - if(hasAPriority) { - splitParagraphSpec.position += insertTextSpec.text.length - }else { - insertTextSpec.position += 1 - } - return null - } + showPointerCursor() } - 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 = [] +}; +gui.HyperlinkClickHandler.Modifier = {None:0, Ctrl:1, Meta:2}; +gui.HyperlinkController = function HyperlinkController(session, inputMemberId) { + var odfUtils = new odf.OdfUtils, odtDocument = session.getOdtDocument(); + function addHyperlink(hyperlink, insertionText) { + var selection = odtDocument.getCursorSelection(inputMemberId), op = new ops.OpApplyHyperlink, operations = []; + if(selection.length === 0 || insertionText) { + insertionText = insertionText || hyperlink; + op = new ops.OpInsertText; + op.init({memberid:inputMemberId, position:selection.position, text:insertionText}); + selection.length = insertionText.length; + operations.push(op) + } + op = new ops.OpApplyHyperlink; + op.init({memberid:inputMemberId, position:selection.position, length:selection.length, hyperlink:hyperlink}); + operations.push(op); + session.enqueue(operations) + } + this.addHyperlink = addHyperlink; + function removeHyperlinks() { + var iterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), selectedRange = odtDocument.getCursor(inputMemberId).getSelectedRange(), links = odfUtils.getHyperlinkElements(selectedRange), removeEntireLink = selectedRange.collapsed && links.length === 1, domRange = odtDocument.getDOMDocument().createRange(), operations = [], cursorRange, firstLink, lastLink, offset, op; + if(links.length === 0) { + return + } + links.forEach(function(link) { + domRange.selectNodeContents(link); + cursorRange = odtDocument.convertDomToCursorRange({anchorNode:(domRange.startContainer), anchorOffset:domRange.startOffset, focusNode:(domRange.endContainer), focusOffset:domRange.endOffset}); + op = new ops.OpRemoveHyperlink; + op.init({memberid:inputMemberId, position:cursorRange.position, length:cursorRange.length}); + operations.push(op) + }); + if(!removeEntireLink) { + firstLink = (links[0]); + if(selectedRange.comparePoint(firstLink, 0) === -1) { + domRange.setStart(firstLink, 0); + domRange.setEnd(selectedRange.startContainer, selectedRange.startOffset); + cursorRange = odtDocument.convertDomToCursorRange({anchorNode:(domRange.startContainer), anchorOffset:domRange.startOffset, focusNode:(domRange.endContainer), focusOffset:domRange.endOffset}); + if(cursorRange.length > 0) { + op = new ops.OpApplyHyperlink; + (op).init({memberid:inputMemberId, position:cursorRange.position, length:cursorRange.length, hyperlink:odfUtils.getHyperlinkTarget(firstLink)}); + operations.push(op) + } + } + lastLink = (links[links.length - 1]); + iterator.moveToEndOfNode(lastLink); + offset = iterator.unfilteredDomOffset(); + if(selectedRange.comparePoint(lastLink, offset) === 1) { + domRange.setStart(selectedRange.endContainer, selectedRange.endOffset); + domRange.setEnd(lastLink, offset); + cursorRange = odtDocument.convertDomToCursorRange({anchorNode:(domRange.startContainer), anchorOffset:domRange.startOffset, focusNode:(domRange.endContainer), focusOffset:domRange.endOffset}); + if(cursorRange.length > 0) { + op = new ops.OpApplyHyperlink; + (op).init({memberid:inputMemberId, position:cursorRange.position, length:cursorRange.length, hyperlink:odfUtils.getHyperlinkTarget(lastLink)}); + operations.push(op) } } - if(!(minorSpec.setProperties && hasProperties(minorSpec.setProperties)) && !(minorSpec.removedProperties && hasRemovedProperties(minorSpec.removedProperties))) { - if(hasAPriority) { - updateParagraphStyleSpecBResult = [] - }else { - updateParagraphStyleSpecAResult = [] + } + session.enqueue(operations); + domRange.detach() + } + this.removeHyperlinks = removeHyperlinks +}; +gui.EventManager = function EventManager(odtDocument) { + var window = (runtime.getWindow()), bindToDirectHandler = {"beforecut":true, "beforepaste":true}, bindToWindow = {"mousedown":true, "mouseup":true, "focus":true}, eventDelegates = {}, eventTrap, canvasElement = odtDocument.getCanvas().getElement(); + function EventDelegate() { + var self = this, recentEvents = []; + this.filters = []; + this.handlers = []; + this.handleEvent = function(e) { + if(recentEvents.indexOf(e) === -1) { + recentEvents.push(e); + if(self.filters.every(function(filter) { + return filter(e) + })) { + self.handlers.forEach(function(handler) { + handler(e) + }) } + runtime.setTimeout(function() { + recentEvents.splice(recentEvents.indexOf(e), 1) + }, 0) } } - 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 = [] + function WindowScrollState(window) { + var x = window.scrollX, y = window.scrollY; + this.restore = function() { + if(window.scrollX !== x || window.scrollY !== y) { + window.scrollTo(x, y) } } - if(!(minorSpec.setProperties && hasProperties(minorSpec.setProperties)) && !(minorSpec.removedProperties && hasRemovedProperties(minorSpec.removedProperties))) { - if(hasAPriority) { - updateMetadataSpecBResult = [] - }else { - updateMetadataSpecAResult = [] + } + 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 } } - 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 { - if(splitParagraphSpecA.position === splitParagraphSpecB.position) { - if(hasAPriority) { - splitParagraphSpecB.position += 1 - }else { - splitParagraphSpecA.position += 1 - } - } - } + function listenEvent(eventTarget, eventType, eventHandler) { + var onVariant = "on" + eventType, bound = false; + if(eventTarget.attachEvent) { + eventTarget.attachEvent(onVariant, eventHandler); + bound = true + } + if(!bound && eventTarget.addEventListener) { + eventTarget.addEventListener(eventType, eventHandler, false); + bound = true + } + if((!bound || bindToDirectHandler[eventType]) && eventTarget.hasOwnProperty(onVariant)) { + eventTarget[onVariant] = eventHandler } - 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 getDelegateForEvent(eventName, shouldCreate) { + var delegate = eventDelegates[eventName] || null; + if(!delegate && shouldCreate) { + delegate = eventDelegates[eventName] = new EventDelegate; + if(bindToWindow[eventName]) { + listenEvent(window, eventName, delegate.handleEvent) } + listenEvent(eventTrap, eventName, delegate.handleEvent); + listenEvent(canvasElement, eventName, delegate.handleEvent) } - if(isMoveCursorSpecRangeInverted) { - invertMoveCursorSpecRange(moveCursorSpec) + return delegate + } + this.addFilter = function(eventName, filter) { + var delegate = getDelegateForEvent(eventName, true); + delegate.filters.push(filter) + }; + this.removeFilter = function(eventName, filter) { + var delegate = getDelegateForEvent(eventName, true), index = delegate.filters.indexOf(filter); + if(index !== -1) { + delegate.filters.splice(index, 1) } - return{opSpecsA:[moveCursorSpec], opSpecsB:[removeTextSpec]} + }; + this.subscribe = function(eventName, handler) { + var delegate = getDelegateForEvent(eventName, true); + delegate.handlers.push(handler) + }; + this.unsubscribe = function(eventName, handler) { + var delegate = getDelegateForEvent(eventName, false), handlerIndex = delegate && delegate.handlers.indexOf(handler); + if(delegate && handlerIndex !== -1) { + delegate.handlers.splice(handlerIndex, 1) + } + }; + function hasFocus() { + return odtDocument.getDOMDocument().activeElement === eventTrap } - 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 + this.hasFocus = hasFocus; + function findScrollableParents(element) { + var scrollParents = []; + while(element) { + if(element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight) { + scrollParents.push(new ElementScrollState(element)) } + element = (element.parentNode) } - if(isMoveCursorSpecRangeInverted) { - invertMoveCursorSpecRange(moveCursorSpec) + scrollParents.push(new WindowScrollState(window)); + return scrollParents + } + this.focus = function() { + var scrollParents; + if(!hasFocus()) { + scrollParents = findScrollableParents(eventTrap); + eventTrap.focus(); + scrollParents.forEach(function(scrollParent) { + scrollParent.restore() + }) } - return{opSpecsA:[moveCursorSpec], opSpecsB:[splitParagraphSpec]} + }; + this.getEventTrap = function() { + return eventTrap + }; + this.blur = function() { + if(hasFocus()) { + eventTrap.blur() + } + }; + this.destroy = function(callback) { + eventTrap.parentNode.removeChild(eventTrap); + callback() + }; + function init() { + var sizerElement = odtDocument.getOdfCanvas().getSizer(), doc = sizerElement.ownerDocument; + runtime.assert(Boolean(window), "EventManager requires a window object to operate correctly"); + eventTrap = (doc.createElement("input")); + eventTrap.id = "eventTrap"; + eventTrap.setAttribute("tabindex", -1); + sizerElement.appendChild(eventTrap) } - function transformRemoveCursorRemoveCursor(removeCursorSpecA, removeCursorSpecB) { - var isSameMemberid = removeCursorSpecA.memberid === removeCursorSpecB.memberid; - return{opSpecsA:isSameMemberid ? [] : [removeCursorSpecA], opSpecsB:isSameMemberid ? [] : [removeCursorSpecB]} + init() +}; +/* + + Copyright (C) 2014 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/ +*/ +gui.IOSSafariSupport = function(eventManager) { + var window = runtime.getWindow(), eventTrap = eventManager.getEventTrap(); + function suppressFocusScrollIfKeyboardOpen() { + if(window.innerHeight !== window.outerHeight) { + eventTrap.style.display = "none"; + runtime.requestAnimationFrame(function() { + eventTrap.style.display = "block" + }) + } } - function transformRemoveStyleRemoveStyle(removeStyleSpecA, removeStyleSpecB) { - var isSameStyle = removeStyleSpecA.styleName === removeStyleSpecB.styleName && removeStyleSpecA.styleFamily === removeStyleSpecB.styleFamily; - return{opSpecsA:isSameStyle ? [] : [removeStyleSpecA], opSpecsB:isSameStyle ? [] : [removeStyleSpecB]} + this.destroy = function(callback) { + eventManager.unsubscribe("focus", suppressFocusScrollIfKeyboardOpen); + eventTrap.removeAttribute("autocapitalize"); + callback() + }; + function init() { + eventManager.subscribe("focus", suppressFocusScrollIfKeyboardOpen); + eventTrap.setAttribute("autocapitalize", "off") } - 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 = "" + init() +}; +gui.ImageController = function ImageController(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) } - return{opSpecsA:removeStyleSpecResult, opSpecsB:setParagraphStyleSpecResult} + 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 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) - } + function trimmedSize(originalSize, pageContentSize) { + var widthRatio = 1, heightRatio = 1, ratio; + if(originalSize.width > pageContentSize.width) { + widthRatio = pageContentSize.width / originalSize.width } - return{opSpecsA:removeStyleSpecResult, opSpecsB:updateParagraphStyleSpecResult} + if(originalSize.height > pageContentSize.height) { + heightRatio = pageContentSize.height / originalSize.height + } + ratio = Math.min(widthRatio, heightRatio); + return{width:originalSize.width * ratio, height:originalSize.height * ratio} } - 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 = [] - } - } - } + 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) + } +}; +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 = (document.createElement("div")); + selectorElement.id = "imageSelector"; + selectorElement.style.borderWidth = selectorBorderWidth + "px"; + sizerElement.appendChild(selectorElement); + function createDiv(className) { + var squareElement = document.createElement("div"); + squareElement.className = className; + selectorElement.appendChild(squareElement) + } + squareClassNames.forEach(createDiv); + 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" } } - return{opSpecsA:removeTextSpecAResult, opSpecsB:removeTextSpecBResult} + hasSelection = false + }; + this.isSelectorElement = function(node) { + var selectorElement = document.getElementById(imageSelectorId); + if(!selectorElement) { + return false + } + return node === selectorElement || node.parentNode === selectorElement } - 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) +}; +(function() { + function DetectSafariCompositionError(eventManager) { + var lastCompositionValue, suppressedKeyPress = false; + function suppressIncorrectKeyPress(e) { + suppressedKeyPress = e.which && String.fromCharCode(e.which) === lastCompositionValue; + lastCompositionValue = undefined; + return suppressedKeyPress === false + } + function clearSuppression() { + suppressedKeyPress = false + } + function trapComposedValue(e) { + lastCompositionValue = e.data; + suppressedKeyPress = false + } + function init() { + eventManager.subscribe("textInput", clearSuppression); + eventManager.subscribe("compositionend", trapComposedValue); + eventManager.addFilter("keypress", suppressIncorrectKeyPress) + } + this.destroy = function(callback) { + eventManager.unsubscribe("textInput", clearSuppression); + eventManager.unsubscribe("compositionend", trapComposedValue); + eventManager.removeFilter("keypress", suppressIncorrectKeyPress); + callback() + }; + init() + } + gui.InputMethodEditor = function InputMethodEditor(inputMemberId, eventManager) { + var cursorns = "urn:webodf:names:cursor", localCursor = null, eventTrap = eventManager.getEventTrap(), doc = (eventTrap.ownerDocument), compositionElement, async = new core.Async, FAKE_CONTENT = "b", processUpdates, pendingEvent = false, pendingData = "", events = new core.EventNotifier([gui.InputMethodEditor.signalCompositionStart, gui.InputMethodEditor.signalCompositionEnd]), lastCompositionData, filters = [], cleanup, isEditable = false; + this.subscribe = events.subscribe; + this.unsubscribe = events.unsubscribe; + function setCursorComposing(state) { + if(localCursor) { + if(state) { + localCursor.getNode().setAttributeNS(cursorns, "composing", "true") + }else { + localCursor.getNode().removeAttributeNS(cursorns, "composing"); + compositionElement.textContent = "" + } } } - if(removeTextSpec.position + removeTextSpec.length <= splitParagraphSpec.position) { - splitParagraphSpec.position -= removeTextSpec.length - }else { - if(removeTextSpec.position < splitParagraphSpec.position) { - splitParagraphSpec.position = removeTextSpec.position + function flushEvent() { + if(pendingEvent) { + pendingEvent = false; + setCursorComposing(false); + events.emit(gui.InputMethodEditor.signalCompositionEnd, {data:pendingData}); + pendingData = "" } } - 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] = {} + function addCompositionData(data) { + pendingEvent = true; + pendingData += data; + processUpdates.trigger() + } + function resetWindowSelection() { + flushEvent(); + if(localCursor && localCursor.getSelectedRange().collapsed) { + eventTrap.value = "" + }else { + eventTrap.value = FAKE_CONTENT } - 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 + eventTrap.setSelectionRange(0, eventTrap.value.length) } - 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} + function compositionStart() { + lastCompositionData = undefined; + processUpdates.cancel(); + setCursorComposing(true); + if(!pendingEvent) { + events.emit(gui.InputMethodEditor.signalCompositionStart, {data:""}) } - }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 compositionEnd(e) { + lastCompositionData = e.data; + addCompositionData(e.data) } - return result - } -}; + function textInput(e) { + if(e.data !== lastCompositionData) { + addCompositionData(e.data) + } + lastCompositionData = undefined + } + function synchronizeCompositionText() { + compositionElement.textContent = eventTrap.value + } + this.registerCursor = function(cursor) { + if(cursor.getMemberId() === inputMemberId) { + localCursor = cursor; + localCursor.getNode().appendChild(compositionElement); + eventManager.subscribe("input", synchronizeCompositionText); + eventManager.subscribe("compositionupdate", synchronizeCompositionText) + } + }; + this.removeCursor = function(memberid) { + if(localCursor && memberid === inputMemberId) { + localCursor.getNode().removeChild(compositionElement); + eventManager.unsubscribe("input", synchronizeCompositionText); + eventManager.unsubscribe("compositionupdate", synchronizeCompositionText); + localCursor = null + } + }; + function suppressFocus() { + eventManager.blur(); + eventTrap.setAttribute("disabled", true) + } + function synchronizeEventStatus() { + var hasFocus = eventManager.hasFocus(); + if(hasFocus) { + eventManager.blur() + } + if(isEditable) { + eventTrap.removeAttribute("disabled") + }else { + eventTrap.setAttribute("disabled", true) + } + if(hasFocus) { + eventManager.focus() + } + } + this.setEditing = function(editable) { + isEditable = editable; + synchronizeEventStatus() + }; + this.destroy = function(callback) { + eventManager.unsubscribe("compositionstart", compositionStart); + eventManager.unsubscribe("compositionend", compositionEnd); + eventManager.unsubscribe("textInput", textInput); + eventManager.unsubscribe("keypress", flushEvent); + eventManager.unsubscribe("mousedown", suppressFocus); + eventManager.unsubscribe("mouseup", synchronizeEventStatus); + eventManager.unsubscribe("focus", resetWindowSelection); + async.destroyAll(cleanup, callback) + }; + function init() { + eventManager.subscribe("compositionstart", compositionStart); + eventManager.subscribe("compositionend", compositionEnd); + eventManager.subscribe("textInput", textInput); + eventManager.subscribe("keypress", flushEvent); + eventManager.subscribe("mousedown", suppressFocus); + eventManager.subscribe("mouseup", synchronizeEventStatus); + eventManager.subscribe("focus", resetWindowSelection); + filters.push(new DetectSafariCompositionError(eventManager)); + function getDestroy(filter) { + return filter.destroy + } + cleanup = filters.map(getDestroy); + compositionElement = doc.createElement("span"); + compositionElement.setAttribute("id", "composer"); + processUpdates = new core.ScheduledTask(resetWindowSelection, 1); + cleanup.push(processUpdates.destroy) + } + init() + }; + gui.InputMethodEditor.signalCompositionStart = "input/compositionstart"; + gui.InputMethodEditor.signalCompositionEnd = "input/compositionend"; + return gui.InputMethodEditor +})(); /* - 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 OperationTransformer() { - var operationFactory, operationTransformMatrix = new ops.OperationTransformMatrix; - function operations(opspecs) { - var ops = []; - opspecs.forEach(function(opspec) { - ops.push(operationFactory.create(opspec)) - }); - return ops +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 transformOpVsOp(opSpecA, opSpecB) { - return operationTransformMatrix.transformOpspecVsOpspec(opSpecA, opSpecB) + function getKeyCombo(keyCode, modifiers) { + if(!modifiers) { + modifiers = modifier.None + } + return keyCode + ":" + modifiers } - 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 + this.setDefault = function(callback) { + defaultBinding = callback + }; + this.bind = function(keyCode, modifiers, callback, overwrite) { + var keyCombo = getKeyCombo(keyCode, modifiers); + runtime.assert(overwrite || 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) } - opSpecB = transformResult.opSpecsB.pop() } - if(opSpecB) { - transformedOpspecsB.push(opSpecB) + if(handled) { + if(e.preventDefault) { + e.preventDefault() + }else { + e.returnValue = false + } } - 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) - } - return{opsA:operations(opSpecsA), opsB:operations(transformedOpspecsB)} +}; +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, Ctrl:17, 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, LeftMeta:91, MetaInMozilla:224}; +(function() { + return gui.KeyboardHandler +})(); +/* + + 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, moveCursor:true})); + cursorPosition += 1; + operations.push(createOp(new ops.OpInsertText, {memberid:inputMemberId, position:cursorPosition, text:text, moveCursor:true})); + cursorPosition += text.length + }); + operations.push(createOp(new ops.OpRemoveText, {memberid:inputMemberId, position:originalCursorPosition, length:1})); + return operations } }; /* - Copyright (C) 2012 KO GmbH + Copyright (C) 2014 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -15412,39 +14286,415 @@ ops.OperationTransformer = function OperationTransformer() { @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() { +odf.WordBoundaryFilter = function WordBoundaryFilter(odtDocument, includeWhitespace) { + var TEXT_NODE = Node.TEXT_NODE, ELEMENT_NODE = Node.ELEMENT_NODE, odfUtils = new odf.OdfUtils, punctuation = /[!-#%-*,-\/:-;?-@\[-\]_{}\u00a1\u00ab\u00b7\u00bb\u00bf;\u00b7\u055a-\u055f\u0589-\u058a\u05be\u05c0\u05c3\u05c6\u05f3-\u05f4\u0609-\u060a\u060c-\u060d\u061b\u061e-\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0964-\u0965\u0970\u0df4\u0e4f\u0e5a-\u0e5b\u0f04-\u0f12\u0f3a-\u0f3d\u0f85\u0fd0-\u0fd4\u104a-\u104f\u10fb\u1361-\u1368\u166d-\u166e\u169b-\u169c\u16eb-\u16ed\u1735-\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u180a\u1944-\u1945\u19de-\u19df\u1a1e-\u1a1f\u1b5a-\u1b60\u1c3b-\u1c3f\u1c7e-\u1c7f\u2000-\u206e\u207d-\u207e\u208d-\u208e\u3008-\u3009\u2768-\u2775\u27c5-\u27c6\u27e6-\u27ef\u2983-\u2998\u29d8-\u29db\u29fc-\u29fd\u2cf9-\u2cfc\u2cfe-\u2cff\u2e00-\u2e7e\u3000-\u303f\u30a0\u30fb\ua60d-\ua60f\ua673\ua67e\ua874-\ua877\ua8ce-\ua8cf\ua92e-\ua92f\ua95f\uaa5c-\uaa5f\ufd3e-\ufd3f\ufe10-\ufe19\ufe30-\ufe52\ufe54-\ufe61\ufe63\ufe68\ufe6a-\ufe6b\uff01-\uff03\uff05-\uff0a\uff0c-\uff0f\uff1a-\uff1b\uff1f-\uff20\uff3b-\uff3d\uff3f\uff5b\uff5d\uff5f-\uff65]|\ud800[\udd00-\udd01\udf9f\udfd0]|\ud802[\udd1f\udd3f\ude50-\ude58]|\ud809[\udc00-\udc7e]/, + spacing = /\s/, FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, FILTER_REJECT = core.PositionFilter.FilterResult.FILTER_REJECT, TRAILING = odf.WordBoundaryFilter.IncludeWhitespace.TRAILING, LEADING = odf.WordBoundaryFilter.IncludeWhitespace.LEADING, NeighborType = {NO_NEIGHBOUR:0, SPACE_CHAR:1, PUNCTUATION_CHAR:2, WORD_CHAR:3, OTHER:4}; + function findHigherNeighborNode(node, direction, nodeFilter) { + var neighboringNode = null, rootNode = odtDocument.getRootNode(), unfilteredCandidate; + while(node !== rootNode && (node !== null && neighboringNode === null)) { + unfilteredCandidate = direction < 0 ? node.previousSibling : node.nextSibling; + if(nodeFilter(unfilteredCandidate) === NodeFilter.FILTER_ACCEPT) { + neighboringNode = unfilteredCandidate + } + node = node.parentNode + } + return neighboringNode + } + function typeOfNeighbor(node, getOffset) { + var neighboringChar; + if(node === null) { + return NeighborType.NO_NEIGHBOUR + } + if(odfUtils.isCharacterElement(node)) { + return NeighborType.SPACE_CHAR + } + if(node.nodeType === TEXT_NODE || (odfUtils.isTextSpan(node) || odfUtils.isHyperlink(node))) { + neighboringChar = node.textContent.charAt(getOffset()); + if(spacing.test(neighboringChar)) { + return NeighborType.SPACE_CHAR + } + if(punctuation.test(neighboringChar)) { + return NeighborType.PUNCTUATION_CHAR + } + return NeighborType.WORD_CHAR + } + return NeighborType.OTHER + } + this.acceptPosition = function(iterator) { + var container = iterator.container(), leftNode = iterator.leftNode(), rightNode = iterator.rightNode(), getRightCharOffset = iterator.unfilteredDomOffset, getLeftCharOffset = function() { + return iterator.unfilteredDomOffset() - 1 + }, leftNeighborType, rightNeighborType; + if(container.nodeType === ELEMENT_NODE) { + if(rightNode === null) { + rightNode = findHigherNeighborNode(container, 1, iterator.getNodeFilter()) + } + if(leftNode === null) { + leftNode = findHigherNeighborNode(container, -1, iterator.getNodeFilter()) + } + } + if(container !== rightNode) { + getRightCharOffset = function() { + return 0 + } + } + if(container !== leftNode && leftNode !== null) { + getLeftCharOffset = function() { + return leftNode.textContent.length - 1 + } + } + leftNeighborType = typeOfNeighbor(leftNode, getLeftCharOffset); + rightNeighborType = typeOfNeighbor(rightNode, getRightCharOffset); + if(leftNeighborType === NeighborType.WORD_CHAR && rightNeighborType === NeighborType.WORD_CHAR || (leftNeighborType === NeighborType.PUNCTUATION_CHAR && rightNeighborType === NeighborType.PUNCTUATION_CHAR || (includeWhitespace === TRAILING && (leftNeighborType !== NeighborType.NO_NEIGHBOUR && rightNeighborType === NeighborType.SPACE_CHAR) || includeWhitespace === LEADING && (leftNeighborType === NeighborType.SPACE_CHAR && rightNeighborType !== NeighborType.NO_NEIGHBOUR)))) { + return FILTER_REJECT + } + return FILTER_ACCEPT + } +}; +odf.WordBoundaryFilter.IncludeWhitespace = {None:0, TRAILING:1, LEADING:2}; +(function() { + return odf.WordBoundaryFilter +})(); +gui.SelectionController = function SelectionController(session, inputMemberId) { + var odtDocument = session.getOdtDocument(), domUtils = new core.DomUtils, odfUtils = new odf.OdfUtils, baseFilter = odtDocument.getPositionFilter(), keyboardMovementsFilter = new core.PositionFilterChain, rootFilter = odtDocument.createRootFilter(inputMemberId), TRAILING_SPACE = odf.WordBoundaryFilter.IncludeWhitespace.TRAILING, LEADING_SPACE = odf.WordBoundaryFilter.IncludeWhitespace.LEADING; + function createKeyboardStepIterator() { + var cursor = odtDocument.getCursor(inputMemberId), node = cursor.getNode(); + return odtDocument.createStepIterator(node, 0, [baseFilter, rootFilter], odtDocument.getRootElement(node)) + } + function createWordBoundaryStepIterator(node, offset, includeWhitespace) { + var wordBoundaryFilter = new odf.WordBoundaryFilter(odtDocument, includeWhitespace); + return odtDocument.createStepIterator(node, offset, [baseFilter, rootFilter, wordBoundaryFilter], odtDocument.getRootElement(node)) + } + function constrain(lookup) { + return function(originalNode) { + var originalContainer = lookup(originalNode); + return function(step, node) { + return lookup(node) === originalContainer + } + } + } + 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} + } + this.selectionToRange = selectionToRange; + 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} + } + this.rangeToSelection = rangeToSelection; + function createOpMoveCursor(position, length, selectionType) { + var op = new ops.OpMoveCursor; + op.init({memberid:inputMemberId, position:position, length:length || 0, selectionType:selectionType}); + return op + } + function selectImage(frameNode) { + var frameRoot = odtDocument.getRootElement(frameNode), frameRootFilter = odtDocument.createRootFilter(frameRoot), stepIterator = odtDocument.createStepIterator(frameNode, 0, [frameRootFilter, odtDocument.getPositionFilter()], frameRoot), anchorNode, anchorOffset, newSelection, op; + if(!stepIterator.roundToPreviousStep()) { + runtime.assert(false, "No walkable position before frame") + } + anchorNode = stepIterator.container(); + anchorOffset = stepIterator.offset(); + stepIterator.setPosition(frameNode, frameNode.childNodes.length); + if(!stepIterator.roundToNextStep()) { + runtime.assert(false, "No walkable position after frame") + } + newSelection = odtDocument.convertDomToCursorRange({anchorNode:anchorNode, anchorOffset:anchorOffset, focusNode:stepIterator.container(), focusOffset:stepIterator.offset()}); + op = createOpMoveCursor(newSelection.position, newSelection.length, ops.OdtCursor.RegionSelection); + session.enqueue([op]) + } + this.selectImage = selectImage; + function expandToWordBoundaries(range) { + var stepIterator; + stepIterator = createWordBoundaryStepIterator((range.startContainer), range.startOffset, TRAILING_SPACE); + if(stepIterator.roundToPreviousStep()) { + range.setStart(stepIterator.container(), stepIterator.offset()) + } + stepIterator = createWordBoundaryStepIterator((range.endContainer), range.endOffset, LEADING_SPACE); + if(stepIterator.roundToNextStep()) { + range.setEnd(stepIterator.container(), stepIterator.offset()) + } + } + this.expandToWordBoundaries = expandToWordBoundaries; + function expandToParagraphBoundaries(range) { + var paragraphs = odfUtils.getParagraphElements(range), startParagraph = paragraphs[0], endParagraph = paragraphs[paragraphs.length - 1]; + 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) + } + } + } + this.expandToParagraphBoundaries = expandToParagraphBoundaries; + 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 + } + if(startInsideCanvas && endInsideCanvas) { + if(clickCount === 2) { + expandToWordBoundaries(range) + }else { + if(clickCount >= 3) { + expandToParagraphBoundaries(range) + } + } + } + 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]) + } + } + this.selectRange = selectRange; + function extendCursorByAdjustment(lengthAdjust) { + var selection = odtDocument.getCursorSelection(inputMemberId), stepCounter = odtDocument.getCursor(inputMemberId).getStepCounter(), newLength; + if(lengthAdjust !== 0) { + if(lengthAdjust > 0) { + lengthAdjust = stepCounter.convertForwardStepsBetweenFilters(lengthAdjust, keyboardMovementsFilter, baseFilter) + }else { + lengthAdjust = -stepCounter.convertBackwardStepsBetweenFilters(-lengthAdjust, keyboardMovementsFilter, baseFilter) + } + newLength = selection.length + lengthAdjust; + session.enqueue([createOpMoveCursor(selection.position, newLength)]) + } + } + function extendSelection(advanceIterator) { + var stepIterator = createKeyboardStepIterator(), anchorNode = odtDocument.getCursor(inputMemberId).getAnchorNode(), newSelection; + if(advanceIterator(stepIterator)) { + newSelection = odtDocument.convertDomToCursorRange({anchorNode:anchorNode, anchorOffset:0, focusNode:stepIterator.container(), focusOffset:stepIterator.offset()}); + session.enqueue([createOpMoveCursor(newSelection.position, newSelection.length)]) + } + } + 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 moveCursor(advanceIterator) { + var stepIterator = createKeyboardStepIterator(), position; + if(advanceIterator(stepIterator)) { + position = odtDocument.convertDomPointToCursorStep(stepIterator.container(), stepIterator.offset()); + session.enqueue([createOpMoveCursor(position, 0)]) + } + } + function moveCursorToLeft() { + moveCursor(function(iterator) { + return iterator.previousStep() + }); + return true + } + this.moveCursorToLeft = moveCursorToLeft; + function moveCursorToRight() { + moveCursor(function(iterator) { + return iterator.nextStep() + }); + return true + } + this.moveCursorToRight = moveCursorToRight; + function extendSelectionToLeft() { + extendSelection(function(iterator) { + return iterator.previousStep() + }); + return true + } + this.extendSelectionToLeft = extendSelectionToLeft; + function extendSelectionToRight() { + extendSelection(function(iterator) { + return iterator.nextStep() + }); + return true + } + this.extendSelectionToRight = extendSelectionToRight; + function moveCursorByLine(direction, extend) { + var paragraphNode = odtDocument.getParagraphElement(odtDocument.getCursor(inputMemberId).getNode()), steps; + runtime.assert(Boolean(paragraphNode), "SelectionController: Cursor outside paragraph"); + steps = odtDocument.getCursor(inputMemberId).getStepCounter().countLinesSteps(direction, keyboardMovementsFilter); + if(extend) { + extendCursorByAdjustment(steps) + }else { + moveCursorByAdjustment(steps) + } + } + function moveCursorUp() { + moveCursorByLine(-1, false); + return true + } + this.moveCursorUp = moveCursorUp; + function moveCursorDown() { + moveCursorByLine(1, false); + return true + } + this.moveCursorDown = moveCursorDown; + function extendSelectionUp() { + moveCursorByLine(-1, true); + return true + } + this.extendSelectionUp = extendSelectionUp; + function extendSelectionDown() { + moveCursorByLine(1, true); + return true + } + this.extendSelectionDown = extendSelectionDown; + function moveCursorToLineBoundary(direction, extend) { + var steps = odtDocument.getCursor(inputMemberId).getStepCounter().countStepsToLineBoundary(direction, keyboardMovementsFilter); + if(extend) { + extendCursorByAdjustment(steps) + }else { + moveCursorByAdjustment(steps) + } + } + function moveCursorByWord(direction, extend) { + var cursor = odtDocument.getCursor(inputMemberId), newSelection = rangeToSelection(cursor.getSelectedRange(), cursor.hasForwardSelection()), newCursorSelection, selectionUpdated, stepIterator = createWordBoundaryStepIterator(newSelection.focusNode, newSelection.focusOffset, TRAILING_SPACE); + if(direction >= 0) { + selectionUpdated = stepIterator.nextStep() + }else { + selectionUpdated = stepIterator.previousStep() + } + if(selectionUpdated) { + newSelection.focusNode = stepIterator.container(); + newSelection.focusOffset = stepIterator.offset(); + if(!extend) { + newSelection.anchorNode = newSelection.focusNode; + newSelection.anchorOffset = newSelection.focusOffset + } + newCursorSelection = odtDocument.convertDomToCursorRange(newSelection); + session.enqueue([createOpMoveCursor(newCursorSelection.position, newCursorSelection.length)]) + } + } + function moveCursorBeforeWord() { + moveCursorByWord(-1, false); + return true + } + this.moveCursorBeforeWord = moveCursorBeforeWord; + function moveCursorPastWord() { + moveCursorByWord(1, false); + return true + } + this.moveCursorPastWord = moveCursorPastWord; + function extendSelectionBeforeWord() { + moveCursorByWord(-1, true); + return true + } + this.extendSelectionBeforeWord = extendSelectionBeforeWord; + function extendSelectionPastWord() { + moveCursorByWord(1, true); + return true + } + this.extendSelectionPastWord = extendSelectionPastWord; + function moveCursorToLineStart() { + moveCursorToLineBoundary(-1, false); + return true + } + this.moveCursorToLineStart = moveCursorToLineStart; + function moveCursorToLineEnd() { + moveCursorToLineBoundary(1, false); + return true + } + this.moveCursorToLineEnd = moveCursorToLineEnd; + function extendSelectionToLineStart() { + moveCursorToLineBoundary(-1, true); + return true + } + this.extendSelectionToLineStart = extendSelectionToLineStart; + function extendSelectionToLineEnd() { + moveCursorToLineBoundary(1, true); + return true + } + this.extendSelectionToLineEnd = extendSelectionToLineEnd; + function extendCursorToNodeBoundary(direction, getContainmentNode) { + var cursor = odtDocument.getCursor(inputMemberId), node = getContainmentNode(cursor.getNode()), selection = rangeToSelection(cursor.getSelectedRange(), cursor.hasForwardSelection()), newCursorSelection; + runtime.assert(Boolean(node), "SelectionController: Cursor outside root"); + if(direction < 0) { + selection.focusNode = (node); + selection.focusOffset = 0 + }else { + selection.focusNode = (node); + selection.focusOffset = node.childNodes.length + } + newCursorSelection = odtDocument.convertDomToCursorRange(selection, constrain(getContainmentNode)); + session.enqueue([createOpMoveCursor(newCursorSelection.position, newCursorSelection.length)]) + } + function extendSelectionToParagraphStart() { + extendCursorToNodeBoundary(-1, odtDocument.getParagraphElement); + return true + } + this.extendSelectionToParagraphStart = extendSelectionToParagraphStart; + function extendSelectionToParagraphEnd() { + extendCursorToNodeBoundary(1, odtDocument.getParagraphElement); + return true + } + this.extendSelectionToParagraphEnd = extendSelectionToParagraphEnd; + function moveCursorToRootBoundary(direction) { + var cursor = odtDocument.getCursor(inputMemberId), root = odtDocument.getRootElement(cursor.getNode()), newPosition; + runtime.assert(Boolean(root), "SelectionController: Cursor outside root"); + if(direction < 0) { + newPosition = odtDocument.convertDomPointToCursorStep(root, 0, function(step) { + return step === ops.StepsTranslator.NEXT_STEP + }) + }else { + newPosition = odtDocument.convertDomPointToCursorStep(root, root.childNodes.length) + } + session.enqueue([createOpMoveCursor(newPosition, 0)]); + return true + } + function moveCursorToDocumentStart() { + moveCursorToRootBoundary(-1); + return true + } + this.moveCursorToDocumentStart = moveCursorToDocumentStart; + function moveCursorToDocumentEnd() { + moveCursorToRootBoundary(1); + return true + } + this.moveCursorToDocumentEnd = moveCursorToDocumentEnd; + function extendSelectionToDocumentStart() { + extendCursorToNodeBoundary(-1, odtDocument.getRootElement); + return true + } + this.extendSelectionToDocumentStart = extendSelectionToDocumentStart; + function extendSelectionToDocumentEnd() { + extendCursorToNodeBoundary(1, odtDocument.getRootElement); + return true + } + this.extendSelectionToDocumentEnd = extendSelectionToDocumentEnd; + function extendSelectionToEntireDocument() { + var cursor = odtDocument.getCursor(inputMemberId), root = odtDocument.getRootElement(cursor.getNode()), newSelection, newCursorSelection; + runtime.assert(Boolean(root), "SelectionController: Cursor outside root"); + newSelection = {anchorNode:root, anchorOffset:0, focusNode:root, focusOffset:root.childNodes.length}; + newCursorSelection = odtDocument.convertDomToCursorRange(newSelection, constrain(odtDocument.getRootElement)); + session.enqueue([createOpMoveCursor(newCursorSelection.position, newCursorSelection.length)]); return true } + this.extendSelectionToEntireDocument = extendSelectionToEntireDocument; + function init() { + keyboardMovementsFilter.addFilter(baseFilter); + keyboardMovementsFilter.addFilter(odtDocument.createRootFilter(inputMemberId)) + } + 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 @@ -15479,100 +14729,107 @@ ops.TrivialOperationRouter = function TrivialOperationRouter() { @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) +gui.TextController = function TextController(session, inputMemberId, directStyleOp, paragraphStyleOps) { + 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 setLastAuthor(memberid) { - marker.setAttributeNS(editinfons, "editinfo:memberid", memberid) + function toForwardSelection(selection) { + if(selection.length < 0) { + selection.position += selection.length; + selection.length = -selection.length + } + return selection } - this.addEdit = function(memberid, timestamp) { - var age = Date.now() - timestamp; - editInfo.addEdit(memberid, timestamp); - handle.setEdits(editInfo.getSortedEdits()); - setLastAuthor(memberid); - if(decay1) { - deleteDecay(decay1) + this.enqueueParagraphSplittingOps = function() { + var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op, operations = [], styleOps; + if(selection.length > 0) { + op = createOpRemoveSelection(selection); + operations.push(op) } - if(decay2) { - deleteDecay(decay2) + op = new ops.OpSplitParagraph; + op.init({memberid:inputMemberId, position:selection.position, moveCursor:true}); + operations.push(op); + if(paragraphStyleOps) { + styleOps = paragraphStyleOps(selection.position + 1); + operations = operations.concat(styleOps) } - 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) + 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(odtDocument.getPositionFilter()); + rootConstrainedFilter.addFilter(odtDocument.createRootFilter(inputMemberId)); + iterator.setUnfilteredPosition(cursorNode, 0); + while(nextPosition()) { + if(rootConstrainedFilter.acceptPosition(iterator) === FILTER_ACCEPT) { + return true } } - }; - this.getEdits = function() { - return editInfo.getEdits() - }; - this.clearEdits = function() { - editInfo.clearEdits(); - handle.setEdits([]); - if(marker.hasAttributeNS(editinfons, "editinfo:memberid")) { - marker.removeAttributeNS(editinfons, "editinfo:memberid") + 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.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.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.hideHandle = function() { - handle.hide() + this.removeCurrentSelection = function() { + var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op; + if(selection.length !== 0) { + op = createOpRemoveSelection(selection); + session.enqueue([op]) + } + return true }; - this.destroy = function(callback) { - editInfoNode.removeChild(marker); - handle.destroy(function(err) { - if(err) { - callback(err) - }else { - editInfo.destroy(callback) + function insertText(text) { + var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op, stylingOp, operations = [], useCachedStyle = false; + if(selection.length > 0) { + op = createOpRemoveSelection(selection); + operations.push(op); + useCachedStyle = true + } + op = new ops.OpInsertText; + op.init({memberid:inputMemberId, position:selection.position, text:text, moveCursor:true}); + operations.push(op); + if(directStyleOp) { + stylingOp = directStyleOp(selection.position, text.length, useCachedStyle); + if(stylingOp) { + operations.push(stylingOp) } - }) - }; - 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() } + session.enqueue(operations) } - init() + this.insertText = insertText }; +(function() { + return gui.TextController +})(); /* Copyright (C) 2013 KO GmbH @@ -15610,321 +14867,630 @@ gui.EditInfoMarker = function EditInfoMarker(editInfo, initialVisibility) { @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, moveCursor:true})); - cursorPosition += 1; - operations.push(createOp(new ops.OpInsertText, {memberid:inputMemberId, position:cursorPosition, text:text, moveCursor:true})); - cursorPosition += text.length - }); - operations.push(createOp(new ops.OpRemoveText, {memberid:inputMemberId, position:originalCursorPosition, length:1})); - return operations - } +gui.UndoManager = function UndoManager() { }; -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) +gui.UndoManager.prototype.subscribe = function(signal, callback) { +}; +gui.UndoManager.prototype.unsubscribe = function(signal, callback) { +}; +gui.UndoManager.prototype.setDocument = function(newDocument) { +}; +gui.UndoManager.prototype.setInitialState = function() { +}; +gui.UndoManager.prototype.initialize = function() { +}; +gui.UndoManager.prototype.purgeInitialState = 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 +})(); +(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, mimeDataExporter = new gui.MimeDataExporter, clipboard = new gui.Clipboard(mimeDataExporter), keyDownHandler = new gui.KeyboardHandler, keyPressHandler = new gui.KeyboardHandler, keyUpHandler = new gui.KeyboardHandler, clickStartedWithinCanvas = false, objectNameGenerator = new odf.ObjectNameGenerator(odtDocument.getOdfCanvas().odfContainer(), + inputMemberId), isMouseMoved = false, mouseDownRootFilter = null, handleMouseClickTimeoutId, undoManager = null, eventManager = new gui.EventManager(odtDocument), annotationController = new gui.AnnotationController(session, inputMemberId), directFormattingController = new gui.DirectFormattingController(session, inputMemberId, objectNameGenerator, args.directParagraphStylingEnabled), createCursorStyleOp = (directFormattingController.createCursorStyleOp), createParagraphStyleOps = (directFormattingController.createParagraphStyleOps), + textController = new gui.TextController(session, inputMemberId, createCursorStyleOp, createParagraphStyleOps), imageController = new gui.ImageController(session, inputMemberId, objectNameGenerator), imageSelector = new gui.ImageSelector(odtDocument.getOdfCanvas()), shadowCursorIterator = gui.SelectionMover.createPositionIterator(odtDocument.getRootNode()), drawShadowCursorTask, redrawRegionSelectionTask, pasteHandler = new gui.PlainTextPasteboard(odtDocument, inputMemberId), inputMethodEditor = + new gui.InputMethodEditor(inputMemberId, eventManager), clickCount = 0, hyperlinkClickHandler = new gui.HyperlinkClickHandler(odtDocument.getRootNode), hyperlinkController = new gui.HyperlinkController(session, inputMemberId), selectionController = new gui.SelectionController(session, inputMemberId), modifier = gui.KeyboardHandler.Modifier, keyCode = gui.KeyboardHandler.KeyCode, isMacOS = window.navigator.appVersion.toLowerCase().indexOf("mac") !== -1, isIOS = ["iPad", "iPod", "iPhone"].indexOf(window.navigator.platform) !== + -1, iOSSafariSupport; + runtime.assert(window !== null, "Expected to be run in an environment which has a global window, like a browser."); + function getTarget(e) { + return(e.target) || (e.srcElement || null) + } + function cancelEvent(event) { + if(event.preventDefault) { + event.preventDefault() + }else { + event.returnValue = false + } + } + function caretPositionFromPoint(x, y) { + var doc = odtDocument.getDOMDocument(), 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 + } + function redrawRegionSelection() { + var cursor = odtDocument.getCursor(inputMemberId), imageElement; + if(cursor && cursor.getSelectionType() === ops.OdtCursor.RegionSelection) { + imageElement = odfUtils.getImageElements(cursor.getSelectedRange())[0]; + if(imageElement) { + imageSelector.select((imageElement.parentNode)); + return + } + } + imageSelector.clearSelection() + } + 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 + } + function handleCut(e) { + var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); + if(selectedRange.collapsed) { + e.preventDefault(); + return + } + if(clipboard.setDataFromRange(e, selectedRange)) { + textController.removeCurrentSelection() + }else { + runtime.log("Cut operation failed") + } + } + function handleBeforeCut() { + var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); + return selectedRange.collapsed !== false + } + function handleCopy(e) { + var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); + if(selectedRange.collapsed) { + e.preventDefault(); + return + } + if(!clipboard.setDataFromRange(e, selectedRange)) { + runtime.log("Copy operation failed") + } + } + function handlePaste(e) { + var plainText; + if(window.clipboardData && window.clipboardData.getData) { + plainText = window.clipboardData.getData("Text") + }else { + if(e.clipboardData && e.clipboardData.getData) { + plainText = e.clipboardData.getData("text/plain") + } + } + if(plainText) { + textController.removeCurrentSelection(); + session.enqueue(pasteHandler.createPasteOps(plainText)) + } + cancelEvent(e) + } + function handleBeforePaste() { + return false + } + function updateUndoStack(op) { + if(undoManager) { + undoManager.onOperationExecuted(op) + } + } + function forwardUndoStackChange(e) { + odtDocument.emit(ops.OdtDocument.signalUndoStackChanged, e) + } + function undo() { + var eventTrap = eventManager.getEventTrap(), sizer, hadFocusBefore; + if(undoManager) { + hadFocusBefore = eventManager.hasFocus(); + undoManager.moveBackward(1); + sizer = odtDocument.getOdfCanvas().getSizer(); + if(!domUtils.containsNode(sizer, eventTrap)) { + sizer.appendChild(eventTrap); + if(hadFocusBefore) { + eventManager.focus() + } + } + return true + } + return false } - } - 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 + this.undo = undo; + function redo() { + var hadFocusBefore; + if(undoManager) { + hadFocusBefore = eventManager.hasFocus(); + undoManager.moveForward(1); + if(hadFocusBefore) { + eventManager.focus() + } + return true + } + return false + } + this.redo = redo; + function updateShadowCursor() { + var selection = window.getSelection(), selectionRange = selection.rangeCount > 0 && selectionController.selectionToRange(selection); + if(clickStartedWithinCanvas && selectionRange) { + isMouseMoved = true; + imageSelector.clearSelection(); + shadowCursorIterator.setUnfilteredPosition((selection.focusNode), selection.focusOffset); + if(mouseDownRootFilter.acceptPosition(shadowCursorIterator) === FILTER_ACCEPT) { + if(clickCount === 2) { + selectionController.expandToWordBoundaries(selectionRange.range) + }else { + if(clickCount >= 3) { + selectionController.expandToParagraphBoundaries(selectionRange.range) + } + } + shadowCursor.setSelectedRange(selectionRange.range, selectionRange.hasForwardSelection); + odtDocument.emit(ops.Document.signalCursorMoved, shadowCursor) + } + } + } + function synchronizeWindowSelection(cursor) { + var selection = window.getSelection(), 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 { - if(node.nodeType === Node.TEXT_NODE && startOffset > 0) { - startOffset -= 1 + selection.removeAllRanges(); + selection.addRange(range.cloneRange()) + } + } + function handleMouseDown(e) { + var target = getTarget(e), cursor = odtDocument.getCursor(inputMemberId); + clickStartedWithinCanvas = target !== null && domUtils.containsNode(odtDocument.getOdfCanvas().getElement(), target); + if(clickStartedWithinCanvas) { + isMouseMoved = false; + mouseDownRootFilter = odtDocument.createRootFilter((target)); + clickCount = e.detail; + if(cursor && e.shiftKey) { + window.getSelection().collapse(cursor.getAnchorNode(), 0) }else { - if(nodes[nextNodeIndex]) { - node = nodes[nextNodeIndex]; - nextNodeIndex -= 1; - startOffset = endOffset = node.length || node.childNodes.length - }else { - return false + synchronizeWindowSelection(cursor) + } + if(clickCount > 1) { + updateShadowCursor() + } + } + } + function mutableSelection(selection) { + if(selection) { + return{anchorNode:selection.anchorNode, anchorOffset:selection.anchorOffset, focusNode:selection.focusNode, focusOffset:selection.focusOffset} + } + return null + } + function getNextWalkablePosition(node) { + var root = odtDocument.getRootElement(node), rootFilter = odtDocument.createRootFilter(root), stepIterator = odtDocument.createStepIterator(node, 0, [rootFilter, odtDocument.getPositionFilter()], root); + stepIterator.setPosition(node, node.childNodes.length); + if(!stepIterator.roundToNextStep()) { + return null + } + return{container:stepIterator.container(), offset:stepIterator.offset()} + } + function moveByMouseClickEvent(event) { + var selection = mutableSelection(window.getSelection()), position, selectionRange, rect, frameNode; + if(!selection.anchorNode && !selection.focusNode) { + position = caretPositionFromPoint(event.clientX, event.clientY); + if(position) { + selection.anchorNode = (position.container); + selection.anchorOffset = position.offset; + selection.focusNode = selection.anchorNode; + selection.focusOffset = selection.anchorOffset + } + } + if(odfUtils.isImage(selection.focusNode) && (selection.focusOffset === 0 && odfUtils.isCharacterFrame(selection.focusNode.parentNode))) { + frameNode = (selection.focusNode.parentNode); + rect = frameNode.getBoundingClientRect(); + if(event.clientX > rect.right) { + position = getNextWalkablePosition(frameNode); + if(position) { + selection.anchorNode = selection.focusNode = position.container; + selection.anchorOffset = selection.focusOffset = position.offset + } + } + }else { + if(odfUtils.isImage(selection.focusNode.firstChild) && (selection.focusOffset === 1 && odfUtils.isCharacterFrame(selection.focusNode))) { + position = getNextWalkablePosition(selection.focusNode); + if(position) { + selection.anchorNode = selection.focusNode = position.container; + selection.anchorOffset = selection.focusOffset = position.offset } } } - range.setStart(node, startOffset); - range.setEnd(node, endOffset) + if(selection.anchorNode && selection.focusNode) { + selectionRange = selectionController.selectionToRange(selection); + selectionController.selectRange(selectionRange.range, selectionRange.hasForwardSelection, event.detail) + } + eventManager.focus() } - 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 + function handleMouseClickEvent(event) { + var target = getTarget(event), range, wasCollapsed, frameNode, pos; + drawShadowCursorTask.processRequests(); + if(odfUtils.isImage(target) && (odfUtils.isCharacterFrame(target.parentNode) && window.getSelection().isCollapsed)) { + selectionController.selectImage((target.parentNode)); + eventManager.focus() }else { - if(node.nodeType === Node.TEXT_NODE && endOffset < node.length) { - endOffset += 1 + if(imageSelector.isSelectorElement(target)) { + eventManager.focus() }else { - if(nodes[nextNodeIndex]) { - node = nodes[nextNodeIndex]; - nextNodeIndex += 1; - startOffset = endOffset = 0 - }else { - return false + if(clickStartedWithinCanvas) { + if(isMouseMoved) { + range = shadowCursor.getSelectedRange(); + wasCollapsed = range.collapsed; + if(odfUtils.isImage(range.endContainer) && (range.endOffset === 0 && odfUtils.isCharacterFrame(range.endContainer.parentNode))) { + frameNode = (range.endContainer.parentNode); + pos = getNextWalkablePosition(frameNode); + if(pos) { + range.setEnd(pos.container, pos.offset); + if(wasCollapsed) { + range.collapse(false) + } + } + } + selectionController.selectRange(range, shadowCursor.hasForwardSelection(), event.detail); + eventManager.focus() + }else { + if(isIOS) { + moveByMouseClickEvent(event) + }else { + handleMouseClickTimeoutId = runtime.setTimeout(function() { + moveByMouseClickEvent(event) + }, 0) + } + } } } } - range.setStart(node, startOffset); - range.setEnd(node, endOffset) + clickCount = 0; + clickStartedWithinCanvas = false; + isMouseMoved = false } - 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 handleDragStart(e) { + var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); + if(selectedRange.collapsed) { + return + } + mimeDataExporter.exportRangeToDataTransfer((e.dataTransfer), selectedRange) } - if(!firstVisibleRect(firstRange, nodes)) { - return null + function handleDragEnd() { + if(clickStartedWithinCanvas) { + eventManager.focus() + } + clickCount = 0; + clickStartedWithinCanvas = false; + isMouseMoved = false } - if(!lastVisibleRect(lastRange, nodes)) { - return null + function handleContextMenu(e) { + handleMouseClickEvent(e) } - 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 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); + eventManager.focus() }else { - originalRect = getBoundingRect(originalRect, newRect) + 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 + function insertNonEmptyData(e) { + var input = e.data; + if(input) { + textController.insertText(input) } - return FILTER_REJECT } - function getRectFromNodeAfterFiltering(node) { - var rect = null; - if(acceptNode(node) === FILTER_ACCEPT) { - rect = domUtils.getBoundingClientRect(node) + function returnTrue(fn) { + return function() { + fn(); + return true } - 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 + function rangeSelectionOnly(fn) { + return function(e) { + var selectionType = odtDocument.getCursor(inputMemberId).getSelectionType(); + if(selectionType === ops.OdtCursor.RangeSelection) { + return fn(e) + } + return true + } } - lastSibling = lastNode; - while(lastSibling.parentNode !== containerNode) { - lastSibling = lastSibling.parentNode + function insertLocalCursor() { + runtime.assert(session.getOdtDocument().getCursor(inputMemberId) === undefined, "Inserting local cursor a second time."); + var op = new ops.OpAddCursor; + op.init({memberid:inputMemberId}); + session.enqueue([op]); + eventManager.focus() } - rootFilter = odtDocument.createRootFilter(firstNode); - currentNode = firstSibling.nextSibling; - while(currentNode && currentNode !== lastSibling) { - currentRect = getRectFromNodeAfterFiltering(currentNode); - grownRect = checkAndGrowOrCreateRect(grownRect, currentRect); - currentNode = currentNode.nextSibling + this.insertLocalCursor = insertLocalCursor; + function removeLocalCursor() { + runtime.assert(session.getOdtDocument().getCursor(inputMemberId) !== undefined, "Removing local cursor without inserting before."); + var op = new ops.OpRemoveCursor; + op.init({memberid:inputMemberId}); + session.enqueue([op]) } - 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) + this.removeLocalCursor = removeLocalCursor; + this.startEditing = function() { + inputMethodEditor.subscribe(gui.InputMethodEditor.signalCompositionStart, textController.removeCurrentSelection); + inputMethodEditor.subscribe(gui.InputMethodEditor.signalCompositionEnd, insertNonEmptyData); + eventManager.subscribe("beforecut", handleBeforeCut); + eventManager.subscribe("cut", handleCut); + eventManager.subscribe("beforepaste", handleBeforePaste); + eventManager.subscribe("paste", handlePaste); + window.addEventListener("focus", hyperlinkClickHandler.showTextCursor, false); + if(undoManager) { + undoManager.initialize() + } + inputMethodEditor.setEditing(true); + hyperlinkClickHandler.setModifier(isMacOS ? gui.HyperlinkClickHandler.Modifier.Meta : gui.HyperlinkClickHandler.Modifier.Ctrl); + keyDownHandler.bind(keyCode.Backspace, modifier.None, returnTrue(textController.removeTextByBackspaceKey), true); + keyDownHandler.bind(keyCode.Delete, modifier.None, textController.removeTextByDeleteKey); + keyDownHandler.bind(keyCode.Tab, modifier.None, rangeSelectionOnly(function() { + textController.insertText("\t"); + return true + })); + if(isMacOS) { + keyDownHandler.bind(keyCode.Clear, modifier.None, textController.removeCurrentSelection); + keyDownHandler.bind(keyCode.B, modifier.Meta, rangeSelectionOnly(directFormattingController.toggleBold)); + keyDownHandler.bind(keyCode.I, modifier.Meta, rangeSelectionOnly(directFormattingController.toggleItalic)); + keyDownHandler.bind(keyCode.U, modifier.Meta, rangeSelectionOnly(directFormattingController.toggleUnderline)); + keyDownHandler.bind(keyCode.L, modifier.MetaShift, rangeSelectionOnly(directFormattingController.alignParagraphLeft)); + keyDownHandler.bind(keyCode.E, modifier.MetaShift, rangeSelectionOnly(directFormattingController.alignParagraphCenter)); + keyDownHandler.bind(keyCode.R, modifier.MetaShift, rangeSelectionOnly(directFormattingController.alignParagraphRight)); + keyDownHandler.bind(keyCode.J, modifier.MetaShift, rangeSelectionOnly(directFormattingController.alignParagraphJustified)); + keyDownHandler.bind(keyCode.C, modifier.MetaShift, annotationController.addAnnotation); + keyDownHandler.bind(keyCode.Z, modifier.Meta, undo); + keyDownHandler.bind(keyCode.Z, modifier.MetaShift, redo); + keyDownHandler.bind(keyCode.LeftMeta, modifier.Meta, hyperlinkClickHandler.showPointerCursor); + keyDownHandler.bind(keyCode.MetaInMozilla, modifier.Meta, hyperlinkClickHandler.showPointerCursor); + keyUpHandler.bind(keyCode.LeftMeta, modifier.None, hyperlinkClickHandler.showTextCursor); + keyUpHandler.bind(keyCode.MetaInMozilla, modifier.None, hyperlinkClickHandler.showTextCursor) }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() + keyDownHandler.bind(keyCode.B, modifier.Ctrl, rangeSelectionOnly(directFormattingController.toggleBold)); + keyDownHandler.bind(keyCode.I, modifier.Ctrl, rangeSelectionOnly(directFormattingController.toggleItalic)); + keyDownHandler.bind(keyCode.U, modifier.Ctrl, rangeSelectionOnly(directFormattingController.toggleUnderline)); + keyDownHandler.bind(keyCode.L, modifier.CtrlShift, rangeSelectionOnly(directFormattingController.alignParagraphLeft)); + keyDownHandler.bind(keyCode.E, modifier.CtrlShift, rangeSelectionOnly(directFormattingController.alignParagraphCenter)); + keyDownHandler.bind(keyCode.R, modifier.CtrlShift, rangeSelectionOnly(directFormattingController.alignParagraphRight)); + keyDownHandler.bind(keyCode.J, modifier.CtrlShift, rangeSelectionOnly(directFormattingController.alignParagraphJustified)); + keyDownHandler.bind(keyCode.C, modifier.CtrlAlt, annotationController.addAnnotation); + keyDownHandler.bind(keyCode.Z, modifier.Ctrl, undo); + keyDownHandler.bind(keyCode.Z, modifier.CtrlShift, redo); + keyDownHandler.bind(keyCode.Ctrl, modifier.Ctrl, hyperlinkClickHandler.showPointerCursor); + keyUpHandler.bind(keyCode.Ctrl, modifier.None, hyperlinkClickHandler.showTextCursor) + } + function handler(e) { + var text = stringFromKeyPress(e); + if(text && !(e.altKey || (e.ctrlKey || e.metaKey))) { + textController.insertText(text); + return true } + return false } - } - 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) + keyPressHandler.setDefault(rangeSelectionOnly(handler)); + keyPressHandler.bind(keyCode.Enter, modifier.None, rangeSelectionOnly(textController.enqueueParagraphSplittingOps)) + }; + this.endEditing = function() { + inputMethodEditor.unsubscribe(gui.InputMethodEditor.signalCompositionStart, textController.removeCurrentSelection); + inputMethodEditor.unsubscribe(gui.InputMethodEditor.signalCompositionEnd, insertNonEmptyData); + eventManager.unsubscribe("cut", handleCut); + eventManager.unsubscribe("beforecut", handleBeforeCut); + eventManager.unsubscribe("paste", handlePaste); + eventManager.unsubscribe("beforepaste", handleBeforePaste); + window.removeEventListener("focus", hyperlinkClickHandler.showTextCursor, false); + inputMethodEditor.setEditing(false); + hyperlinkClickHandler.setModifier(gui.HyperlinkClickHandler.Modifier.None); + keyDownHandler.bind(keyCode.Backspace, modifier.None, function() { + return true + }, true); + keyDownHandler.unbind(keyCode.Delete, modifier.None); + keyDownHandler.unbind(keyCode.Tab, modifier.None); + if(isMacOS) { + keyDownHandler.unbind(keyCode.Clear, modifier.None); + keyDownHandler.unbind(keyCode.B, modifier.Meta); + keyDownHandler.unbind(keyCode.I, modifier.Meta); + keyDownHandler.unbind(keyCode.U, modifier.Meta); + keyDownHandler.unbind(keyCode.L, modifier.MetaShift); + keyDownHandler.unbind(keyCode.E, modifier.MetaShift); + keyDownHandler.unbind(keyCode.R, modifier.MetaShift); + keyDownHandler.unbind(keyCode.J, modifier.MetaShift); + keyDownHandler.unbind(keyCode.C, modifier.MetaShift); + keyDownHandler.unbind(keyCode.Z, modifier.Meta); + keyDownHandler.unbind(keyCode.Z, modifier.MetaShift); + keyDownHandler.unbind(keyCode.LeftMeta, modifier.Meta); + keyDownHandler.unbind(keyCode.MetaInMozilla, modifier.Meta); + keyUpHandler.unbind(keyCode.LeftMeta, modifier.None); + keyUpHandler.unbind(keyCode.MetaInMozilla, modifier.None) }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 - } - } + keyDownHandler.unbind(keyCode.B, modifier.Ctrl); + keyDownHandler.unbind(keyCode.I, modifier.Ctrl); + keyDownHandler.unbind(keyCode.U, modifier.Ctrl); + keyDownHandler.unbind(keyCode.L, modifier.CtrlShift); + keyDownHandler.unbind(keyCode.E, modifier.CtrlShift); + keyDownHandler.unbind(keyCode.R, modifier.CtrlShift); + keyDownHandler.unbind(keyCode.J, modifier.CtrlShift); + keyDownHandler.unbind(keyCode.C, modifier.CtrlAlt); + keyDownHandler.unbind(keyCode.Z, modifier.Ctrl); + keyDownHandler.unbind(keyCode.Z, modifier.CtrlShift); + keyDownHandler.unbind(keyCode.Ctrl, modifier.Ctrl); + keyUpHandler.unbind(keyCode.Ctrl, modifier.None) + } + keyPressHandler.setDefault(null); + keyPressHandler.unbind(keyCode.Enter, modifier.None) + }; + 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.setDocument(odtDocument); + undoManager.setPlaybackFunction(session.enqueue); + undoManager.subscribe(gui.UndoManager.signalUndoStackChanged, forwardUndoStackChange) } + }; + this.getUndoManager = function() { + return undoManager + }; + this.getAnnotationController = function() { + return annotationController + }; + this.getDirectFormattingController = function() { + return directFormattingController + }; + this.getHyperlinkController = function() { + return hyperlinkController + }; + this.getImageController = function() { + return imageController + }; + this.getSelectionController = function() { + return selectionController + }; + this.getTextController = function() { + return textController + }; + this.getEventManager = function() { + return eventManager + }; + this.getKeyboardHandlers = function() { + return{keydown:keyDownHandler, keypress:keyPressHandler} + }; + function destroy(callback) { + eventManager.unsubscribe("keydown", keyDownHandler.handleEvent); + eventManager.unsubscribe("keypress", keyPressHandler.handleEvent); + eventManager.unsubscribe("keyup", keyUpHandler.handleEvent); + eventManager.unsubscribe("copy", handleCopy); + eventManager.unsubscribe("mousedown", handleMouseDown); + eventManager.unsubscribe("mousemove", drawShadowCursorTask.trigger); + eventManager.unsubscribe("mouseup", handleMouseUp); + eventManager.unsubscribe("contextmenu", handleContextMenu); + eventManager.unsubscribe("dragstart", handleDragStart); + eventManager.unsubscribe("dragend", handleDragEnd); + eventManager.unsubscribe("click", hyperlinkClickHandler.handleClick); + odtDocument.unsubscribe(ops.OdtDocument.signalOperationEnd, redrawRegionSelectionTask.trigger); + odtDocument.unsubscribe(ops.Document.signalCursorAdded, inputMethodEditor.registerCursor); + odtDocument.unsubscribe(ops.Document.signalCursorRemoved, inputMethodEditor.removeCursor); + odtDocument.unsubscribe(ops.OdtDocument.signalOperationEnd, updateUndoStack); + callback() } - 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) + this.destroy = function(callback) { + var destroyCallbacks = []; + if(iOSSafariSupport) { + destroyCallbacks.push(iOSSafariSupport.destroy) } - 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" + destroyCallbacks = destroyCallbacks.concat([drawShadowCursorTask.destroy, redrawRegionSelectionTask.destroy, directFormattingController.destroy, inputMethodEditor.destroy, eventManager.destroy, destroy]); + runtime.clearTimeout(handleMouseClickTimeoutId); + async.destroyAll(destroyCallbacks, callback) + }; + function init() { + drawShadowCursorTask = new core.ScheduledTask(updateShadowCursor, 0); + redrawRegionSelectionTask = new core.ScheduledTask(redrawRegionSelection, 0); + keyDownHandler.bind(keyCode.Left, modifier.None, rangeSelectionOnly(selectionController.moveCursorToLeft)); + keyDownHandler.bind(keyCode.Right, modifier.None, rangeSelectionOnly(selectionController.moveCursorToRight)); + keyDownHandler.bind(keyCode.Up, modifier.None, rangeSelectionOnly(selectionController.moveCursorUp)); + keyDownHandler.bind(keyCode.Down, modifier.None, rangeSelectionOnly(selectionController.moveCursorDown)); + keyDownHandler.bind(keyCode.Left, modifier.Shift, rangeSelectionOnly(selectionController.extendSelectionToLeft)); + keyDownHandler.bind(keyCode.Right, modifier.Shift, rangeSelectionOnly(selectionController.extendSelectionToRight)); + keyDownHandler.bind(keyCode.Up, modifier.Shift, rangeSelectionOnly(selectionController.extendSelectionUp)); + keyDownHandler.bind(keyCode.Down, modifier.Shift, rangeSelectionOnly(selectionController.extendSelectionDown)); + keyDownHandler.bind(keyCode.Home, modifier.None, rangeSelectionOnly(selectionController.moveCursorToLineStart)); + keyDownHandler.bind(keyCode.End, modifier.None, rangeSelectionOnly(selectionController.moveCursorToLineEnd)); + keyDownHandler.bind(keyCode.Home, modifier.Ctrl, rangeSelectionOnly(selectionController.moveCursorToDocumentStart)); + keyDownHandler.bind(keyCode.End, modifier.Ctrl, rangeSelectionOnly(selectionController.moveCursorToDocumentEnd)); + keyDownHandler.bind(keyCode.Home, modifier.Shift, rangeSelectionOnly(selectionController.extendSelectionToLineStart)); + keyDownHandler.bind(keyCode.End, modifier.Shift, rangeSelectionOnly(selectionController.extendSelectionToLineEnd)); + keyDownHandler.bind(keyCode.Up, modifier.CtrlShift, rangeSelectionOnly(selectionController.extendSelectionToParagraphStart)); + keyDownHandler.bind(keyCode.Down, modifier.CtrlShift, rangeSelectionOnly(selectionController.extendSelectionToParagraphEnd)); + keyDownHandler.bind(keyCode.Home, modifier.CtrlShift, rangeSelectionOnly(selectionController.extendSelectionToDocumentStart)); + keyDownHandler.bind(keyCode.End, modifier.CtrlShift, rangeSelectionOnly(selectionController.extendSelectionToDocumentEnd)); + if(isMacOS) { + keyDownHandler.bind(keyCode.Left, modifier.Alt, rangeSelectionOnly(selectionController.moveCursorBeforeWord)); + keyDownHandler.bind(keyCode.Right, modifier.Alt, rangeSelectionOnly(selectionController.moveCursorPastWord)); + keyDownHandler.bind(keyCode.Left, modifier.Meta, rangeSelectionOnly(selectionController.moveCursorToLineStart)); + keyDownHandler.bind(keyCode.Right, modifier.Meta, rangeSelectionOnly(selectionController.moveCursorToLineEnd)); + keyDownHandler.bind(keyCode.Home, modifier.Meta, rangeSelectionOnly(selectionController.moveCursorToDocumentStart)); + keyDownHandler.bind(keyCode.End, modifier.Meta, rangeSelectionOnly(selectionController.moveCursorToDocumentEnd)); + keyDownHandler.bind(keyCode.Left, modifier.AltShift, rangeSelectionOnly(selectionController.extendSelectionBeforeWord)); + keyDownHandler.bind(keyCode.Right, modifier.AltShift, rangeSelectionOnly(selectionController.extendSelectionPastWord)); + keyDownHandler.bind(keyCode.Left, modifier.MetaShift, rangeSelectionOnly(selectionController.extendSelectionToLineStart)); + keyDownHandler.bind(keyCode.Right, modifier.MetaShift, rangeSelectionOnly(selectionController.extendSelectionToLineEnd)); + keyDownHandler.bind(keyCode.Up, modifier.AltShift, rangeSelectionOnly(selectionController.extendSelectionToParagraphStart)); + keyDownHandler.bind(keyCode.Down, modifier.AltShift, rangeSelectionOnly(selectionController.extendSelectionToParagraphEnd)); + keyDownHandler.bind(keyCode.Up, modifier.MetaShift, rangeSelectionOnly(selectionController.extendSelectionToDocumentStart)); + keyDownHandler.bind(keyCode.Down, modifier.MetaShift, rangeSelectionOnly(selectionController.extendSelectionToDocumentEnd)); + keyDownHandler.bind(keyCode.A, modifier.Meta, rangeSelectionOnly(selectionController.extendSelectionToEntireDocument)) }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)}) + keyDownHandler.bind(keyCode.Left, modifier.Ctrl, rangeSelectionOnly(selectionController.moveCursorBeforeWord)); + keyDownHandler.bind(keyCode.Right, modifier.Ctrl, rangeSelectionOnly(selectionController.moveCursorPastWord)); + keyDownHandler.bind(keyCode.Left, modifier.CtrlShift, rangeSelectionOnly(selectionController.extendSelectionBeforeWord)); + keyDownHandler.bind(keyCode.Right, modifier.CtrlShift, rangeSelectionOnly(selectionController.extendSelectionPastWord)); + keyDownHandler.bind(keyCode.A, modifier.Ctrl, rangeSelectionOnly(selectionController.extendSelectionToEntireDocument)) } - 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) - }; - this.visible = function() { - return isVisible - }; - function handleCursorMove(movedCursor) { - if(movedCursor === cursor) { - rerender() + if(isIOS) { + iOSSafariSupport = new gui.IOSSafariSupport(eventManager) + } + eventManager.subscribe("keydown", keyDownHandler.handleEvent); + eventManager.subscribe("keypress", keyPressHandler.handleEvent); + eventManager.subscribe("keyup", keyUpHandler.handleEvent); + eventManager.subscribe("copy", handleCopy); + eventManager.subscribe("mousedown", handleMouseDown); + eventManager.subscribe("mousemove", drawShadowCursorTask.trigger); + eventManager.subscribe("mouseup", handleMouseUp); + eventManager.subscribe("contextmenu", handleContextMenu); + eventManager.subscribe("dragstart", handleDragStart); + eventManager.subscribe("dragend", handleDragEnd); + eventManager.subscribe("click", hyperlinkClickHandler.handleClick); + odtDocument.subscribe(ops.OdtDocument.signalOperationEnd, redrawRegionSelectionTask.trigger); + odtDocument.subscribe(ops.Document.signalCursorAdded, inputMethodEditor.registerCursor); + odtDocument.subscribe(ops.Document.signalCursorRemoved, inputMethodEditor.removeCursor); + odtDocument.subscribe(ops.OdtDocument.signalOperationEnd, updateUndoStack) } - } - this.destroy = function(callback) { - root.removeChild(overlayTop); - root.removeChild(overlayMiddle); - root.removeChild(overlayBottom); - cursor.getOdtDocument().unsubscribe(ops.OdtDocument.signalCursorMoved, handleCursorMove); - callback() + init() }; - 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() -}; + return gui.SessionController +})(); /* - 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 @@ -15959,76 +15525,169 @@ gui.SelectionView = function SelectionView(cursor) { @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 +gui.CaretManager = function CaretManager(sessionController) { + var carets = {}, async = new core.Async, window = runtime.getWindow(), ensureCaretVisibleTimeoutId, scrollIntoViewScheduled = false; + function getCaret(memberId) { + return carets.hasOwnProperty(memberId) ? carets[memberId] : null } - this.getSelectionView = getSelectionView; - function getSelectionViews() { - return Object.keys(selectionViews).map(function(memberid) { - return selectionViews[memberid] + function getCarets() { + return Object.keys(carets).map(function(memberid) { + return carets[memberid] }) } - this.getSelectionViews = getSelectionViews; - function removeSelectionView(memberId) { - if(selectionViews.hasOwnProperty(memberId)) { - selectionViews[memberId].destroy(function() { + function removeCaret(memberId) { + var caret = carets[memberId]; + if(caret) { + caret.destroy(function() { }); - delete selectionViews[memberId] + delete carets[memberId] } } - this.removeSelectionView = removeSelectionView; - function hideSelectionView(memberId) { - if(selectionViews.hasOwnProperty(memberId)) { - selectionViews[memberId].hide() + function refreshLocalCaretBlinking(cursor) { + var caret, memberId = cursor.getMemberId(); + if(memberId === sessionController.getInputMemberId()) { + caret = getCaret(memberId); + if(caret) { + caret.refreshCursorBlinking() + } } } - this.hideSelectionView = hideSelectionView; - function showSelectionView(memberId) { - if(selectionViews.hasOwnProperty(memberId)) { - selectionViews[memberId].show() + function executeEnsureCaretVisible() { + var caret = getCaret(sessionController.getInputMemberId()); + scrollIntoViewScheduled = false; + if(caret) { + caret.ensureVisible() + } + } + function scheduleCaretVisibilityCheck() { + var caret = getCaret(sessionController.getInputMemberId()); + if(caret) { + caret.handleUpdate(); + if(!scrollIntoViewScheduled) { + scrollIntoViewScheduled = true; + ensureCaretVisibleTimeoutId = runtime.setTimeout(executeEnsureCaretVisible, 50) + } + } + } + function ensureLocalCaretVisible(info) { + if(info.memberId === sessionController.getInputMemberId()) { + scheduleCaretVisibilityCheck() + } + } + function focusLocalCaret() { + var caret = getCaret(sessionController.getInputMemberId()); + if(caret) { + caret.setFocus() + } + } + function blurLocalCaret() { + var caret = getCaret(sessionController.getInputMemberId()); + if(caret) { + caret.removeFocus() + } + } + function showLocalCaret() { + var caret = getCaret(sessionController.getInputMemberId()); + if(caret) { + caret.show() + } + } + 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), eventManager = sessionController.getEventManager(); + carets[memberid] = caret; + if(memberid === sessionController.getInputMemberId()) { + runtime.log("Starting to track input on new cursor of " + memberid); + cursor.subscribe(ops.OdtCursor.signalCursorUpdated, scheduleCaretVisibilityCheck); + caret.setOverlayElement(eventManager.getEventTrap()) + }else { + cursor.subscribe(ops.OdtCursor.signalCursorUpdated, caret.handleUpdate) + } + return caret + }; + this.getCaret = getCaret; + this.getCarets = getCarets; + this.destroy = function(callback) { + var odtDocument = sessionController.getSession().getOdtDocument(), eventManager = sessionController.getEventManager(), caretCleanup = getCarets().map(function(caret) { + return caret.destroy + }); + runtime.clearTimeout(ensureCaretVisibleTimeoutId); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, ensureLocalCaretVisible); + odtDocument.unsubscribe(ops.Document.signalCursorMoved, refreshLocalCaretBlinking); + odtDocument.unsubscribe(ops.Document.signalCursorRemoved, removeCaret); + eventManager.unsubscribe("focus", focusLocalCaret); + eventManager.unsubscribe("blur", blurLocalCaret); + window.removeEventListener("focus", showLocalCaret, false); + window.removeEventListener("blur", hideLocalCaret, false); + carets = {}; + async.destroyAll(caretCleanup, callback) + }; + function init() { + var odtDocument = sessionController.getSession().getOdtDocument(), eventManager = sessionController.getEventManager(); + odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, ensureLocalCaretVisible); + odtDocument.subscribe(ops.Document.signalCursorMoved, refreshLocalCaretBlinking); + odtDocument.subscribe(ops.Document.signalCursorRemoved, removeCaret); + eventManager.subscribe("focus", focusLocalCaret); + eventManager.subscribe("blur", blurLocalCaret); + window.addEventListener("focus", showLocalCaret, false); + window.addEventListener("blur", hideLocalCaret, false) + } + 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.showSelectionView = showSelectionView; - this.rerenderSelectionViews = function() { - Object.keys(selectionViews).forEach(function(memberId) { - if(selectionViews[memberId].visible()) { - selectionViews[memberId].rerender() - } - }) + this.setEdits = function(editArray) { + edits = editArray; + renderEdits() }; - 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.show = function() { + handle.style.display = "block" + }; + this.hide = function() { + handle.style.display = "none" }; 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) + 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 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -16063,167 +15722,51 @@ gui.SelectionViewManager = function SelectionViewManager() { @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 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 +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}) } } - while(operations && remainingAddOps > 0) { - operations.reverse(); - operations.forEach(processOp); - operations = undoStates.pop() - } - return values(addCursor).concat(values(moveCursor)) + arr.sort(function(a, b) { + return a.time - b.time + }); + return arr } - 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.getNode = function() { + return editInfoNode }; - this.setOdtDocument = function(newDocument) { - odtDocument = newDocument + this.getOdtDocument = function() { + return odtDocument }; - this.resetInitialState = function() { - undoStates.length = 0; - redoStates.length = 0; - initialState.length = 0; - currentUndoState.length = 0; - initialDoc = null; - emitStackChange() + this.getEdits = function() { + return editHistory }; - this.saveInitialState = function() { - var odfContainer = odtDocument.getOdfCanvas().odfContainer(), annotationViewManager = odtDocument.getOdfCanvas().getAnnotationViewManager(); - if(annotationViewManager) { - annotationViewManager.forgetAnnotations() - } - 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.getSortedEdits = function() { + return sortEdits() }; - this.setPlaybackFunction = function(playback_func) { - playFunc = playback_func + this.addEdit = function(memberid, timestamp) { + editHistory[memberid] = {time:timestamp} }; - 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.clearEdits = function() { + editHistory = {} }; - 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() + this.destroy = function(callback) { + if(container.parentNode) { + container.removeChild(editInfoNode) } - return moved + callback() }; - 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 + function init() { + var editInfons = "urn:webodf:names:editinfo", dom = odtDocument.getDOMDocument(); + editInfoNode = dom.createElementNS(editInfons, "editinfo"); + container.insertBefore(editInfoNode, container.firstChild) } + init() }; -gui.TrivialUndoManager.signalDocumentRootReplaced = "documentRootReplaced"; -(function() { - return gui.TrivialUndoManager -})(); /* Copyright (C) 2012-2013 KO GmbH @@ -16261,167 +15804,139 @@ gui.TrivialUndoManager.signalDocumentRootReplaced = "documentRootReplaced"; @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) +gui.EditInfoMarker = function EditInfoMarker(editInfo, initialVisibility) { + var self = this, editInfoNode, handle, marker, editinfons = "urn:webodf:names:editinfo", decayTimer0, decayTimer1, decayTimer2, decayTimeStep = 1E4; + function applyDecay(opacity, delay) { + return runtime.setTimeout(function() { + marker.style.opacity = opacity + }, delay) + } + function deleteDecay(timerId) { + runtime.clearTimeout(timerId) + } + 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); + deleteDecay(decayTimer1); + deleteDecay(decayTimer2); + if(age < decayTimeStep) { + decayTimer0 = applyDecay(1, 0); + decayTimer1 = applyDecay(0.5, decayTimeStep - age); + decayTimer2 = applyDecay(0.2, decayTimeStep * 2 - age) + }else { + if(age >= decayTimeStep && age < decayTimeStep * 2) { + decayTimer0 = applyDecay(0.5, 0); + decayTimer2 = applyDecay(0.2, decayTimeStep * 2 - age) + }else { + decayTimer0 = applyDecay(0.2, 0) + } } }; - 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.getEdits = function() { + return editInfo.getEdits() + }; + this.clearEdits = function() { + editInfo.clearEdits(); + handle.setEdits([]); + if(marker.hasAttributeNS(editinfons, "editinfo:memberid")) { + marker.removeAttributeNS(editinfons, "editinfo:memberid") + } }; - this.getOperationFactory = function() { - return operationFactory + this.getEditInfo = function() { + return editInfo }; - this.getOdtDocument = function() { - return odtDocument + this.show = function() { + marker.style.display = "block" }; - this.enqueue = function(ops) { - operationRouter.push(ops) + this.hide = function() { + self.hideHandle(); + marker.style.display = "none" }; - this.close = function(callback) { - operationRouter.close(function(err) { + this.showHandle = function() { + handle.show() + }; + this.hideHandle = function() { + handle.hide() + }; + this.destroy = function(callback) { + deleteDecay(decayTimer0); + deleteDecay(decayTimer1); + deleteDecay(decayTimer2); + editInfoNode.removeChild(marker); + handle.destroy(function(err) { if(err) { callback(err) }else { - odtDocument.close(callback) + editInfo.destroy(callback) } }) }; - this.destroy = function(callback) { - odtDocument.destroy(callback) - }; function init() { - self.setOperationRouter(new ops.TrivialOperationRouter) + var dom = editInfo.getOdtDocument().getDOMDocument(), 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() + } } init() }; -/* - - 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 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 = false; - if(cursorNode) { - newIsAnnotatable = !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 +gui.ShadowCursor = function ShadowCursor(document) { + var selectedRange = (document.getDOMDocument().createRange()), forwardSelection = true; + this.removeFromDocument = function() { }; - 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.getMemberId = function() { + return gui.ShadowCursor.ShadowCursorMemberId }; - 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.getSelectedRange = function() { + return selectedRange }; - this.subscribe = function(eventid, cb) { - eventNotifier.subscribe(eventid, cb) + this.setSelectedRange = function(range, isForwardSelection) { + selectedRange = range; + forwardSelection = isForwardSelection !== false }; - this.unsubscribe = function(eventid, cb) { - eventNotifier.unsubscribe(eventid, cb) + this.hasForwardSelection = function() { + return forwardSelection }; - this.destroy = function(callback) { - odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); - callback() + this.getDocument = function() { + return document + }; + this.getSelectionType = function() { + return ops.OdtCursor.RangeSelection }; function init() { - odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded); - odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved); - odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved); - updatedCachedValues() + selectedRange.setStart(document.getRootNode(), 0) } init() }; -gui.AnnotationController.annotatableChanged = "annotatable/changed"; +gui.ShadowCursor.ShadowCursorMemberId = ""; (function() { - return gui.AnnotationController + return gui.ShadowCursor })(); +gui.SelectionView = function SelectionView(cursor) { +}; +gui.SelectionView.prototype.rerender = function() { +}; +gui.SelectionView.prototype.show = function() { +}; +gui.SelectionView.prototype.hide = function() { +}; +gui.SelectionView.prototype.destroy = function(callback) { +}; /* Copyright (C) 2013 KO GmbH @@ -16459,159 +15974,74 @@ gui.AnnotationController.annotatableChanged = "annotatable/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() - } +gui.SelectionViewManager = function SelectionViewManager(SelectionView) { + var selectionViews = {}; + function getSelectionView(memberId) { + return selectionViews.hasOwnProperty(memberId) ? selectionViews[memberId] : null } - function onCursorRemoved(memberId) { - if(memberId === inputMemberId) { - updatedCachedValues() - } + this.getSelectionView = getSelectionView; + function getSelectionViews() { + return Object.keys(selectionViews).map(function(memberid) { + return selectionViews[memberid] + }) } - function onCursorMoved(cursor) { - if(cursor.getMemberId() === inputMemberId) { - updatedCachedValues() + this.getSelectionViews = getSelectionViews; + function removeSelectionView(memberId) { + if(selectionViews.hasOwnProperty(memberId)) { + selectionViews[memberId].destroy(function() { + }); + delete selectionViews[memberId] } } - function onParagraphStyleModified() { - updatedCachedValues() - } - function onParagraphChanged(args) { - var cursor = odtDocument.getCursor(inputMemberId); - if(cursor && odtDocument.getParagraphElement(cursor.getNode()) === args.paragraphElement) { - updatedCachedValues() + this.removeSelectionView = removeSelectionView; + function hideSelectionView(memberId) { + if(selectionViews.hasOwnProperty(memberId)) { + selectionViews[memberId].hide() } } - 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 + this.hideSelectionView = hideSelectionView; + function showSelectionView(memberId) { + if(selectionViews.hasOwnProperty(memberId)) { + selectionViews[memberId].show() } - 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.showSelectionView = showSelectionView; + this.rerenderSelectionViews = function() { + Object.keys(selectionViews).forEach(function(memberId) { + selectionViews[memberId].rerender() + }) }; - this.unsubscribe = function(eventid, cb) { - eventNotifier.unsubscribe(eventid, cb) + this.registerCursor = function(cursor, virtualSelectionsInitiallyVisible) { + var memberId = cursor.getMemberId(), selectionView = new SelectionView(cursor); + if(virtualSelectionsInitiallyVisible) { + selectionView.show() + }else { + selectionView.hide() + } + selectionViews[memberId] = selectionView; + return selectionView }; 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() + 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() + } + } + } + destroySelectionView(0, undefined) } - init() }; -gui.DirectParagraphStyler.paragraphStylingChanged = "paragraphStyling/changed"; -(function() { - return gui.DirectParagraphStyler -})(); /* - 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 @@ -16646,272 +16076,659 @@ gui.DirectParagraphStyler.paragraphStylingChanged = "paragraphStyling/changed"; @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)) +gui.SessionViewOptions = function() { + this.editInfoMarkersInitiallyVisible = true; + this.caretAvatarsInitiallyVisible = true; + this.caretBlinksOnRangeSelect = true +}; +(function() { + function configOption(userValue, defaultValue) { + return userValue !== undefined ? Boolean(userValue) : defaultValue + } + gui.SessionView = 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); + 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 + } + 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(".selectionOverlay", "{ fill: " + color + "; stroke: " + color + ";}", "") + } + function highlightEdit(element, memberId, timestamp) { + var editInfo, editInfoMarker, id = "", editInfoNode = element.getElementsByTagNameNS(editInfons, "editinfo").item(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").item(0)); + editInfoNode.setAttributeNS(editInfons, "id", id); + editInfoMap[id] = editInfoMarker + } + 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) + } + this.destroy = function(callback) { + var odtDocument = session.getOdtDocument(), editInfoArray = Object.keys(editInfoMap).map(function(keyname) { + return editInfoMap[keyname] + }); + odtDocument.unsubscribe(ops.Document.signalMemberAdded, renderMemberData); + odtDocument.unsubscribe(ops.Document.signalMemberUpdated, renderMemberData); + odtDocument.unsubscribe(ops.Document.signalCursorAdded, onCursorAdded); + odtDocument.unsubscribe(ops.Document.signalCursorRemoved, onCursorRemoved); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); + odtDocument.unsubscribe(ops.Document.signalCursorMoved, onCursorMoved); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, selectionViewManager.rerenderSelectionViews); + odtDocument.unsubscribe(ops.OdtDocument.signalTableAdded, selectionViewManager.rerenderSelectionViews); + odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, selectionViewManager.rerenderSelectionViews); + 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").item(0); + odtDocument.subscribe(ops.Document.signalMemberAdded, renderMemberData); + odtDocument.subscribe(ops.Document.signalMemberUpdated, renderMemberData); + odtDocument.subscribe(ops.Document.signalCursorAdded, onCursorAdded); + odtDocument.subscribe(ops.Document.signalCursorRemoved, onCursorRemoved); + odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged); + odtDocument.subscribe(ops.Document.signalCursorMoved, onCursorMoved); + odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, selectionViewManager.rerenderSelectionViews); + odtDocument.subscribe(ops.OdtDocument.signalTableAdded, selectionViewManager.rerenderSelectionViews); + odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, selectionViewManager.rerenderSelectionViews); + 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() + } +})(); +gui.SvgSelectionView = function SvgSelectionView(cursor) { + var document = cursor.getDocument(), documentRoot, root, doc = document.getDOMDocument(), async = new core.Async, svgns = "http://www.w3.org/2000/svg", overlay = doc.createElementNS(svgns, "svg"), polygon = doc.createElementNS(svgns, "polygon"), odfUtils = new odf.OdfUtils, domUtils = new core.DomUtils, isVisible = true, positionIterator = gui.SelectionMover.createPositionIterator(document.getRootNode()), FILTER_ACCEPT = NodeFilter.FILTER_ACCEPT, FILTER_REJECT = NodeFilter.FILTER_REJECT, renderTask; + function addOverlay() { + var newDocumentRoot = document.getRootNode(); + if(documentRoot !== newDocumentRoot) { + documentRoot = newDocumentRoot; + root = (documentRoot.parentNode.parentNode.parentNode); + root.appendChild(overlay); + overlay.setAttribute("class", "selectionOverlay"); + overlay.appendChild(polygon) + } + } + function translateRect(rect) { + var rootRect = domUtils.getBoundingClientRect(root), zoomLevel = document.getCanvas().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, endOffset; + if(range.endContainer === node) { + startOffset = range.endOffset + }else { + if(node.nodeType === Node.TEXT_NODE) { + startOffset = node.length + }else { + startOffset = 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 + } + } + } + range.setStart(node, startOffset); + range.setEnd(node, endOffset) } - return selectionStyles + return true } - function updatedCachedValues() { - var fontSize, diffMap; - currentSelectionStyles = getAppliedStyles(); - function noteChange(oldValue, newValue, id) { - if(oldValue !== newValue) { - if(diffMap === undefined) { - diffMap = {} + 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 + } } - 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) + range.setStart(node, startOffset); + range.setEnd(node, endOffset) } + return true } - function onCursorAdded(cursor) { - if(cursor.getMemberId() === inputMemberId) { - updatedCachedValues() + 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 onCursorRemoved(memberId) { - if(memberId === inputMemberId) { - updatedCachedValues() + if(!firstVisibleRect(firstRange, nodes)) { + return null } - } - function onCursorMoved(cursor) { - if(cursor.getMemberId() === inputMemberId) { - updatedCachedValues() + if(!lastVisibleRect(lastRange, nodes)) { + return null } + fillerRange.setStart(firstRange.startContainer, firstRange.startOffset); + fillerRange.setEnd(lastRange.endContainer, lastRange.endOffset); + return{firstRange:firstRange, lastRange:lastRange, fillerRange:fillerRange} } - function onParagraphStyleModified() { - updatedCachedValues() + 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 onParagraphChanged(args) { - var cursor = odtDocument.getCursor(inputMemberId); - if(cursor && odtDocument.getParagraphElement(cursor.getNode()) === args.paragraphElement) { - updatedCachedValues() + function checkAndGrowOrCreateRect(originalRect, newRect) { + if(newRect && (newRect.width > 0 && newRect.height > 0)) { + if(!originalRect) { + originalRect = newRect + }else { + originalRect = getBoundingRect(originalRect, newRect) + } } + return originalRect } - function toggle(predicate, toggleMethod) { - var cursor = odtDocument.getCursor(inputMemberId), appliedStyles; - if(!cursor) { - return false + 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 } - appliedStyles = styleHelper.getAppliedStyles(cursor.getSelectedRange()); - toggleMethod(!predicate(appliedStyles)); - 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]) + function getRectFromNodeAfterFiltering(node) { + var rect = null; + if(acceptNode(node) === FILTER_ACCEPT) { + rect = domUtils.getBoundingClientRect(node) + } + 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 = document.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 { - directCursorStyleProperties = utils.mergeObjects(directCursorStyleProperties || {}, properties); - updatedCachedValues() + 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 { + 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() + } + } } - } - 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() + if(!lastMeasuredNode) { + lastMeasuredNode = firstNode } - return styleOp - }; - function clearCursorStyle(op) { - var spec = op.spec(); - if(directCursorStyleProperties && spec.memberid === inputMemberId) { - if(spec.optype !== "SplitParagraph") { - directCursorStyleProperties = null; - updatedCachedValues() + 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 grownRect } - 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) + 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 } - this.setHasUnderline = setHasUnderline; - function setHasStrikethrough(checked) { - var value = checked ? "solid" : "none"; - applyTextPropertyToSelection("style:text-line-through-style", value) + function setPoints(points) { + var pointsString = "", i; + for(i = 0;i < points.length;i += 1) { + pointsString += points[i].x + "," + points[i].y + " " + } + polygon.setAttribute("points", pointsString) } - this.setHasStrikethrough = setHasStrikethrough; - function setFontSize(value) { - applyTextPropertyToSelection("fo:font-size", value + "pt") + function repositionOverlays(selectedRange) { + var extremes = getExtremeRanges(selectedRange), firstRange, lastRange, fillerRange, firstRect, fillerRect, lastRect, left, right, top, bottom; + if(extremes) { + 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) + } + left = fillerRect.left; + right = firstRect.left + Math.max(0, fillerRect.width - (firstRect.left - fillerRect.left)); + top = Math.min(firstRect.top, lastRect.top); + bottom = lastRect.top + lastRect.height; + setPoints([{x:firstRect.left, y:top + firstRect.height}, {x:firstRect.left, y:top}, {x:right, y:top}, {x:right, y:bottom - lastRect.height}, {x:lastRect.right, y:bottom - lastRect.height}, {x:lastRect.right, y:bottom}, {x:left, y:bottom}, {x:left, y:top + firstRect.height}, {x:firstRect.left, y:top + firstRect.height}]); + firstRange.detach(); + lastRange.detach(); + fillerRange.detach() + } + return Boolean(extremes) } - this.setFontSize = setFontSize; - function setFontName(value) { - applyTextPropertyToSelection("style:font-name", value) + function rerender() { + var range = cursor.getSelectedRange(), shouldShow; + shouldShow = isVisible && (cursor.getSelectionType() === ops.OdtCursor.RangeSelection && !range.collapsed); + if(shouldShow) { + addOverlay(); + shouldShow = repositionOverlays(range) + } + if(shouldShow) { + overlay.style.display = "block" + }else { + overlay.style.display = "none" + } } - 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.rerender = function() { + if(isVisible) { + renderTask.trigger() + } }; - this.unsubscribe = function(eventid, cb) { - eventNotifier.unsubscribe(eventid, cb) + this.show = function() { + isVisible = true; + renderTask.trigger() }; - 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); + this.hide = function() { + isVisible = false; + renderTask.trigger() + }; + function handleCursorMove(movedCursor) { + if(isVisible && movedCursor === cursor) { + renderTask.trigger() + } + } + function destroy(callback) { + root.removeChild(overlay); + cursor.getDocument().unsubscribe(ops.Document.signalCursorMoved, handleCursorMove); callback() + } + this.destroy = function(callback) { + async.destroyAll([renderTask.destroy, destroy], 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() + var editinfons = "urn:webodf:names:editinfo", memberid = cursor.getMemberId(); + renderTask = new core.ScheduledTask(rerender, 0); + addOverlay(); + overlay.setAttributeNS(editinfons, "editinfo:memberid", memberid); + cursor.getDocument().subscribe(ops.Document.signalCursorMoved, handleCursorMove) } 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 +/* + + 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.UndoStateRules = function UndoStateRules() { + function ReverseIterator(array, predicate) { + var index = array.length; + this.previous = function() { + for(index = index - 1;index >= 0;index -= 1) { + if(predicate(array[index])) { + return array[index] + } + } + return null + } } - 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 getOpType(op) { + return op.spec().optype } - function getFileExtension(mimetype) { - mimetype = mimetype.toLowerCase(); - return fileExtensionByMimetype.hasOwnProperty(mimetype) ? fileExtensionByMimetype[mimetype] : null + function getOpPosition(op) { + var key = "position", spec = op.spec(), value; + if(spec.hasOwnProperty(key)) { + value = (spec[key]) + } + return value } - 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) + function isEditOperation(op) { + return op.isEdit + } + this.isEditOperation = isEditOperation; + function canAggregateOperation(op) { + switch(getOpType(op)) { + case "RemoveText": + ; + case "InsertText": + return true; + default: + return false } - 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 isSameDirectionOfTravel(thisOp, lastEditOp, secondLastEditOp) { + var thisPosition = getOpPosition(thisOp), lastPosition = getOpPosition(lastEditOp), secondLastPosition = getOpPosition(secondLastEditOp), diffLastToSecondLast = lastPosition - secondLastPosition, diffThisToLast = thisPosition - lastPosition; + return diffThisToLast === diffLastToSecondLast + } + function isAdjacentOperation(thisOp, lastEditOp) { + var positionDifference = getOpPosition(thisOp) - getOpPosition(lastEditOp); + return positionDifference === 0 || Math.abs(positionDifference) === 1 + } + function continuesOperations(thisOp, lastEditOp, secondLastEditOp) { + if(!secondLastEditOp) { + return isAdjacentOperation(thisOp, lastEditOp) } - if(originalSize.height > pageContentSize.height) { - heightRatio = pageContentSize.height / originalSize.height + return isSameDirectionOfTravel(thisOp, lastEditOp, secondLastEditOp) + } + function continuesMostRecentEditOperation(thisOp, recentOperations) { + var thisOpType = getOpType(thisOp), editOpsFinder = new ReverseIterator(recentOperations, isEditOperation), lastEditOp = editOpsFinder.previous(); + runtime.assert(Boolean(lastEditOp), "No edit operations found in state"); + if(thisOpType === getOpType((lastEditOp))) { + return continuesOperations(thisOp, (lastEditOp), editOpsFinder.previous()) } - ratio = Math.min(widthRatio, heightRatio); - return{width:originalSize.width * ratio, height:originalSize.height * ratio} + return false } - 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") + function continuesMostRecentEditGroup(thisOp, recentOperations) { + var thisOpType = getOpType(thisOp), editOpsFinder = new ReverseIterator(recentOperations, isEditOperation), candidateOp = editOpsFinder.previous(), lastEditOp, secondLastEditOp = null, inspectedGroupsCount, groupId; + runtime.assert(Boolean(candidateOp), "No edit operations found in state"); + groupId = candidateOp.group; + runtime.assert(groupId !== undefined, "Operation has no group"); + inspectedGroupsCount = 1; + while(candidateOp && candidateOp.group === groupId) { + if(thisOpType === getOpType(candidateOp)) { + lastEditOp = candidateOp; + break + } + candidateOp = editOpsFinder.previous() } - pageContentSize = paragraphStyleToPageContentSizeMap[styleName]; - originalSize = {width:widthInPx * cmPerPixel, height:heightInPx * cmPerPixel}; - newSize = trimmedSize(originalSize, pageContentSize); - insertImageInternal(mimetype, content, newSize.width, newSize.height) + if(lastEditOp) { + candidateOp = editOpsFinder.previous(); + while(candidateOp) { + if(candidateOp.group !== groupId) { + if(inspectedGroupsCount === 2) { + break + } + groupId = candidateOp.group; + inspectedGroupsCount += 1 + } + if(thisOpType === getOpType(candidateOp)) { + secondLastEditOp = candidateOp; + break + } + candidateOp = editOpsFinder.previous() + } + return continuesOperations(thisOp, (lastEditOp), secondLastEditOp) + } + return false + } + function isPartOfOperationSet(operation, recentOperations) { + var areOperationsGrouped = operation.group !== undefined, lastOperation; + if(!isEditOperation(operation)) { + return true + } + if(recentOperations.length === 0) { + return true + } + lastOperation = recentOperations[recentOperations.length - 1]; + if(areOperationsGrouped && operation.group === lastOperation.group) { + return true + } + if(canAggregateOperation(operation) && recentOperations.some(isEditOperation)) { + if(areOperationsGrouped) { + return continuesMostRecentEditGroup(operation, recentOperations) + } + return continuesMostRecentEditOperation(operation, recentOperations) + } + return false } + this.isPartOfOperationSet = isPartOfOperationSet }; /* @@ -16950,986 +16767,861 @@ gui.ImageManager = function ImageManager(session, inputMemberId, objectNameGener @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 +gui.TrivialUndoManager = function TrivialUndoManager(defaultRules) { + var self = this, cursorns = "urn:webodf:names:cursor", domUtils = new core.DomUtils, initialDoc, initialState = [], playFunc, document, currentUndoState = [], undoStates = [], redoStates = [], eventNotifier = new core.EventNotifier([gui.UndoManager.signalUndoStackChanged, gui.UndoManager.signalUndoStateCreated, gui.UndoManager.signalUndoStateModified, gui.TrivialUndoManager.signalDocumentRootReplaced]), undoRules = defaultRules || new gui.UndoStateRules, isExecutingOps = false; + function executeOperations(operations) { + if(operations.length > 0) { + isExecutingOps = true; + playFunc(operations); + isExecutingOps = false } - return selection } - this.enqueueParagraphSplittingOps = function() { - var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op, operations = []; - if(selection.length > 0) { - op = createOpRemoveSelection(selection); - operations.push(op) + 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) } - op = new ops.OpSplitParagraph; - op.init({memberid:inputMemberId, position:selection.position, moveCursor:true}); - 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 + } + 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(); + document.getMemberIds().forEach(function(memberid) { + requiredAddOps[memberid] = 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 } } - return false + 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.unsubscribe = function(signal, callback) { + eventNotifier.unsubscribe(signal, callback) + }; + this.hasUndoStates = function() { + return undoStates.length > 0 + }; + this.hasRedoStates = function() { + return redoStates.length > 0 + }; + this.setDocument = function(newDocument) { + document = newDocument + }; + this.purgeInitialState = function() { + undoStates.length = 0; + redoStates.length = 0; + initialState.length = 0; + currentUndoState.length = 0; + initialDoc = null; + emitStackChange() + }; + function setInitialState() { + initialDoc = document.cloneDocumentElement(); + removeCursors(initialDoc); + completeCurrentUndoState(); + currentUndoState = initialState = extractCursorStates([initialState].concat(undoStates)); + undoStates.length = 0; + redoStates.length = 0; + emitStackChange() } - 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]) + this.setInitialState = setInitialState; + this.initialize = function() { + if(!initialDoc) { + setInitialState() } - 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]) - } + this.setPlaybackFunction = function(playback_func) { + playFunc = playback_func + }; + this.onOperationExecuted = function(op) { + if(isExecutingOps) { + return + } + if(undoRules.isEditOperation(op) && (currentUndoState === initialState || redoStates.length > 0) || !undoRules.isPartOfOperationSet(op, currentUndoState)) { + redoStates.length = 0; + completeCurrentUndoState(); + currentUndoState = [op]; + undoStates.push(currentUndoState); + eventNotifier.emit(gui.UndoManager.signalUndoStateCreated, {operations:currentUndoState}); + emitStackChange() }else { - op = createOpRemoveSelection(selection); - session.enqueue([op]) + currentUndoState.push(op); + eventNotifier.emit(gui.UndoManager.signalUndoStateModified, {operations:currentUndoState}) } - return op !== null }; - this.removeCurrentSelection = function() { - var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op; - if(selection.length !== 0) { - op = createOpRemoveSelection(selection); - session.enqueue([op]) + this.moveForward = function(states) { + var moved = 0, redoOperations; + while(states && redoStates.length) { + redoOperations = redoStates.pop(); + undoStates.push(redoOperations); + executeOperations(redoOperations); + states -= 1; + moved += 1 } - return true + if(moved) { + currentUndoState = mostRecentUndoState(); + emitStackChange() + } + return moved }; - function insertText(text) { - var selection = toForwardSelection(odtDocument.getCursorSelection(inputMemberId)), op, stylingOp, operations = []; - if(selection.length > 0) { - op = createOpRemoveSelection(selection); - operations.push(op) + this.moveBackward = function(states) { + var moved = 0; + while(states && undoStates.length) { + redoStates.push(undoStates.pop()); + states -= 1; + moved += 1 } - op = new ops.OpInsertText; - op.init({memberid:inputMemberId, position:selection.position, text:text, moveCursor:true}); - operations.push(op); - if(directStyleOp) { - stylingOp = directStyleOp(selection.position, text.length); - if(stylingOp) { - operations.push(stylingOp) - } + if(moved) { + document.setDocumentElement((initialDoc.cloneNode(true))); + eventNotifier.emit(gui.TrivialUndoManager.signalDocumentRootReplaced, {}); + document.getMemberIds().forEach(function(memberid) { + document.removeCursor(memberid) + }); + executeOperations(initialState); + undoStates.forEach(executeOperations); + currentUndoState = mostRecentUndoState() || initialState; + emitStackChange() } - session.enqueue(operations) + return moved } - this.insertText = insertText }; +gui.TrivialUndoManager.signalDocumentRootReplaced = "documentRootReplaced"; (function() { - return gui.TextManipulator + return gui.TrivialUndoManager })(); -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, 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 cancelEvent(event) { - if(event.preventDefault) { - event.preventDefault() - }else { - event.returnValue = false - } - } - 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 +/* + + 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.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) } - 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 isBackwards + } + function getStyleReferencingAttributes(setProperties, styleName) { + var attributes = []; + function check(attributeName) { + if(setProperties[attributeName] === styleName) { + attributes.push(attributeName) } - return result } - 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()) - } - 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()) + if(setProperties) { + ["style:parent-style-name", "style:next-style-name"].forEach(check) } - 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) - } + return attributes + } + function dropStyleReferencingAttributes(setProperties, deletedStyleName) { + function del(attributeName) { + if(setProperties[attributeName] === deletedStyleName) { + delete setProperties[attributeName] } } - 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() + if(setProperties) { + ["style:parent-style-name", "style:next-style-name"].forEach(del) } - 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) + } + function cloneOpspec(opspec) { + var result = {}; + Object.keys(opspec).forEach(function(key) { + if(typeof opspec[key] === "object") { + result[key] = cloneOpspec(opspec[key]) }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 - } + result[key] = opspec[key] } + }); + return result + } + function dropOverruledAndUnneededAttributes(minorSetProperties, minorRemovedProperties, majorSetProperties, majorRemovedProperties) { + var i, name, majorChanged = false, minorChanged = false, removedPropertyNames, majorRemovedPropertyNames = []; + if(majorRemovedProperties && majorRemovedProperties.attributes) { + majorRemovedPropertyNames = majorRemovedProperties.attributes.split(",") } - 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 - } - if(startInsideCanvas && endInsideCanvas) { - if(clickCount === 2) { - expandToWordBoundaries(range) - }else { - if(clickCount >= 3) { - expandToParagraphBoundaries(range) + if(minorSetProperties && (majorSetProperties || majorRemovedPropertyNames.length > 0)) { + Object.keys(minorSetProperties).forEach(function(key) { + var value = minorSetProperties[key], overrulingPropertyValue; + if(typeof value !== "object") { + if(majorSetProperties) { + overrulingPropertyValue = majorSetProperties[key] + } + if(overrulingPropertyValue !== undefined) { + delete minorSetProperties[key]; + minorChanged = true; + if(overrulingPropertyValue === value) { + delete majorSetProperties[key]; + majorChanged = true + } + }else { + if(majorRemovedPropertyNames.indexOf(key) !== -1) { + delete minorSetProperties[key]; + minorChanged = true + } } } - } - 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]) - } - } - 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) + 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 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) + if(removedPropertyNames.length > 0) { + minorRemovedProperties.attributes = removedPropertyNames.join(",") }else { - moveCursorByAdjustment(steps) + delete minorRemovedProperties.attributes } } - 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{majorChanged:majorChanged, minorChanged:minorChanged} + } + function hasProperties(properties) { + var key; + for(key in properties) { + if(properties.hasOwnProperty(key)) { + return true } - 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()) + return false + } + function hasRemovedProperties(properties) { + var key; + for(key in properties) { + if(properties.hasOwnProperty(key)) { + if(key !== "attributes" || properties.attributes.length > 0) { + return true } } - 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 { - moveCursorByAdjustment(steps) - } - } - this.moveCursorToDocumentBoundary = moveCursorToDocumentBoundary; - function moveCursorToDocumentStart() { - moveCursorToDocumentBoundary(-1, false); - return true } - function moveCursorToDocumentEnd() { - moveCursorToDocumentBoundary(1, false); - return true + return false + } + function dropOverruledAndUnneededProperties(minorSet, minorRem, majorSet, majorRem, propertiesName) { + var minorSP = minorSet ? minorSet[propertiesName] : null, minorRP = minorRem ? minorRem[propertiesName] : null, majorSP = majorSet ? majorSet[propertiesName] : null, majorRP = majorRem ? majorRem[propertiesName] : null, result; + result = dropOverruledAndUnneededAttributes(minorSP, minorRP, majorSP, majorRP); + if(minorSP && !hasProperties(minorSP)) { + delete minorSet[propertiesName] } - function extendSelectionToDocumentStart() { - moveCursorToDocumentBoundary(-1, true); - return true + if(minorRP && !hasRemovedProperties(minorRP)) { + delete minorRem[propertiesName] } - function extendSelectionToDocumentEnd() { - moveCursorToDocumentBoundary(1, true); - return true + if(majorSP && !hasProperties(majorSP)) { + delete majorSet[propertiesName] } - function extendSelectionToEntireDocument() { - var rootNode = odtDocument.getRootNode(), lastWalkableStep = odtDocument.convertDomPointToCursorStep(rootNode, rootNode.childNodes.length); - session.enqueue([createOpMoveCursor(0, lastWalkableStep)]); - return true + if(majorRP && !hasRemovedProperties(majorRP)) { + delete majorRem[propertiesName] } - this.extendSelectionToEntireDocument = extendSelectionToEntireDocument; - function redrawRegionSelection() { - var cursor = odtDocument.getCursor(inputMemberId), imageElement; - if(cursor && cursor.getSelectionType() === ops.OdtCursor.RegionSelection) { - imageElement = odfUtils.getImageElements(cursor.getSelectedRange())[0]; - if(imageElement) { - imageSelector.select((imageElement.parentNode)); - return - } + 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) } - imageSelector.clearSelection() + dropStyleReferencingAttributes(addStyleSpec.setProperties, removeStyleSpec.styleName) } - function stringFromKeyPress(event) { - if(event.which === null || event.which === undefined) { - return String.fromCharCode(event.keyCode) + 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) } - if(event.which !== 0 && event.charCode !== 0) { - return String.fromCharCode(event.which) + dropResult = dropOverruledAndUnneededProperties(minorSpec.setProperties, null, majorSpec.setProperties, null, "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 + } } - return null } - function handleCut(e) { - var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); - if(selectedRange.collapsed) { - e.preventDefault(); - return - } - if(clipboard.setDataFromRange(e, selectedRange)) { - textManipulator.removeCurrentSelection() - }else { - runtime.log("Cut operation failed") + 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 } } - function handleBeforeCut() { - var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); - return selectedRange.collapsed !== false - } - function handleCopy(e) { - var cursor = odtDocument.getCursor(inputMemberId), selectedRange = cursor.getSelectedRange(); - if(selectedRange.collapsed) { - e.preventDefault(); - return + 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 { + applyDirectStylingSpec.position = removeTextSpec.position; + if(removeTextSpecEnd < applyDirectStylingSpecEnd) { + applyDirectStylingSpec.length = applyDirectStylingSpecEnd - removeTextSpecEnd + }else { + applyDirectStylingSpecResult = [] + } + } } - if(!clipboard.setDataFromRange(e, selectedRange)) { - runtime.log("Copy operation failed") + } + 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 handlePaste(e) { - var plainText; - if(window.clipboardData && window.clipboardData.getData) { - plainText = window.clipboardData.getData("Text") + 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(e.clipboardData && e.clipboardData.getData) { - plainText = e.clipboardData.getData("text/plain") + if(hasAPriority) { + insertTextSpecB.position += insertTextSpecA.text.length + }else { + insertTextSpecA.position += insertTextSpecB.text.length } } - if(plainText) { - textManipulator.removeCurrentSelection(); - session.enqueue(pasteHandler.createPasteOps(plainText)) - } - cancelEvent(e) - } - function handleBeforePaste() { - return false - } - function updateUndoStack(op) { - if(undoManager) { - undoManager.onOperationExecuted(op) - } - } - function forwardUndoStackChange(e) { - odtDocument.emit(ops.OdtDocument.signalUndoStackChanged, e) } - function undo() { - if(undoManager) { - undoManager.moveBackward(1); - redrawRegionSelectionTask.trigger(); - 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 } - return false } - function redo() { - if(undoManager) { - undoManager.moveForward(1); - redrawRegionSelectionTask.trigger(); - return true - } - return false + if(isMoveCursorSpecRangeInverted) { + invertMoveCursorSpecRange(moveCursorSpec) } - 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) - } + 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 } } - function synchronizeWindowSelection(cursor) { - var selection = window.getSelection(), range = cursor.getSelectedRange(); - if(selection.extend) { - if(cursor.hasForwardSelection()) { - selection.collapse(range.startContainer, range.startOffset); - selection.extend(range.endContainer, range.endOffset) + 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 { + if(hasAPriority) { + splitParagraphSpec.position += insertTextSpec.text.length }else { - selection.collapse(range.endContainer, range.endOffset); - selection.extend(range.startContainer, range.startOffset) + insertTextSpec.position += 1 } - }else { - selection.removeAllRanges(); - selection.addRange(range.cloneRange()); - (odtDocument.getOdfCanvas().getElement()).setActive() + return null } } - 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) + 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.setProperties, minorSpec.removedProperties, majorSpec.setProperties, majorSpec.removedProperties, "style:paragraph-properties"); + dropOverruledAndUnneededProperties(minorSpec.setProperties, minorSpec.removedProperties, majorSpec.setProperties, majorSpec.removedProperties, "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 { - synchronizeWindowSelection(cursor) + updateParagraphStyleSpecBResult = [] } - if(clickCount > 1) { - updateShadowCursor() + } + if(!(minorSpec.setProperties && hasProperties(minorSpec.setProperties)) && !(minorSpec.removedProperties && hasRemovedProperties(minorSpec.removedProperties))) { + if(hasAPriority) { + updateParagraphStyleSpecBResult = [] + }else { + updateParagraphStyleSpecAResult = [] } } } - function mutableSelection(selection) { - if(selection) { - return{anchorNode:selection.anchorNode, anchorOffset:selection.anchorOffset, focusNode:selection.focusNode, focusOffset:selection.focusOffset} + 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 = [] } - 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); - eventManager.focus() + if(!(minorSpec.setProperties && hasProperties(minorSpec.setProperties)) && !(minorSpec.removedProperties && hasRemovedProperties(minorSpec.removedProperties))) { + if(hasAPriority) { + updateMetadataSpecBResult = [] }else { - if(clickStartedWithinContainer && !imageSelector.isSelectorElement(target)) { - if(isMouseMoved) { - selectRange(shadowCursor.getSelectedRange(), shadowCursor.hasForwardSelection(), event.detail); - eventManager.focus() + updateMetadataSpecAResult = [] + } + } + 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 { + if(splitParagraphSpecA.position === splitParagraphSpecB.position) { + if(hasAPriority) { + splitParagraphSpecB.position += 1 }else { - runtime.setTimeout(function() { - var selection = mutableSelection(window.getSelection()), selectionRange, caretPos; - if(!selection.anchorNode && !selection.focusNode) { - caretPos = caretPositionFromPoint(eventDetails.clientX, eventDetails.clientY); - if(caretPos) { - selection.anchorNode = (caretPos.container); - selection.anchorOffset = caretPos.offset; - selection.focusNode = selection.anchorNode; - selection.focusOffset = selection.anchorOffset - } - } - if(selection.anchorNode && selection.focusNode) { - selectionRange = selectionToRange(selection); - selectRange(selectionRange.range, selectionRange.hasForwardSelection, eventDetails.detail) - } - eventManager.focus() - }, 0) + splitParagraphSpecA.position += 1 } } } - clickCount = 0; - clickStartedWithinContainer = false; - isMouseMoved = false } - function handleDragEnd() { - if(clickStartedWithinContainer) { - eventManager.focus() + 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 + } + } } - clickCount = 0; - clickStartedWithinContainer = false; - isMouseMoved = false } - function handleContextMenu(e) { - handleMouseClickEvent(e) + if(isMoveCursorSpecRangeInverted) { + invertMoveCursorSpecRange(moveCursorSpec) } - 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{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 } } - 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("dragend", handleDragEnd); - 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() + if(isMoveCursorSpecRangeInverted) { + invertMoveCursorSpecRange(moveCursorSpec) + } + 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 = "" + } + 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) } - 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("dragend", handleDragEnd); - 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) + if(removeStyleSpec.styleName === updateParagraphStyleSpec.styleName) { + updateParagraphStyleSpecResult = [] + }else { + dropStyleReferencingAttributes(updateParagraphStyleSpec.setProperties, removeStyleSpec.styleName) } - undoManager = manager; - if(undoManager) { - undoManager.setOdtDocument(odtDocument); - undoManager.setPlaybackFunction(function(op) { - op.execute(odtDocument) - }); - undoManager.subscribe(gui.UndoManager.signalUndoStackChanged, forwardUndoStackChange) + } + 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 = [] + } + } + } } - }; - 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) + } + 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) } - async.destroyAll(destroyCallbacks, callback) - }; - function returnTrue(fn) { - return function() { - fn(); - return true + } + if(removeTextSpec.position + removeTextSpec.length <= splitParagraphSpec.position) { + splitParagraphSpec.position -= removeTextSpec.length + }else { + if(removeTextSpec.position < splitParagraphSpec.position) { + splitParagraphSpec.position = removeTextSpec.position } } - function rangeSelectionOnly(fn) { - return function(e) { - var selectionType = odtDocument.getCursor(inputMemberId).getSelectionType(); - if(selectionType === ops.OdtCursor.RangeSelection) { - return fn(e) - } - return true + return{opSpecsA:removeTextSpecResult, opSpecsB:splitParagraphSpecResult} + } + function passUnchanged(opSpecA, opSpecB) { + return{opSpecsA:[opSpecA], opSpecsB:[opSpecB]} + } + var transformations; + 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 } - 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(redrawRegionSelection, 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 { - 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) + 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} } - 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)) + }else { + result = null } - init() - }; - return gui.SessionController -}(); + runtime.log("result:"); + if(result) { + runtime.log(runtime.toJson(result.opSpecsA)); + runtime.log(runtime.toJson(result.opSpecsB)) + }else { + runtime.log("null") + } + return result + } +}; /* - 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"); -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] - }) - } - function getCanvasElement() { - return sessionController.getSession().getOdtDocument().getOdfCanvas().getElement() +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 removeCaret(memberId) { - if(memberId === sessionController.getInputMemberId()) { - getCanvasElement().removeAttribute("tabindex") - } - delete carets[memberId] + function transformOpVsOp(opSpecA, opSpecB) { + return operationTransformMatrix.transformOpspecVsOpspec(opSpecA, opSpecB) } - function refreshLocalCaretBlinking(cursor) { - var caret, memberId = cursor.getMemberId(); - if(memberId === sessionController.getInputMemberId()) { - caret = getCaret(memberId); - if(caret) { - caret.refreshCursorBlinking() + function transformOpListVsOp(opSpecsA, opSpecB) { + var transformResult, transformListResult, transformedOpspecsA = [], transformedOpspecsB = []; + while(opSpecsA.length > 0 && opSpecB) { + transformResult = transformOpVsOp(opSpecsA.shift(), opSpecB); + if(!transformResult) { + return null } - } - } - function executeEnsureCaretVisible() { - var caret = getCaret(sessionController.getInputMemberId()); - scrollIntoViewScheduled = false; - if(caret) { - caret.ensureVisible() - } - } - function scheduleCaretVisibilityCheck() { - var caret = getCaret(sessionController.getInputMemberId()); - if(caret) { - caret.handleUpdate(); - if(!scrollIntoViewScheduled) { - scrollIntoViewScheduled = true; - runtime.setTimeout(executeEnsureCaretVisible, 50) + 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() } - } - function ensureLocalCaretVisible(info) { - if(info.memberId === sessionController.getInputMemberId()) { - scheduleCaretVisibilityCheck() - } - } - function focusLocalCaret() { - var caret = getCaret(sessionController.getInputMemberId()); - if(caret) { - caret.setFocus() - } - } - function blurLocalCaret() { - var caret = getCaret(sessionController.getInputMemberId()); - if(caret) { - caret.removeFocus() - } - } - function showLocalCaret() { - var caret = getCaret(sessionController.getInputMemberId()); - if(caret) { - caret.show() - } - } - function hideLocalCaret() { - var caret = getCaret(sessionController.getInputMemberId()); - if(caret) { - caret.hide() + if(opSpecB) { + transformedOpspecsB.push(opSpecB) } + return{opSpecsA:transformedOpspecsA, opSpecsB:transformedOpspecsB} } - 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.setOperationFactory = function(f) { + operationFactory = f }; - 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 = {} + this.getOperationTransformMatrix = function() { + return operationTransformMatrix }; - 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) + 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) + } + return{opsA:operations(opSpecsA), opsB:operations(transformedOpspecsB)} } - 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 @@ -17964,230 +17656,19 @@ gui.CaretManager = function CaretManager(sessionController) { @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 +ops.Server = function Server() { }; -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 - } - 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 { - 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)) - } - 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() - } - 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"; +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) { +}; +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@namespace svgns url(http://www.w3.org/2000/svg);\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\noffice|document *::selection {\n background: transparent;\n}\noffice|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}\ndraw|frame {\n /** make sure frames are above the main body. */\n z-index: 1;\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}\noffice|text[webodfhelper|links="inactive"] text|a {\n cursor: text;\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: 0;\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 pointer-events: none;\n}\n\ncursor|cursor > .caret {\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 > .handle {\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 > .handle > img {\n border-radius: 5px;\n}\n\ncursor|cursor > .handle.active {\n opacity: 0.8;\n}\n\ncursor|cursor > .handle: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/** Input Method Editor input pane & behaviours */\n/* not within a cursor */\n#eventTrap {\n height: auto;\n display: block;\n position: absolute;\n width: 1px;\n outline: none;\n opacity: 0;\n color: rgba(255, 255, 255, 0); /* hide the blinking caret by setting the colour to fully transparent */\n overflow: hidden; /* The overflow visibility is used to hide and show characters being entered */\n pointer-events: none;\n}\n\n/* within a cursor */\ncursor|cursor > #composer {\n text-decoration: underline;\n}\n\ncursor|cursor[cursor|composing="true"] > #composer {\n display: inline-block;\n height: auto;\n width: auto;\n}\n\ncursor|cursor[cursor|composing="true"] {\n display: inline-block;\n width: auto;\n height: inherit;\n}\n\ncursor|cursor[cursor|composing="true"] > .caret {\n /* during composition, the caret should be pushed along by the composition text, inline with the text */\n position: static;\n /* as it is now part of an inline-block, it will no longer need correct to top or height values to align properly */\n height: auto !important;\n top: auto !important;\n}\n\neditinfo|editinfo {\n /* Empty or invisible display:inline elements respond very badly to mouse selection.\n Inline blocks are much more reliably selectable in Chrome & friends */\n display: inline-block;\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 color: black;\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 pointer-events: none;\n top: 0;\n left: 0;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 15;\n}\n.selectionOverlay > polygon {\n fill-opacity: 0.3;\n stroke-opacity: 0.8;\n stroke-width: 1;\n fill-rule: evenodd;\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\ndiv.customScrollbars::-webkit-scrollbar\n{\n width: 8px;\n height: 8px;\n background-color: transparent;\n}\n\ndiv.customScrollbars::-webkit-scrollbar-track\n{\n background-color: transparent;\n}\n\ndiv.customScrollbars::-webkit-scrollbar-thumb\n{\n background-color: #444;\n border-radius: 4px;\n}\n'; diff --git a/js/3rdparty/webodf/webodf.js b/js/3rdparty/webodf/webodf.js index 688b4a12..2690b0fd 100644 --- a/js/3rdparty/webodf/webodf.js +++ b/js/3rdparty/webodf/webodf.js @@ -1,55 +1,57 @@ // Input 0 -var webodf_version="0.4.2-1579-gfc3a4e6"; +var webodf_version="0.4.2-2010-ge06842f"; // 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,f,p){};Runtime.prototype.readFile=function(g,l,f){};Runtime.prototype.readFileSync=function(g,l){};Runtime.prototype.loadXML=function(g,l){};Runtime.prototype.writeFile=function(g,l,f){}; -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.currentDirectory=function(){};Runtime.prototype.setCurrentDirectory=function(g){};Runtime.prototype.type=function(){};Runtime.prototype.getDOMImplementation=function(){}; -Runtime.prototype.parseXML=function(g){};Runtime.prototype.exit=function(g){};Runtime.prototype.getWindow=function(){};Runtime.prototype.assert=function(g,l,f){};var IS_COMPILED_CODE=!0; -Runtime.byteArrayToString=function(g,l){function f(f){var h="",d,m=f.length;for(d=0;de?c.push(e):(d+=1,a=f[d],194<=e&&224>e?c.push((e&31)<<6|a&63):(d+=1,b=f[d],224<=e&&240>e?c.push((e&15)<<12|(a&63)<<6|b&63):(d+=1,q=f[d],240<=e&&245>e&&(e=(e&7)<<18|(a&63)<<12|(b&63)<<6|q&63,e-=65536,c.push((e>>10)+55296,(e&1023)+56320))))),1E3===c.length&&(h+=String.fromCharCode.apply(null, -c),c.length=0);return h+String.fromCharCode.apply(null,c)}var r;"utf8"===l?r=p(g):("binary"!==l&&this.log("Unsupported encoding: "+l),r=f(g));return r};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(d,m){var c,e,a;void 0!==m?a=d:m=d;g?(e=g.ownerDocument,a&&(c=e.createElement("span"),c.className=a,c.appendChild(e.createTextNode(a)),g.appendChild(c),g.appendChild(e.createTextNode(" "))),c=e.createElement("span"),0b?(e[q]=b,q+=1):2048>b?(e[q]=192|b>>>6,e[q+1]=128|b&63,q+=2):(e[q]=224|b>>>12&15,e[q+1]=128|b>>>6&63,e[q+2]=128|b&63,q+=3)}else for("binary"!==m&&n.log("unknown encoding: "+m),c=d.length,e=new Uint8Array(new ArrayBuffer(c)),a=0;ae.status||0===e.status?c(null):c("Status "+String(e.status)+": "+e.responseText||e.statusText):c("File "+d+" is empty."))};a=m.buffer&&!e.sendAsBinary?m.buffer:n.byteArrayToString(m,"binary");try{e.sendAsBinary?e.sendAsBinary(a):e.send(a)}catch(b){n.log("HUH? "+b+" "+m),c(b.message)}};this.deleteFile=function(d,m){delete h[d];var c=new XMLHttpRequest;c.open("DELETE",d,!0);c.onreadystatechange=function(){4===c.readyState&& -(200>c.status&&300<=c.status?m(c.responseText):m(null))};c.send(null)};this.loadXML=function(d,m){var c=new XMLHttpRequest;c.open("GET",d,!0);c.overrideMimeType&&c.overrideMimeType("text/xml");c.onreadystatechange=function(){4===c.readyState&&(0!==c.status||c.responseText?200===c.status||0===c.status?m(null,c.responseXML):m(c.responseText,null):m("File "+d+" is empty.",null))};try{c.send(null)}catch(e){m(e.message,null)}};this.isFile=function(d,m){n.getFileSize(d,function(c){m(-1!==c)})};this.getFileSize= -function(d,m){if(h.hasOwnProperty(d)&&"string"!==typeof h[d])m(h[d].length);else{var c=new XMLHttpRequest;c.open("HEAD",d,!0);c.onreadystatechange=function(){if(4===c.readyState){var e=c.getResponseHeader("Content-Length");e?m(parseInt(e,10)):r(d,"binary",function(a,b){a?m(-1):m(b.length)})}};c.send(null)}};this.log=l;this.assert=function(d,m,c){if(!d)throw l("alert","ASSERTION FAILED:\n"+m),c&&c(),m;};this.setTimeout=function(d,m){return setTimeout(function(){d()},m)};this.clearTimeout=function(d){clearTimeout(d)}; -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(d){return(new DOMParser).parseFromString(d,"text/xml")};this.exit=function(d){l("Calling exit with code "+String(d)+", but exit() is not implemented.")};this.getWindow=function(){return window}} -function NodeJSRuntime(){function g(d){var c=d.length,e,a=new Uint8Array(new ArrayBuffer(c));for(e=0;e").implementation} -function RhinoRuntime(){function g(d,h){var c;void 0!==h?c=d:h=d;"alert"===c&&print("\n!!!!! ALERT !!!!!");print(h);"alert"===c&&print("!!!!! ALERT !!!!!")}var l=this,f={},p=f.javax.xml.parsers.DocumentBuilderFactory.newInstance(),r,n,h="";p.setValidating(!1);p.setNamespaceAware(!0);p.setExpandEntityReferences(!1);p.setSchema(null);n=f.org.xml.sax.EntityResolver({resolveEntity:function(d,h){var c=new f.java.io.FileReader(h);return new f.org.xml.sax.InputSource(c)}});r=p.newDocumentBuilder();r.setEntityResolver(n); -this.byteArrayFromString=function(d,h){var c,e=d.length,a=new Uint8Array(new ArrayBuffer(e));for(c=0;cf?l.push(f):(q+=1,c=b[q],194<=f&&224>f?l.push((f&31)<<6|c&63):(q+=1,a=b[q],224<=f&&240>f?l.push((f&15)<<12|(c&63)<<6|a&63):(q+=1,m=b[q],240<=f&&245>f&&(f=(f&7)<<18|(c&63)<<12|(a&63)<<6|m&63,f-=65536,l.push((f>>10)+55296,(f&1023)+56320))))),1E3===l.length&&(g+=String.fromCharCode.apply(null, +l),l.length=0);return g+String.fromCharCode.apply(null,l)}var d;"utf8"===h?d=p(k):("binary"!==h&&this.log("Unsupported encoding: "+h),d=b(k));return d};Runtime.getVariable=function(k){try{return eval(k)}catch(h){}};Runtime.toJson=function(k){return JSON.stringify(k)};Runtime.fromJson=function(k){return JSON.parse(k)};Runtime.getFunctionName=function(k){return void 0===k.name?(k=/function\s+(\w+)/.exec(k))&&k[1]:k.name}; +function BrowserRuntime(k){function h(f){var c=f.length,a,m,e=0;for(a=0;am&&(e+=1,a+=1);return e}function b(f,c,a){var m=f.length,e,b;c=new Uint8Array(new ArrayBuffer(c));a?(c[0]=239,c[1]=187,c[2]=191,b=3):b=0;for(a=0;ae?(c[b]=e,b+=1):2048>e?(c[b]=192|e>>>6,c[b+1]=128|e&63,b+=2):55040>=e||57344<=e?(c[b]=224|e>>>12&15,c[b+1]=128|e>>>6&63,c[b+2]=128|e&63,b+=3):(a+=1,e=(e-55296<<10|f.charCodeAt(a)-56320)+65536, +c[b]=240|e>>>18&7,c[b+1]=128|e>>>12&63,c[b+2]=128|e>>>6&63,c[b+3]=128|e&63,b+=4);return c}function p(f){var c=f.length,a=new Uint8Array(new ArrayBuffer(c)),m;for(m=0;mm.status||0===m.status?a(null):a("Status "+String(m.status)+": "+m.responseText||m.statusText):a("File "+f+" is empty."))};e=c.buffer&&!m.sendAsBinary?c.buffer:r.byteArrayToString(c,"binary");try{m.sendAsBinary?m.sendAsBinary(e):m.send(e)}catch(b){r.log("HUH? "+b+" "+c),a(b.message)}};this.deleteFile=function(f,c){delete l[f];var a=new XMLHttpRequest;a.open("DELETE",f,!0);a.onreadystatechange= +function(){4===a.readyState&&(200>a.status&&300<=a.status?c(a.responseText):c(null))};a.send(null)};this.loadXML=function(f,c){var a=new XMLHttpRequest;a.open("GET",f,!0);a.overrideMimeType&&a.overrideMimeType("text/xml");a.onreadystatechange=function(){4===a.readyState&&(0!==a.status||a.responseText?200===a.status||0===a.status?c(null,a.responseXML):c(a.responseText,null):c("File "+f+" is empty.",null))};try{a.send(null)}catch(m){c(m.message,null)}};this.isFile=function(f,c){r.getFileSize(f,function(a){c(-1!== +a)})};this.getFileSize=function(f,c){if(l.hasOwnProperty(f)&&"string"!==typeof l[f])c(l[f].length);else{var a=new XMLHttpRequest;a.open("HEAD",f,!0);a.onreadystatechange=function(){if(4===a.readyState){var m=a.getResponseHeader("Content-Length");m?c(parseInt(m,10)):q(f,"binary",function(a,f){a?c(-1):c(f.length)})}};a.send(null)}};this.log=d;this.assert=function(f,c,a){if(!f)throw d("alert","ASSERTION FAILED:\n"+c),a&&a(),c;};this.setTimeout=function(f,c){return setTimeout(function(){f()},c)};this.clearTimeout= +function(f){clearTimeout(f)};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(f){return(new DOMParser).parseFromString(f,"text/xml")};this.exit=function(f){d("Calling exit with code "+String(f)+", but exit() is not implemented.")};this.getWindow=function(){return window};this.requestAnimationFrame= +function(f){var c=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame,a=0;if(c)c.bind(window),a=c(f);else return setTimeout(f,15);return a};this.cancelAnimationFrame=function(f){var c=window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.msCancelAnimationFrame;c?(c.bind(window),c(f)):clearTimeout(f)}} +function NodeJSRuntime(){function k(b){var l=b.length,f,c=new Uint8Array(new ArrayBuffer(l));for(f=0;f").implementation} +function RhinoRuntime(){function k(b,g){var l;void 0!==g?l=b:g=b;"alert"===l&&print("\n!!!!! ALERT !!!!!");print(g);"alert"===l&&print("!!!!! ALERT !!!!!")}var h=this,b={},p=b.javax.xml.parsers.DocumentBuilderFactory.newInstance(),d,n,g="";p.setValidating(!1);p.setNamespaceAware(!0);p.setExpandEntityReferences(!1);p.setSchema(null);n=b.org.xml.sax.EntityResolver({resolveEntity:function(q,g){var l=new b.java.io.FileReader(g);return new b.org.xml.sax.InputSource(l)}});d=p.newDocumentBuilder();d.setEntityResolver(n); +this.byteArrayFromString=function(b,g){var l,f=b.length,c=new Uint8Array(new ArrayBuffer(f));for(l=0;l>>18],c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[b>>>12&63],c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[b>>>6&63],c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[b& -63];e===q+1?(b=a[e]<<4,c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[b>>>6],c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[b&63],c+="=="):e===q&&(b=a[e]<<10|a[e+1]<<2,c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[b>>>12],c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[b>>>6&63],c+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[b&63],c+="=");return c}function f(a){a=a.replace(/[^A-Za-z0-9+\/]+/g, -"");var b=a.length,c=new Uint8Array(new ArrayBuffer(3*b)),e=a.length%4,q=0,d,f;for(d=0;d>16,c[q+1]=f>>8&255,c[q+2]=f&255,q+=3;b=3*b-[0,0,2,1][e];return c.subarray(0,b)}function p(a){var b,c,e=a.length,q=0,k=new Uint8Array(new ArrayBuffer(3*e));for(b=0;bc?k[q++]=c:(2048>c?k[q++]=192|c>>>6:(k[q++]=224|c>>>12&15,k[q++]=128|c>>>6&63),k[q++]=128|c&63);return k.subarray(0, -q)}function r(a){var b,c,e,q,k=a.length,d=new Uint8Array(new ArrayBuffer(k)),f=0;for(b=0;bc?d[f++]=c:(b+=1,e=a[b],224>c?d[f++]=(c&31)<<6|e&63:(b+=1,q=a[b],d[f++]=(c&15)<<12|(e&63)<<6|q&63));return d.subarray(0,f)}function n(a){return l(g(a))}function h(a){return String.fromCharCode.apply(String,f(a))}function d(a){return r(g(a))}function m(a){a=r(a);for(var b="",c=0;cb?d+=String.fromCharCode(b):(k+=1,e=a.charCodeAt(k)&255,224>b?d+=String.fromCharCode((b&31)<<6|e&63):(k+=1,q=a.charCodeAt(k)&255,d+=String.fromCharCode((b&15)<<12|(e&63)<<6|q&63)));return d}function e(a,b){function e(){var d=k+1E5;d>a.length&&(d=a.length);q+=c(a,k,d);k=d;d=k===a.length;b(q,d)&&!d&&runtime.setTimeout(e,0)}var q="",k=0;1E5>a.length?b(c(a,0,a.length),!0):("string"!==typeof a&&(a=a.slice()),e())}function a(a){return p(g(a))}function b(a){return String.fromCharCode.apply(String, -p(a))}function q(a){return String.fromCharCode.apply(String,p(g(a)))}var k=function(a){var b={},c,e;c=0;for(e=a.length;c>>18],e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c>>>12&63],e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c>>>6&63],e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c& +63];f===b+1?(c=a[f]<<4,e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c>>>6],e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c&63],e+="=="):f===b&&(c=a[f]<<10|a[f+1]<<2,e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c>>>12],e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c>>>6&63],e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c&63],e+="=");return e}function b(a){a=a.replace(/[^A-Za-z0-9+\/]+/g, +"");var c=a.length,f=new Uint8Array(new ArrayBuffer(3*c)),b=a.length%4,m=0,d,g;for(d=0;d>16,f[m+1]=g>>8&255,f[m+2]=g&255,m+=3;c=3*c-[0,0,2,1][b];return f.subarray(0,c)}function p(a){var c,e,f=a.length,b=0,m=new Uint8Array(new ArrayBuffer(3*f));for(c=0;ce?m[b++]=e:(2048>e?m[b++]=192|e>>>6:(m[b++]=224|e>>>12&15,m[b++]=128|e>>>6&63),m[b++]=128|e&63);return m.subarray(0, +b)}function d(a){var c,e,f,b,m=a.length,d=new Uint8Array(new ArrayBuffer(m)),g=0;for(c=0;ce?d[g++]=e:(c+=1,f=a[c],224>e?d[g++]=(e&31)<<6|f&63:(c+=1,b=a[c],d[g++]=(e&15)<<12|(f&63)<<6|b&63));return d.subarray(0,g)}function n(a){return h(k(a))}function g(a){return String.fromCharCode.apply(String,b(a))}function q(a){return d(k(a))}function r(a){a=d(a);for(var c="",e=0;ec?d+=String.fromCharCode(c):(m+=1,f=a.charCodeAt(m)&255,224>c?d+=String.fromCharCode((c&31)<<6|f&63):(m+=1,b=a.charCodeAt(m)&255,d+=String.fromCharCode((c&15)<<12|(f&63)<<6|b&63)));return d}function f(a,c){function e(){var m=b+1E5;m>a.length&&(m=a.length);f+=l(a,b,m);b=m;m=b===a.length;c(f,m)&&!m&&runtime.setTimeout(e,0)}var f="",b=0;1E5>a.length?c(l(a,0,a.length),!0):("string"!==typeof a&&(a=a.slice()),e())}function c(a){return p(k(a))}function a(a){return String.fromCharCode.apply(String, +p(a))}function m(a){return String.fromCharCode.apply(String,p(k(a)))}var e=function(a){var c={},e,f;e=0;for(f=a.length;er-p&&(r=Math.max(2*r,p+f),f=new Uint8Array(new ArrayBuffer(r)),f.set(n),n=f)}var f=this,p=0,r=1024,n=new Uint8Array(new ArrayBuffer(r));this.appendByteArrayWriter=function(h){f.appendByteArray(h.getByteArray())};this.appendByteArray=function(f){var d=f.length;l(d);n.set(f,p);p+=d};this.appendArray=function(f){var d=f.length;l(d);n.set(f,p);p+=d};this.appendUInt16LE=function(h){f.appendArray([h&255,h>>8&255])};this.appendUInt32LE=function(h){f.appendArray([h& -255,h>>8&255,h>>16&255,h>>24&255])};this.appendString=function(h){f.appendByteArray(runtime.byteArrayFromString(h,g))};this.getLength=function(){return p};this.getByteArray=function(){var f=new Uint8Array(new ArrayBuffer(p));f.set(n.subarray(0,p));return f}}; +core.ByteArrayWriter=function(k){function h(b){b>d-p&&(d=Math.max(2*d,p+b),b=new Uint8Array(new ArrayBuffer(d)),b.set(n),n=b)}var b=this,p=0,d=1024,n=new Uint8Array(new ArrayBuffer(d));this.appendByteArrayWriter=function(d){b.appendByteArray(d.getByteArray())};this.appendByteArray=function(b){var d=b.length;h(d);n.set(b,p);p+=d};this.appendArray=function(b){var d=b.length;h(d);n.set(b,p);p+=d};this.appendUInt16LE=function(d){b.appendArray([d&255,d>>8&255])};this.appendUInt32LE=function(d){b.appendArray([d& +255,d>>8&255,d>>16&255,d>>24&255])};this.appendString=function(d){b.appendByteArray(runtime.byteArrayFromString(d,k))};this.getLength=function(){return p};this.getByteArray=function(){var b=new Uint8Array(new ArrayBuffer(p));b.set(n.subarray(0,p));return b}}; // Input 6 -core.CSSUnits=function(){var g=this,l={"in":1,cm:2.54,mm:25.4,pt:72,pc:12};this.convert=function(f,g,r){return f*l[r]/l[g]};this.convertMeasure=function(f,l){var r,n;f&&l?(r=parseFloat(f),n=f.replace(r.toString(),""),r=g.convert(r,n,l).toString()):r="";return r};this.getUnits=function(f){return f.substr(f.length-2,f.length)}}; +core.CSSUnits=function(){var k=this,h={"in":1,cm:2.54,mm:25.4,pt:72,pc:12};this.convert=function(b,k,d){return b*h[d]/h[k]};this.convertMeasure=function(b,h){var d,n;b&&h?(d=parseFloat(b),n=b.replace(d.toString(),""),d=k.convert(d,n,h).toString()):d="";return d};this.getUnits=function(b){return b.substr(b.length-2,b.length)}}; // Input 7 /* @@ -88,18 +90,65 @@ core.CSSUnits=function(){var g=this,l={"in":1,cm:2.54,mm:25.4,pt:72,pc:12};this. @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -(function(){function g(){var f,g,r,n,h;void 0===l&&(h=(f=runtime.getWindow())&&f.document,l={rangeBCRIgnoresElementBCR:!1,unscaledRangeClientRects:!1},h&&(n=h.createElement("div"),n.style.position="absolute",n.style.left="-99999px",n.style.transform="scale(2)",n.style["-webkit-transform"]="scale(2)",g=h.createElement("div"),n.appendChild(g),h.body.appendChild(n),f=h.createRange(),f.selectNode(g),l.rangeBCRIgnoresElementBCR=0===f.getClientRects().length,g.appendChild(h.createTextNode("Rect transform test")), -g=g.getBoundingClientRect(),r=f.getBoundingClientRect(),l.unscaledRangeClientRects=2=a.compareBoundaryPoints(Range.START_TO_START,b)&&0<=a.compareBoundaryPoints(Range.END_TO_END,b)}function l(a,b){return 0>=a.compareBoundaryPoints(Range.END_TO_START,b)&&0<= -a.compareBoundaryPoints(Range.START_TO_END,b)}function r(a,b){var c=null;a.nodeType===Node.TEXT_NODE&&(0===a.length?(a.parentNode.removeChild(a),b.nodeType===Node.TEXT_NODE&&(c=b)):(b.nodeType===Node.TEXT_NODE&&(a.appendData(b.data),b.parentNode.removeChild(b)),c=a));return c}function n(a){for(var b=a.parentNode;a.firstChild;)b.insertBefore(a.firstChild,a);b.removeChild(a);return b}function h(a,b){for(var c=a.parentNode,e=a.firstChild,d;e;)d=e.nextSibling,h(e,b),e=d;b(a)&&(c=n(a));return c}function d(a, -b){return a===b||Boolean(a.compareDocumentPosition(b)&Node.DOCUMENT_POSITION_CONTAINED_BY)}function m(a,b){for(var c=0,e;a.parentNode!==b;)runtime.assert(null!==a.parentNode,"parent is null"),a=a.parentNode;for(e=b.firstChild;e!==a;)c+=1,e=e.nextSibling;return c}function c(a,b,e){Object.keys(b).forEach(function(k){var d=k.split(":"),f=d[1],h=e(d[0]),d=b[k];"object"===typeof d&&Object.keys(d).length?(k=h?a.getElementsByTagNameNS(h,f)[0]||a.ownerDocument.createElementNS(h,k):a.getElementsByTagName(f)[0]|| -a.ownerDocument.createElement(k),a.appendChild(k),c(k,d,e)):h&&a.setAttributeNS(h,k,String(d))})}var e=null;this.splitBoundaries=function(a){var b=[],c,e;if(a.startContainer.nodeType===Node.TEXT_NODE||a.endContainer.nodeType===Node.TEXT_NODE){if(c=a.endContainer){c=a.endOffset;e=a.endContainer;if(c=f.compareBoundaryPoints(Range.START_TO_START,c)&&0<=f.compareBoundaryPoints(Range.END_TO_END, +c)}function d(f,c){var a=null;f.nodeType===Node.TEXT_NODE&&(0===f.length?(f.parentNode.removeChild(f),c.nodeType===Node.TEXT_NODE&&(a=c)):(c.nodeType===Node.TEXT_NODE&&(f.appendData(c.data),c.parentNode.removeChild(c)),a=f));return a}function n(f){for(var c=f.parentNode;f.firstChild;)c.insertBefore(f.firstChild,f);c.removeChild(f);return c}function g(f,c){for(var a=f.parentNode,b=f.firstChild,e;b;)e=b.nextSibling,g(b,c),b=e;a&&c(f)&&n(f);return a}function q(b,c){return b===c||Boolean(b.compareDocumentPosition(c)& +Node.DOCUMENT_POSITION_CONTAINED_BY)}function r(b,c,a){Object.keys(c).forEach(function(m){var e=m.split(":"),d=e[1],l=a(e[0]),e=c[m],g=typeof e;"object"===g?Object.keys(e).length&&(m=l?b.getElementsByTagNameNS(l,d)[0]||b.ownerDocument.createElementNS(l,m):b.getElementsByTagName(d)[0]||b.ownerDocument.createElement(m),b.appendChild(m),r(m,e,a)):l&&(runtime.assert("number"===g||"string"===g,"attempting to map unsupported type '"+g+"' (key: "+m+")"),b.setAttributeNS(l,m,String(e)))})}var l=null;this.splitBoundaries= +function(f){var c,a=[],m,e,d;if(f.startContainer.nodeType===Node.TEXT_NODE||f.endContainer.nodeType===Node.TEXT_NODE){m=f.endContainer;e=f.endContainer.nodeType!==Node.TEXT_NODE?f.endOffset===f.endContainer.childNodes.length:!1;d=f.endOffset;c=f.endContainer;if(d=b.compareBoundaryPoints(Range.END_TO_START, +c)&&0<=b.compareBoundaryPoints(Range.START_TO_END,c)};this.getNodesInRange=function(b,c,a){var m=[],e=b.commonAncestorContainer;a=b.startContainer.ownerDocument.createTreeWalker(e.nodeType===Node.TEXT_NODE?e.parentNode:e,a,c,!1);var d;b.endContainer.childNodes[b.endOffset-1]?(e=b.endContainer.childNodes[b.endOffset-1],d=Node.DOCUMENT_POSITION_PRECEDING|Node.DOCUMENT_POSITION_CONTAINED_BY):(e=b.endContainer,d=Node.DOCUMENT_POSITION_PRECEDING);b.startContainer.childNodes[b.startOffset]?(b=b.startContainer.childNodes[b.startOffset], +a.currentNode=b):b.startOffset===(b.startContainer.nodeType===Node.TEXT_NODE?b.startContainer.length:b.startContainer.childNodes.length)?(b=b.startContainer,a.currentNode=b,a.lastChild(),b=a.nextNode()):(b=b.startContainer,a.currentNode=b);b&&c(b)===NodeFilter.FILTER_ACCEPT&&m.push(b);for(b=a.nextNode();b;){c=e.compareDocumentPosition(b);if(0!==c&&0===(c&d))break;m.push(b);b=a.nextNode()}return m};this.normalizeTextNodes=function(b){b&&b.nextSibling&&(b=d(b,b.nextSibling));b&&b.previousSibling&&d(b.previousSibling, +b)};this.rangeContainsNode=function(b,c){var a=c.ownerDocument.createRange(),m=c.ownerDocument.createRange(),e;a.setStart(b.startContainer,b.startOffset);a.setEnd(b.endContainer,b.endOffset);m.selectNodeContents(c);e=h(a,m);a.detach();m.detach();return e};this.mergeIntoParent=n;this.removeUnwantedNodes=g;this.getElementsByTagNameNS=function(b,c,a){var m=[];b=b.getElementsByTagNameNS(c,a);m.length=a=b.length;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/ +*/ +core.Destroyable=function(){};core.Destroyable.prototype.destroy=function(k){}; +// Input 10 /* Copyright (C) 2013 KO GmbH @@ -137,9 +186,9 @@ b+" not found."):runtime.log("Property Name ignored: "+b)})};this.getKeyValRepre @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -core.EventNotifier=function(g){var l={};this.emit=function(f,g){var r,n;runtime.assert(l.hasOwnProperty(f),'unknown event fired "'+f+'"');n=l[f];for(r=0;r @@ -177,46 +226,93 @@ r,'tried to unsubscribe unknown callback from event "'+f+'"');-1!==r&&l[f].splic @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -core.LoopWatchDog=function(g,l){var f=Date.now(),p=0;this.check=function(){var r;if(g&&(r=Date.now(),r-f>g))throw runtime.log("alert","watchdog timeout"),"timeout!";if(0l))throw runtime.log("alert","watchdog loop overflow"),"loop overflow";}}; -// Input 10 -core.PositionIterator=function(g,l,f,p){function r(){this.acceptNode=function(a){return!a||a.nodeType===b&&0===a.length?t:k}}function n(a){this.acceptNode=function(c){return!c||c.nodeType===b&&0===c.length?t:a.acceptNode(c)}}function h(){var a=c.currentNode,d=a.nodeType;e=d===b?a.length-1:d===q?1:0}function d(){if(null===c.previousSibling()){if(!c.parentNode()||c.currentNode===g)return c.firstChild(),!1;e=0}else h();return!0}var m=this,c,e,a,b=Node.TEXT_NODE,q=Node.ELEMENT_NODE,k=NodeFilter.FILTER_ACCEPT, -t=NodeFilter.FILTER_REJECT;this.nextPosition=function(){var a=c.currentNode,d=a.nodeType;if(a===g)return!1;if(0===e&&d===q)null===c.firstChild()&&(e=1);else if(d===b&&e+1 "+d.length),runtime.assert(0<=q,"Error in setPosition: "+q+" < 0"),q===d.length&&(c.nextSibling()?e=0:c.parentNode()?e=1:runtime.assert(!1,"Error in setUnfilteredPosition: position not valid.")),!0;f=a(d);for(h=d.parentNode;h&&h!==g&&f===k;)f=a(h),f!==k&&(c.currentNode=h),h=h.parentNode;qm&&(q=m);for(z=1<z){this.status=2;this.m=q;return}z-=f[m];if(0>z)this.status=2,this.m=q;else{f[m]+=z;y[1]=s=0;r=f;p=1;for(v=2;0<--m;)s+=r[p++],y[v++]=s;r=a;m=p=0;do s=r[p++],0!==s&&(g[y[s]++]=m);while(++mu+n[1+g];){u+= -n[1+g];g++;Q=I-u;Q=Q>q?q:Q;s=l-u;h=1<a+1)for(h-=a+1,v=l;++sk&&u>u-n[g],W[g-1][s].e=t.e,W[g-1][s].b=t.b,W[g-1][s].n=t.n,W[g-1][s].t=t.t)}t.b=l-u;p>=b?t.e=99:r[p]r[p]?16:15,t.n=r[p++]):(t.e=d[r[p]-c],t.n=e[r[p++]- -c]);h=1<>u;s>=1)m^=s;for(m^=s;(m&(1<>=c;b-=c}function r(a,b,c){var e,k,I;if(0===c)return 0;for(I=0;;){l(v);k=w.list[f(v)];for(e=k.e;16d;d++)n[Q[d]]=0;v= -7;d=new g(n,19,19,null,null,v);if(0!==d.status)return-1;w=d.root;v=d.m;k=m+s;for(e=q=0;ed)n[e++]=q=d;else if(16===d){l(2);d=3+f(2);p(2);if(e+d>k)return-1;for(;0k)return-1;for(;0C;C++)F[C]=8;for(C=144;256>C;C++)F[C]=9;for(C=256;280>C;C++)F[C]=7;for(C=280;288>C;C++)F[C]=8;e=7;C=new g(F,288,257,B,L,e);if(0!==C.status){alert("HufBuild error: "+C.status);O=-1;break b}m=C.root;e=C.m;for(C=0;30>C;C++)F[C]= -5;z=5;C=new g(F,30,0,I,W,z);if(1k))throw runtime.log("alert","watchdog timeout"),"timeout!";if(0h))throw runtime.log("alert","watchdog loop overflow"),"loop overflow";}}; // Input 12 -core.ScheduledTask=function(g,l){function f(){n&&(runtime.clearTimeout(r),n=!1)}function p(){f();g.apply(void 0,h);h=null}var r,n=!1,h=[];this.trigger=function(){h=Array.prototype.slice.call(arguments);n||(n=!0,r=runtime.setTimeout(p,l))};this.triggerImmediate=function(){h=Array.prototype.slice.call(arguments);p()};this.processRequests=function(){n&&p()};this.cancel=f;this.destroy=function(d){f();d()}}; +core.PositionIterator=function(k,h,b,p){function d(){this.acceptNode=function(a){return!a||a.nodeType===m&&0===a.length?w:t}}function n(a){this.acceptNode=function(c){return!c||c.nodeType===m&&0===c.length?w:a.acceptNode(c)}}function g(){var a=f.currentNode,b=a.nodeType;c=b===m?a.length-1:b===e?1:0}function q(){if(null===f.previousSibling()){if(!f.parentNode()||f.currentNode===k)return f.firstChild(),!1;c=0}else g();return!0}function r(){var b=f.currentNode,e;e=a(b);if(b!==k)for(b=b.parentNode;b&& +b!==k;)a(b)===w&&(f.currentNode=b,e=w),b=b.parentNode;e===w?(c=1,b=l.nextPosition()):b=e===t?!0:l.nextPosition();b&&runtime.assert(a(f.currentNode)===t,"moveToAcceptedNode did not result in walker being on an accepted node");return b}var l=this,f,c,a,m=Node.TEXT_NODE,e=Node.ELEMENT_NODE,t=NodeFilter.FILTER_ACCEPT,w=NodeFilter.FILTER_REJECT;this.nextPosition=function(){var a=f.currentNode,b=a.nodeType;if(a===k)return!1;if(0===c&&b===e)null===f.firstChild()&&(c=1);else if(b===m&&c+1 "+a.length), +runtime.assert(0<=b,"Error in setPosition: "+b+" < 0"),b===a.length&&(f.nextSibling()?c=0:f.parentNode()?c=1:runtime.assert(!1,"Error in setUnfilteredPosition: position not valid.")),!0;b";return runtime.parseXML(f)}; -core.UnitTestRunner=function(){function g(d){h+=1;runtime.log("fail",d)}function l(d,c){var e;try{if(d.length!==c.length)return g("array of length "+d.length+" should be "+c.length+" long"),!1;for(e=0;e1/b?"-0":String(b),g(c+" should be "+d+". Was "+e+".")):g(c+" should be "+d+" (of type "+typeof d+"). Was "+b+" (of type "+typeof b+").")}var h=0,d;d=function(d,c){var e=Object.keys(d),a=Object.keys(c);e.sort();a.sort();return l(e,a)&&Object.keys(d).every(function(a){var e=d[a],f=c[a];return r(e,f)?!0:(g(e+" should be "+f+" for key "+a),!1)})};this.areNodesEqual=p;this.shouldBeNull=function(d,c){n(d,c,"null")};this.shouldBeNonNull=function(d,c){var e,a;try{a=eval(c)}catch(b){e= -b}e?g(c+" should be non-null. Threw exception "+e):null!==a?runtime.log("pass",c+" is non-null."):g(c+" should be non-null. Was "+a)};this.shouldBe=n;this.countFailedTests=function(){return h};this.name=function(d){var c,e,a=[],b=d.length;a.length=b;for(c=0;c"+f+""}var l=0,f={};this.runTests=function(p,r,n){function h(a){if(0===a.length)f[d]=e,l+=m.countFailedTests(),r();else{b=a[0].f;var q=a[0].name;runtime.log("Running "+q);k=m.countFailedTests();c.setUp();b(function(){c.tearDown();e[q]=k===m.countFailedTests();h(a.slice(1))})}}var d=Runtime.getFunctionName(p)||"",m=new core.UnitTestRunner,c=new p(m),e={},a,b,q,k,t="BrowserRuntime"===runtime.type(); -if(f.hasOwnProperty(d))runtime.log("Test "+d+" has already run.");else{t?runtime.log("Running "+g(d,'runSuite("'+d+'");')+": "+c.description()+""):runtime.log("Running "+d+": "+c.description);q=c.tests();for(a=0;aRunning "+g(p,'runTest("'+d+'","'+p+'")')+""):runtime.log("Running "+p),k=m.countFailedTests(),c.setUp(),b(),c.tearDown(),e[p]=k===m.countFailedTests());h(c.asyncTests())}};this.countFailedTests= -function(){return l};this.results=function(){return f}}; +core.PositionFilter=function(){};core.PositionFilter.FilterResult={FILTER_ACCEPT:1,FILTER_REJECT:2,FILTER_SKIP:3};core.PositionFilter.prototype.acceptPosition=function(k){};(function(){return core.PositionFilter})(); // Input 14 -core.Utils=function(){function g(l,f){if(f&&Array.isArray(f)){l=l||[];if(!Array.isArray(l))throw"Destination is not an array.";l=l.concat(f.map(function(f){return g(null,f)}))}else if(f&&"object"===typeof f){l=l||{};if("object"!==typeof l)throw"Destination is not an object.";Object.keys(f).forEach(function(p){l[p]=g(l[p],f[p])})}else l=f;return l}this.hashString=function(g){var f=0,p,r;p=0;for(r=g.length;pA&&(f=A);for(D=1<D){this.status=2;this.m=f;return}D-=d[A];if(0>D)this.status=2,this.m=f;else{d[A]+=D;G[1]=n=0;F=d;t=1;for(r=2;0<--A;)n+=F[t++],G[r++]=n;F=a;A=t=0;do n=F[t++],0!==n&&(k[G[n]++]=A);while(++Av+p[1+k];){v+= +p[1+k];k++;u=h-v;u=u>f?f:u;n=q-v;g=1<a+1)for(g-=a+1,r=q;++nl&&v>v-p[k],J[k-1][n].e=s.e,J[k-1][n].b=s.b,J[k-1][n].n=s.n,J[k-1][n].t=s.t)}s.b=q-v;t>=c?s.e=99:F[t]F[t]?16:15,s.n=F[t++]):(s.e=m[F[t]-b],s.n=e[F[t++]- +b]);g=1<>v;n>=1)A^=n;for(A^=n;(A&(1<>=b;a-=b}function d(a,c,e){var f,d,l;if(0===e)return 0;for(l=0;;){h(v);d=z.list[b(v)];for(f=d.e;16f;f++)t[K[f]]=0;v= +7;f=new k(t,19,19,null,null,v);if(0!==f.status)return-1;z=f.root;v=f.m;g=n+q;for(m=l=0;mf)t[m++]=l=f;else if(16===f){h(2);f=3+b(2);p(2);if(m+f>g)return-1;for(;0g)return-1;for(;0I;I++)R[I]=8;for(I=144;256>I;I++)R[I]=9;for(I=256;280>I;I++)R[I]=7;for(I=280;288>I;I++)R[I]=8;f=7;I=new k(R,288,257,G,D,f);if(0!==I.status){alert("HufBuild error: "+I.status);y=-1;break b}r=I.root;f=I.m;for(I=0;30>I;I++)R[I]= +5;Z=5;I=new k(R,30,0,F,O,Z);if(1 + + @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.StepIterator=function(k,h){function b(){f=null;a=c=void 0}function p(){void 0===a&&(a=k.acceptPosition(h)===l);return a}function d(a,c){b();return h.setUnfilteredPosition(a,c)}function n(){f||(f=h.container());return f}function g(){void 0===c&&(c=h.unfilteredDomOffset());return c}function q(){for(b();h.nextPosition();)if(b(),p())return!0;return!1}function r(){for(b();h.previousPosition();)if(b(),p())return!0;return!1}var l=core.PositionFilter.FilterResult.FILTER_ACCEPT,f,c,a;this.isStep=p;this.setPosition= +d;this.container=n;this.offset=g;this.nextStep=q;this.previousStep=r;this.roundToClosestStep=function(){var a=n(),c=g(),b=p();b||(b=r(),b||(d(a,c),b=q()));return b};this.roundToPreviousStep=function(){var a=p();a||(a=r());return a};this.roundToNextStep=function(){var a=p();a||(a=q());return a}}; +// Input 18 +core.UnitTest=function(){};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 k=runtime.getWindow().document,h=k.getElementById("testarea");runtime.assert(!h,'Unclean test environment, found a div with id "testarea".');h=k.createElement("div");h.setAttribute("id","testarea");k.body.appendChild(h);return h}; +core.UnitTest.cleanupTestAreaDiv=function(){var k=runtime.getWindow().document,h=k.getElementById("testarea");runtime.assert(!!h&&h.parentNode===k.body,'Test environment broken, found no div with id "testarea" below body.');k.body.removeChild(h)};core.UnitTest.createOdtDocument=function(k,h){var b="",b=b+"";return runtime.parseXML(b)}; +core.UnitTestLogger=function(){var k=[],h=0,b=0,p="",d="";this.startTest=function(n,g){k=[];h=0;p=n;d=g;b=(new Date).getTime()};this.endTest=function(){var n=(new Date).getTime();return{description:d,suite:[p,d],success:0===h,log:k,time:n-b}};this.debug=function(b){k.push({category:"debug",message:b})};this.fail=function(b){h+=1;k.push({category:"fail",message:b})};this.pass=function(b){k.push({category:"pass",message:b})}}; +core.UnitTestRunner=function(k,h){function b(a){r+=1;c?h.debug(a):h.fail(a)}function p(a,c){var e;try{if(a.length!==c.length)return b("array of length "+a.length+" should be "+c.length+" long"),!1;for(e=0;e1/d?"-0":String(d),b(c+" should be "+a+". Was "+e+".")):b(c+" should be "+a+" (of type "+typeof a+"). Was "+d+" (of type "+typeof d+").")}var r=0,l,f,c=!1;this.resourcePrefix=function(){return k};this.beginExpectFail=function(){l=r;c=!0};this.endExpectFail=function(){var a=l===r;c=!1;r=l;a&&(r+=1,h.fail("Expected at least one failed test, but none registered."))};f=function(a,c){var e=Object.keys(a),f=Object.keys(c);e.sort(); +f.sort();return p(e,f)&&Object.keys(a).every(function(e){var f=a[e],d=c[e];return g(f,d)?!0:(b(f+" should be "+d+" for key "+e),!1)})};this.areNodesEqual=n;this.shouldBeNull=function(a,c){q(a,c,"null")};this.shouldBeNonNull=function(a,c){var e,f;try{f=eval(c)}catch(d){e=d}e?b(c+" should be non-null. Threw exception "+e):null!==f?h.pass(c+" is non-null."):b(c+" should be non-null. Was "+f)};this.shouldBe=q;this.testFailed=b;this.countFailedTests=function(){return r};this.name=function(a){var c,b,f= +[],d=a.length;f.length=d;for(c=0;c"+b+""}function h(d){b.reporter&&b.reporter(d)}var b=this,p=0,d=new core.UnitTestLogger,n={},g="BrowserRuntime"===runtime.type();this.resourcePrefix="";this.reporter=function(b){var d,l;g?runtime.log("Running "+k(b.description,'runTest("'+b.suite[0]+'","'+b.description+'")')+""):runtime.log("Running "+b.description);if(!b.success)for(d=0;dRunning "+k(c,'runSuite("'+c+'");')+": "+m.description()+""):runtime.log("Running "+c+": "+m.description);z=m.tests();for(t=0;t>>8^q;return c^-1}function p(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 r(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 c,e,d,q,f,h,g,m=this;this.load=function(b){if(null!==m.data)b(null,m.data);else{var c=f+34+e+d+256;c+g>k&&(c=k-g);runtime.read(a,g,c,function(c,e){if(c||null===e)b(c,e);else a:{var d=e,k=new core.ByteArray(d),g=k.readUInt32LE(),s;if(67324752!==g)b("File entry signature is wrong."+g.toString()+" "+d.length.toString(),null);else{k.pos+=22;g=k.readUInt16LE();s=k.readUInt16LE();k.pos+=g+s;if(q){d= -d.subarray(k.pos,k.pos+f);if(f!==d.length){b("The amount of compressed bytes read was "+d.length.toString()+" instead of "+f.toString()+" for "+m.filename+" in "+a+".",null);break a}d=A(d,h)}else d=d.subarray(k.pos,k.pos+h);h!==d.length?b("The amount of bytes read was "+d.length.toString()+" instead of "+h.toString()+" for "+m.filename+" in "+a+".",null):(m.data=d,b(null,d))}}})}};this.set=function(a,b,c,e){m.filename=a;m.data=b;m.compressed=c;m.date=e};this.error=null;b&&(c=b.readUInt32LE(),33639248!== -c?this.error="Central directory entry has wrong signature at position "+(b.pos-4).toString()+' for file "'+a+'": '+b.data.length.toString():(b.pos+=6,q=b.readUInt16LE(),this.date=p(b.readUInt32LE()),b.readUInt32LE(),f=b.readUInt32LE(),h=b.readUInt32LE(),e=b.readUInt16LE(),d=b.readUInt16LE(),c=b.readUInt16LE(),b.pos+=8,g=b.readUInt32LE(),this.filename=runtime.byteArrayToString(b.data.subarray(b.pos,b.pos+e),"utf8"),this.data=null,b.pos+=e+d+c))}function h(a,b){if(22!==a.length)b("Central directory length should be 22.", -w);else{var c=new core.ByteArray(a),e;e=c.readUInt32LE();101010256!==e?b("Central directory signature is wrong: "+e.toString(),w):(e=c.readUInt16LE(),0!==e?b("Zip files with non-zero disk numbers are not supported.",w):(e=c.readUInt16LE(),0!==e?b("Zip files with non-zero disk numbers are not supported.",w):(e=c.readUInt16LE(),t=c.readUInt16LE(),e!==t?b("Number of entries is inconsistent.",w):(e=c.readUInt32LE(),c=c.readUInt16LE(),c=k-22-e,runtime.read(g,c,k-c,function(a,c){if(a||null===c)b(a,w);else a:{var e= -new core.ByteArray(c),d,f;q=[];for(d=0;dk?l("File '"+g+"' cannot be read.",w):runtime.read(g,k-22,22,function(a,b){a||null===l||null===b?l(a,w): -h(b,l)})})}; -// Input 16 -gui.Avatar=function(g,l){var f=this,p,r,n;this.setColor=function(f){r.style.borderColor=f};this.setImageUrl=function(h){f.isVisible()?r.src=h:n=h};this.isVisible=function(){return"block"===p.style.display};this.show=function(){n&&(r.src=n,n=void 0);p.style.display="block"};this.hide=function(){p.style.display="none"};this.markAsFocussed=function(f){p.className=f?"active":""};this.destroy=function(f){g.removeChild(p);f()};(function(){var f=g.ownerDocument,d=f.documentElement.namespaceURI;p=f.createElementNS(d, -"div");r=f.createElementNS(d,"img");r.width=64;r.height=64;p.appendChild(r);p.style.width="64px";p.style.height="70px";p.style.position="absolute";p.style.top="-80px";p.style.left="-34px";p.style.display=l?"block":"none";g.appendChild(p)})()}; -// Input 17 -gui.EditInfoHandle=function(g){var l=[],f,p=g.ownerDocument,r=p.documentElement.namespaceURI;this.setEdits=function(g){l=g;var h,d,m,c;f.innerHTML="";for(g=0;g>>8^d;return b^-1}function p(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 d(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 n(a,c){var b,f,d,m,l,g,n,k=this;this.load=function(c){if(null!==k.data)c(null,k.data);else{var b=l+34+f+d+256;b+n>e&&(b=e-n);runtime.read(a,n,b,function(b,e){if(b||null===e)c(b,e);else a:{var f=e,d=new core.ByteArray(f),n=d.readUInt32LE(),h;if(67324752!==n)c("File entry signature is wrong."+n.toString()+" "+f.length.toString(),null);else{d.pos+=22;n=d.readUInt16LE();h=d.readUInt16LE();d.pos+=n+h;if(m){f= +f.subarray(d.pos,d.pos+l);if(l!==f.length){c("The amount of compressed bytes read was "+f.length.toString()+" instead of "+l.toString()+" for "+k.filename+" in "+a+".",null);break a}f=w(f,g)}else f=f.subarray(d.pos,d.pos+g);g!==f.length?c("The amount of bytes read was "+f.length.toString()+" instead of "+g.toString()+" for "+k.filename+" in "+a+".",null):(k.data=f,c(null,f))}}})}};this.set=function(a,c,b,e){k.filename=a;k.data=c;k.compressed=b;k.date=e};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,m=c.readUInt16LE(),this.date=p(c.readUInt32LE()),c.readUInt32LE(),l=c.readUInt32LE(),g=c.readUInt32LE(),f=c.readUInt16LE(),d=c.readUInt16LE(),b=c.readUInt16LE(),c.pos+=8,n=c.readUInt32LE(),this.filename=runtime.byteArrayToString(c.data.subarray(c.pos,c.pos+f),"utf8"),this.data=null,c.pos+=f+d+b))}function g(a,c){if(22!==a.length)c("Central directory length should be 22.", +z);else{var b=new core.ByteArray(a),f;f=b.readUInt32LE();101010256!==f?c("Central directory signature is wrong: "+f.toString(),z):(f=b.readUInt16LE(),0!==f?c("Zip files with non-zero disk numbers are not supported.",z):(f=b.readUInt16LE(),0!==f?c("Zip files with non-zero disk numbers are not supported.",z):(f=b.readUInt16LE(),t=b.readUInt16LE(),f!==t?c("Number of entries is inconsistent.",z):(f=b.readUInt32LE(),b=b.readUInt16LE(),b=e-22-f,runtime.read(k,b,e-b,function(a,b){if(a||null===b)c(a,z);else a:{var e= +new core.ByteArray(b),f,d;m=[];for(f=0;fe?h("File '"+k+"' cannot be read.",z):runtime.read(k,e-22,22,function(a,c){a||null===h||null===c?h(a,z): +g(c,h)})})}; +// Input 21 +xmldom.LSSerializerFilter=function(){};xmldom.LSSerializerFilter.prototype.acceptNode=function(k){}; +// Input 22 /* - 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 @@ -287,9 +378,8 @@ c=p.createElementNS(r,"span"),c.className="editInfoTime",c.setAttributeNS("urn:w @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -gui.KeyboardHandler=function(){function g(f,g){g||(g=l.None);return f+":"+g}var l=gui.KeyboardHandler.Modifier,f=null,p={};this.setDefault=function(g){f=g};this.bind=function(f,l,h){f=g(f,l);runtime.assert(!1===p.hasOwnProperty(f),"tried to overwrite the callback handler of key combo: "+f);p[f]=h};this.unbind=function(f,l){var h=g(f,l);delete p[h]};this.reset=function(){f=null;p={}};this.handleEvent=function(r){var n=r.keyCode,h=l.None;r.metaKey&&(h|=l.Meta);r.ctrlKey&&(h|=l.Ctrl);r.altKey&&(h|=l.Alt); -r.shiftKey&&(h|=l.Shift);n=g(n,h);n=p[n];h=!1;n?h=n():null!==f&&(h=f(r));h&&(r.preventDefault?r.preventDefault():r.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 +odf.OdfNodeFilter=function(){this.acceptNode=function(k){return"http://www.w3.org/1999/xhtml"===k.namespaceURI?NodeFilter.FILTER_SKIP:k.namespaceURI&&k.namespaceURI.match(/^urn:webodf:/)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}}; +// Input 23 /* Copyright (C) 2012-2013 KO GmbH @@ -331,9 +421,17 @@ odf.Namespaces={namespaceMap:{db:"urn:oasis:names:tc:opendocument:xmlns:database 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",metans:"urn:oasis:names:tc:opendocument:xmlns:meta: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 g=odf.Namespaces.namespaceMap,l=odf.Namespaces.prefixMap,f;for(f in g)g.hasOwnProperty(f)&&(l[g[f]]=f)})();odf.Namespaces.forEachPrefix=function(g){var l=odf.Namespaces.namespaceMap,f;for(f in l)l.hasOwnProperty(f)&&g(f,l[f])};odf.Namespaces.lookupNamespaceURI=function(g){var l=null;odf.Namespaces.namespaceMap.hasOwnProperty(g)&&(l=odf.Namespaces.namespaceMap[g]);return l};odf.Namespaces.lookupPrefix=function(g){var l=odf.Namespaces.prefixMap;return l.hasOwnProperty(g)?l[g]:null}; +(function(){var k=odf.Namespaces.namespaceMap,h=odf.Namespaces.prefixMap,b;for(b in k)k.hasOwnProperty(b)&&(h[k[b]]=b)})();odf.Namespaces.forEachPrefix=function(k){var h=odf.Namespaces.namespaceMap,b;for(b in h)h.hasOwnProperty(b)&&k(b,h[b])};odf.Namespaces.lookupNamespaceURI=function(k){var h=null;odf.Namespaces.namespaceMap.hasOwnProperty(k)&&(h=odf.Namespaces.namespaceMap[k]);return h};odf.Namespaces.lookupPrefix=function(k){var h=odf.Namespaces.prefixMap;return h.hasOwnProperty(k)?h[k]:null}; odf.Namespaces.lookupNamespaceURI.lookupNamespaceURI=odf.Namespaces.lookupNamespaceURI; -// Input 20 +// Input 24 +xmldom.XPathIterator=function(){};xmldom.XPathIterator.prototype.next=function(){};xmldom.XPathIterator.prototype.reset=function(){}; +function createXPathSingleton(){function k(b,c,a){return-1!==b&&(b=g&&a.push(h(b.substring(c,d)))):"["===b[d]&&(0>=g&&(c=d+1),g+=1),d+=1;return d};r=function(b,c,a){var m,e,l,n;for(m=0;m @@ -371,24 +469,44 @@ odf.Namespaces.lookupNamespaceURI.lookupNamespaceURI=odf.Namespaces.lookupNamesp @source: http://www.webodf.org/ @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===y}function l(a){return null!==a&&a.nodeType===Node.ELEMENT_NODE&&"frame"===a.localName&&a.namespaceURI===y&&"as-char"===a.getAttributeNS(H,"anchor-type")}function f(a){var b;(b="annotation"===(a&&a.localName)&&a.namespaceURI===odf.Namespaces.officens)||(b="div"===(a&&a.localName)&&"annotationWrapper"===a.className);return b}function p(a){var b=a&&a.localName;return("p"===b||"h"===b)&&a.namespaceURI===H}function r(a){for(;a&& -!p(a);)a=a.parentNode;return a}function n(a){return/^[ \t\r\n]+$/.test(a)}function h(a){if(null===a||a.nodeType!==Node.ELEMENT_NODE)return!1;var b=a.localName;return/^(span|p|h|a|meta)$/.test(b)&&a.namespaceURI===H||"span"===b&&"annotationHighlight"===a.className}function d(a){var b=a&&a.localName,c=!1;b&&(a=a.namespaceURI,a===H&&(c="s"===b||"tab"===b||"line-break"===b));return c}function m(a){return d(a)||l(a)||f(a)}function c(a){var b=a&&a.localName,c=!1;b&&(a=a.namespaceURI,a===H&&(c="s"===b)); -return c}function e(a){for(;null!==a.firstChild&&h(a);)a=a.firstChild;return a}function a(a){for(;null!==a.lastChild&&h(a);)a=a.lastChild;return a}function b(b){for(;!p(b)&&null===b.previousSibling;)b=b.parentNode;return p(b)?null:a(b.previousSibling)}function q(a){for(;!p(a)&&null===a.nextSibling;)a=a.parentNode;return p(a)?null:e(a.nextSibling)}function k(a){for(var e=!1;a;)if(a.nodeType===Node.TEXT_NODE)if(0===a.length)a=b(a);else return!n(a.data.substr(a.length-1,1));else m(a)?(e=!1===c(a),a= -null):a=b(a);return e}function t(a){var b=!1,c;for(a=a&&e(a);a;){c=a.nodeType===Node.TEXT_NODE?a.length:0;if(0a.value||"%"===a.unit)?null:a}function u(a){return(a=x(a))&&"%"!==a.unit?null:a}function s(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 H=odf.Namespaces.textns,y=odf.Namespaces.drawns,B=/^\s*$/,L=new core.DomUtils;this.isImage=g;this.isCharacterFrame=l;this.isInlineRoot=f;this.isTextSpan=function(a){return"span"===(a&&a.localName)&&a.namespaceURI===H};this.isParagraph=p;this.getParagraphElement=r;this.isWithinTrackedChanges=function(a,b){for(;a&&a!==b;){if(a.namespaceURI===H&&"tracked-changes"===a.localName)return!0;a=a.parentNode}return!1};this.isListItem=function(a){return"list-item"===(a&&a.localName)&&a.namespaceURI===H}; -this.isLineBreak=function(a){return"line-break"===(a&&a.localName)&&a.namespaceURI===H};this.isODFWhitespace=n;this.isGroupingElement=h;this.isCharacterElement=d;this.isAnchoredAsCharacterElement=m;this.isSpaceElement=c;this.firstChild=e;this.lastChild=a;this.previousNode=b;this.nextNode=q;this.scanLeftForNonSpace=k;this.lookLeftForCharacter=function(a){var c,e=c=0;a.nodeType===Node.TEXT_NODE&&(e=a.length);0=b.value||"%"===b.unit)?null:b;return b||u(a)};this.parseFoLineHeight=function(a){return v(a)||u(a)};this.getImpactedParagraphs=function(a){var b,c,e;b=a.commonAncestorContainer;var d=[],f=[];for(b.nodeType===Node.ELEMENT_NODE&& -(d=L.getElementsByTagNameNS(b,H,"p").concat(L.getElementsByTagNameNS(b,H,"h")));b&&!p(b);)b=b.parentNode;b&&d.push(b);c=d.length;for(b=0;b/g,">").replace(/'/g,"'").replace(/"/g,""")}function b(d,n){var g="",k=p.filter?p.filter.acceptNode(n):NodeFilter.FILTER_ACCEPT,r;if(k===NodeFilter.FILTER_ACCEPT&&n.nodeType===Node.ELEMENT_NODE){d.push();r=d.getQName(n);var l,f=n.attributes,c,a,m,e="",t;l="<"+r;c=f.length;for(a=0;a")}if(k===NodeFilter.FILTER_ACCEPT||k===NodeFilter.FILTER_SKIP){for(k=n.firstChild;k;)g+=b(d,k),k=k.nextSibling;n.nodeValue&&(g+=h(n.nodeValue))}r&&(g+="",d.pop());return g}var p=this;this.filter=null;this.writeToString=function(d,n){if(!d)return"";var g=new k(n);return b(g,d)}}; +// Input 27 /* - 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 @@ -423,28 +541,36 @@ this.getImageElements=function(a){var b=a.startContainer.ownerDocument.createRan @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,f,p){};ops.Server.prototype.joinSession=function(g,l,f,p){};ops.Server.prototype.leaveSession=function(g,l,f,p){};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(c,a,b){return-1!==c&&(c=h&&b.push(l(c.substring(a,d)))):"["===c[d]&&(0>=h&&(a=d+1),h+=1),d+=1;return d};m=function(c,a,b){var f,k,g,m;for(f=0;fb)break;d=d.nextSibling}c.insertBefore(a,d)}}}var n=new odf.StyleInfo, +g=new core.DomUtils,q=odf.Namespaces.stylens,r="meta settings scripts font-face-decls styles automatic-styles master-styles body".split(" "),l=(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.AnnotationElement=function(){};odf.OdfPart=function(c,a,b,e){var d=this;this.size=0;this.type=null;this.name=c;this.container=b;this.url=null;this.mimetype=a;this.onstatereadychange=this.document=null;this.EMPTY=0;this.LOADING=1;this.DONE=2;this.state=this.EMPTY;this.data="";this.load=function(){null!==e&&(this.mimetype=a,e.loadAsDataURL(c,a,function(a,c){a&& +runtime.log(a);d.url=c;if(d.onchange)d.onchange(d);if(d.onstatereadychange)d.onstatereadychange(d)}))}};odf.OdfPart.prototype.load=function(){};odf.OdfPart.prototype.getUrl=function(){return this.data?"data:;base64,"+f.toBase64(this.data):null};odf.OdfContainer=function a(m,e){function h(a){for(var b=a.firstChild,e;b;)e=b.nextSibling,b.nodeType===Node.ELEMENT_NODE?h(b):b.nodeType===Node.PROCESSING_INSTRUCTION_NODE&&a.removeChild(b),b=e}function r(a){var b={},e,d,f=a.ownerDocument.createNodeIterator(a, +NodeFilter.SHOW_ELEMENT,null,!1);for(a=f.nextNode();a;)"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===a.namespaceURI&&("annotation"===a.localName?(e=a.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","name"))&&(b.hasOwnProperty(e)?runtime.log("Warning: annotation name used more than once with : '"+e+"'"):b[e]=a):"annotation-end"===a.localName&&((e=a.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","name"))?b.hasOwnProperty(e)?(d=b[e],d.annotationEndElement? +runtime.log("Warning: annotation name used more than once with : '"+e+"'"):d.annotationEndElement=a):runtime.log("Warning: annotation end without an annotation start, name: '"+e+"'"):runtime.log("Warning: annotation end without a name found"))),a=f.nextNode()}function z(a,b){for(var e=a&&a.firstChild;e;)e.nodeType===Node.ELEMENT_NODE&&e.setAttributeNS("urn:webodf:names:scope","scope",b),e=e.nextSibling}function x(a){var b={},e;for(a=a.firstChild;a;)a.nodeType===Node.ELEMENT_NODE&& +a.namespaceURI===q&&"font-face"===a.localName&&(e=a.getAttributeNS(q,"name"),b[e]=a),a=a.nextSibling;return b}function v(a,b){var e=null,d,f,m;if(a)for(e=a.cloneNode(!0),d=e.firstElementChild;d;)f=d.nextElementSibling,(m=d.getAttributeNS("urn:webodf:names:scope","scope"))&&m!==b&&e.removeChild(d),d=f;return e}function u(a,b){var e,d,f,m=null,g={};if(a)for(b.forEach(function(a){n.collectUsedFontFaces(g,a)}),m=a.cloneNode(!0),e=m.firstElementChild;e;)d=e.nextElementSibling,f=e.getAttributeNS(q,"name"), +g[f]||m.removeChild(e),e=d;return m}function s(a){var b=M.rootElement.ownerDocument,e;if(a){h(a.documentElement);try{e=b.importNode(a.documentElement,!0)}catch(d){}}return e}function A(a){M.state=a;if(M.onchange)M.onchange(M);if(M.onstatereadychange)M.onstatereadychange(M)}function J(a){U=null;M.rootElement=a;a.fontFaceDecls=k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","font-face-decls");a.styles=k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles");a.automaticStyles=k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0", +"automatic-styles");a.masterStyles=k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles");a.body=k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","body");a.meta=k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","meta");r(a)}function G(b){var e=s(b),f=M.rootElement,m;e&&"document-styles"===e.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===e.namespaceURI?(f.fontFaceDecls=k(e,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","font-face-decls"),d(f,f.fontFaceDecls), +m=k(e,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles"),f.styles=m||b.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles"),d(f,f.styles),m=k(e,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles"),f.automaticStyles=m||b.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles"),z(f.automaticStyles,"document-styles"),d(f,f.automaticStyles),e=k(e,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles"),f.masterStyles= +e||b.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles"),d(f,f.masterStyles),n.prefixStyleNames(f.automaticStyles,l,f.masterStyles)):A(a.INVALID)}function D(b){b=s(b);var e,f,m,g;if(b&&"document-content"===b.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===b.namespaceURI){e=M.rootElement;m=k(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","font-face-decls");if(e.fontFaceDecls&&m){g=e.fontFaceDecls;var l,h,p,r,F={};f=x(g);r=x(m);for(m=m.firstElementChild;m;){l= +m.nextElementSibling;if(m.namespaceURI===q&&"font-face"===m.localName)if(h=m.getAttributeNS(q,"name"),f.hasOwnProperty(h)){if(!m.isEqualNode(f[h])){p=h;for(var t=f,H=r,J=0,G=void 0,G=p=p.replace(/\d+$/,"");t.hasOwnProperty(G)||H.hasOwnProperty(G);)J+=1,G=p+J;p=G;m.setAttributeNS(q,"style:name",p);g.appendChild(m);f[p]=m;delete r[h];F[h]=p}}else g.appendChild(m),f[h]=m,delete r[h];m=l}g=F}else m&&(e.fontFaceDecls=m,d(e,m));f=k(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles"); +z(f,"document-content");g&&n.changeFontFaceNames(f,g);if(e.automaticStyles&&f)for(g=f.firstChild;g;)e.automaticStyles.appendChild(g),g=f.firstChild;else f&&(e.automaticStyles=f,d(e,f));b=k(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","body");if(null===b)throw" tag is mising.";e.body=b;d(e,e.body)}else A(a.INVALID)}function F(a){a=s(a);var b;a&&"document-meta"===a.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===a.namespaceURI&&(b=M.rootElement,b.meta=k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0", +"meta"),d(b,b.meta))}function O(a){a=s(a);var b;a&&"document-settings"===a.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===a.namespaceURI&&(b=M.rootElement,b.settings=k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","settings"),d(b,b.settings))}function K(a){a=s(a);var b;if(a&&"manifest"===a.localName&&"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"===a.namespaceURI)for(b=M.rootElement,b.manifest=a,a=b.manifest.firstElementChild;a;)"file-entry"===a.localName&&"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"=== +a.namespaceURI&&(S[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.nextElementSibling}function Z(b){var e=b.shift();e?E.loadAsDOM(e.path,function(d,f){e.handler(f);d||M.state===a.INVALID||Z(b)}):(r(M.rootElement),A(a.DONE))}function Q(a){var b="";odf.Namespaces.forEachPrefix(function(a,e){b+=" xmlns:"+a+'="'+e+'"'});return''}function W(){var a=new xmldom.LSSerializer,b=Q("document-meta");a.filter=new odf.OdfNodeFilter;b+=a.writeToString(M.rootElement.meta,odf.Namespaces.namespaceMap);return b+""}function H(a,b){var e=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest:file-entry");e.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest:full-path",a);e.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0", +"manifest:media-type",b);return e}function T(){var a=runtime.parseXML(''),b=k(a,"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest"),e=new xmldom.LSSerializer,d;for(d in S)S.hasOwnProperty(d)&&b.appendChild(H(d,S[d]));e.filter=new odf.OdfNodeFilter;return'\n'+e.writeToString(a,odf.Namespaces.namespaceMap)} +function y(){var a=new xmldom.LSSerializer,b=Q("document-settings");a.filter=new odf.OdfNodeFilter;M.rootElement.settings.firstElementChild&&(b+=a.writeToString(M.rootElement.settings,odf.Namespaces.namespaceMap));return b+""}function aa(){var a,e,d,f=odf.Namespaces.namespaceMap,m=new xmldom.LSSerializer,g=Q("document-styles");e=v(M.rootElement.automaticStyles,"document-styles");d=M.rootElement.masterStyles.cloneNode(!0);a=u(M.rootElement.fontFaceDecls,[d,M.rootElement.styles, +e]);n.removePrefixFromStyleNames(e,l,d);m.filter=new b(d,e);g+=m.writeToString(a,f);g+=m.writeToString(M.rootElement.styles,f);g+=m.writeToString(e,f);g+=m.writeToString(d,f);return g+""}function N(){var a,b,e=odf.Namespaces.namespaceMap,d=new xmldom.LSSerializer,f=Q("document-content");b=v(M.rootElement.automaticStyles,"document-content");a=u(M.rootElement.fontFaceDecls,[b]);d.filter=new p(M.rootElement.body,b);f+=d.writeToString(a,e);f+=d.writeToString(b,e);f+=d.writeToString(M.rootElement.body, +e);return f+""}function R(b,e){runtime.loadXML(b,function(b,d){if(b)e(b);else{var f=s(d);f&&"document"===f.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===f.namespaceURI?(J(f),A(a.DONE)):A(a.INVALID)}})}function I(a,b){var e;e=M.rootElement;var f=e.meta;f||(e.meta=f=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","meta"),d(e,f));e=f;a&&g.mapKeyValObjOntoNode(e,a,odf.Namespaces.lookupNamespaceURI);b&&g.removeKeyElementsFromNode(e, +b,odf.Namespaces.lookupNamespaceURI)}function ca(){function b(a,e){var d;e||(e=a);d=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0",e);f[a]=d;f.appendChild(d)}var e=new core.Zip("",null),d=runtime.byteArrayFromString("application/vnd.oasis.opendocument.text","utf8"),f=M.rootElement,m=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","text");e.save("mimetype",d,!1,new Date);b("meta");b("settings");b("scripts");b("fontFaceDecls","font-face-decls"); +b("styles");b("automaticStyles","automatic-styles");b("masterStyles","master-styles");b("body");f.body.appendChild(m);S["/"]="application/vnd.oasis.opendocument.text";S["settings.xml"]="text/xml";S["meta.xml"]="text/xml";S["styles.xml"]="text/xml";S["content.xml"]="text/xml";A(a.DONE);return e}function ia(){var a,b=new Date,e=runtime.getWindow();a="WebODF/"+("undefined"!==String(typeof webodf_version)?webodf_version:"FromSource");e&&(a=a+" "+e.navigator.userAgent);I({"meta:generator":a},null);a=runtime.byteArrayFromString(y(), +"utf8");E.save("settings.xml",a,!0,b);a=runtime.byteArrayFromString(W(),"utf8");E.save("meta.xml",a,!0,b);a=runtime.byteArrayFromString(aa(),"utf8");E.save("styles.xml",a,!0,b);a=runtime.byteArrayFromString(N(),"utf8");E.save("content.xml",a,!0,b);a=runtime.byteArrayFromString(T(),"utf8");E.save("META-INF/manifest.xml",a,!0,b)}function Y(a,b){ia();E.writeAs(a,function(a){b(a)})}var M=this,E,S={},U;this.onstatereadychange=e;this.state=this.onchange=null;this.setRootElement=J;this.getContentElement= +function(){var a;U||(a=M.rootElement.body,U=k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","text")||k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","presentation")||k(a,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","spreadsheet"));if(!U)throw"Could not find content element in .";return U};this.getDocumentType=function(){var a=M.getContentElement();return a&&a.localName};this.getPart=function(a){return new odf.OdfPart(a,S[a],M,E)};this.getPartData=function(a,b){E.load(a, +b)};this.setMetadata=I;this.incrementEditingCycles=function(){var a;for(a=(a=M.rootElement.meta)&&a.firstChild;a&&(a.namespaceURI!==odf.Namespaces.metans||"editing-cycles"!==a.localName);)a=a.nextSibling;for(a=a&&a.firstChild;a&&a.nodeType!==Node.TEXT_NODE;)a=a.nextSibling;a=a?a.data:null;a=a?parseInt(a,10):0;isNaN(a)&&(a=0);I({"meta:editing-cycles":a+1},null)};this.createByteArray=function(a,b){ia();E.createByteArray(a,b)};this.saveAs=Y;this.save=function(a){Y(m,a)};this.getUrl=function(){return m}; +this.setBlob=function(a,b,e){e=f.convertBase64ToByteArray(e);E.save(a,e,!1,new Date);S.hasOwnProperty(a)&&runtime.log(a+" has been overwritten.");S[a]=b};this.removeBlob=function(a){var b=E.remove(a);runtime.assert(b,"file is not found: "+a);delete S[a]};this.state=a.LOADING;this.rootElement=function(a){var b=document.createElementNS(a.namespaceURI,a.localName),e;a=new a.Type;for(e in a)a.hasOwnProperty(e)&&(b[e]=a[e]);return b}({Type:odf.ODFDocumentElement,namespaceURI:odf.ODFDocumentElement.namespaceURI, +localName:odf.ODFDocumentElement.localName});E=m?new core.Zip(m,function(b,e){E=e;b?R(m,function(e){b&&(E.error=b+"\n"+e,A(a.INVALID))}):Z([{path:"styles.xml",handler:G},{path:"content.xml",handler:D},{path:"meta.xml",handler:F},{path:"settings.xml",handler:O},{path:"META-INF/manifest.xml",handler:K}])}):ca()};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 28 /* Copyright (C) 2012-2013 KO GmbH @@ -482,14 +608,20 @@ runtime.loadClass("core.PositionFilter");core.PositionFilterChain=function(){var @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -gui.AnnotatableCanvas=function(){};gui.AnnotatableCanvas.prototype.refreshSize=function(){};gui.AnnotatableCanvas.prototype.getZoomLevel=function(){};gui.AnnotatableCanvas.prototype.getSizer=function(){}; -gui.AnnotationViewManager=function(g,l,f){function p(a){var b=a.node,d=a.end;a=m.createRange();d&&(a.setStart(b,b.childNodes.length),a.setEnd(d,0),d=c.getTextNodes(a,!1),d.forEach(function(a){var c=m.createElement("span"),d=b.getAttributeNS(odf.Namespaces.officens,"name");c.className="annotationHighlight";c.setAttribute("annotation",d);a.parentNode.insertBefore(c,a);c.appendChild(a)}));a.detach()}function r(a){var b=g.getSizer();a?(f.style.display="inline-block",b.style.paddingRight=e.getComputedStyle(f).width): -(f.style.display="none",b.style.paddingRight=0);g.refreshSize()}function n(){d.sort(function(a,b){return a.node.compareDocumentPosition(b.node)===Node.DOCUMENT_POSITION_FOLLOWING?-1:1})}function h(){var a;for(a=0;a=(m.getBoundingClientRect().top-n.bottom)/b?c.style.top=Math.abs(m.getBoundingClientRect().top-n.bottom)/b+20+"px":c.style.top="0px");h.style.left=e.getBoundingClientRect().width/b+"px";var e=h.style,m=h.getBoundingClientRect().left/b,l=h.getBoundingClientRect().top/b,n=c.getBoundingClientRect().left/b,r=c.getBoundingClientRect().top/b,p=0,s=0,p=n-m,p=p*p,s=r-l,s=s*s,m=Math.sqrt(p+s);e.width= -m+"px";l=Math.asin((c.getBoundingClientRect().top-h.getBoundingClientRect().top)/(b*parseFloat(h.style.width)));h.style.transform="rotate("+l+"rad)";h.style.MozTransform="rotate("+l+"rad)";h.style.WebkitTransform="rotate("+l+"rad)";h.style.msTransform="rotate("+l+"rad)"}}var d=[],m=l.ownerDocument,c=new odf.OdfUtils,e=runtime.getWindow();runtime.assert(Boolean(e),"Expected to be run in an environment which has a global window, like a browser.");this.rerenderAnnotations=h;this.addAnnotation=function(a){r(!0); -d.push({node:a.node,end:a.end});n();var b=m.createElement("div"),c=m.createElement("div"),e=m.createElement("div"),f=m.createElement("div"),g=m.createElement("div"),l=a.node;b.className="annotationWrapper";l.parentNode.insertBefore(b,l);c.className="annotationNote";c.appendChild(l);g.className="annotationRemoveButton";c.appendChild(g);e.className="annotationConnector horizontal";f.className="annotationConnector angular";b.appendChild(c);b.appendChild(e);b.appendChild(f);a.end&&p(a);h()};this.forgetAnnotations= -function(){for(;d.length;){var a=d[0],b=d.indexOf(a),c=a.node,e=c.parentNode.parentNode;"div"===e.localName&&(e.parentNode.insertBefore(c,e),e.parentNode.removeChild(e));a=a.node.getAttributeNS(odf.Namespaces.officens,"name");a=m.querySelectorAll('span.annotationHighlight[annotation="'+a+'"]');e=c=void 0;for(c=0;ca.value||"%"===a.unit)?null:a}function s(a){return(a=v(a))&&"%"!==a.unit?null:a}function A(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 "cursor":case "editinfo":return!1}}return!0} +function J(a,c){for(;0=c.value||"%"===c.unit)?null:c;return c||s(a)};this.parseFoLineHeight=function(a){return u(a)||s(a)};this.isTextContentContainingNode=A;this.getTextNodes=function(a,c){var b;b=W.getNodesInRange(a,function(a){var c=NodeFilter.FILTER_REJECT;a.nodeType===Node.TEXT_NODE?Boolean(n(a)&&(!g(a.textContent)||x(a,0)))&&(c=NodeFilter.FILTER_ACCEPT): +A(a)&&(c=NodeFilter.FILTER_SKIP);return c},NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT);c||J(a,b);return b};this.getTextElements=G;this.getParagraphElements=function(a){var c;c=W.getNodesInRange(a,function(a){var c=NodeFilter.FILTER_REJECT;if(d(a))c=NodeFilter.FILTER_ACCEPT;else if(A(a)||q(a))c=NodeFilter.FILTER_SKIP;return c},NodeFilter.SHOW_ELEMENT);D(a.startContainer,c,d);return c};this.getImageElements=function(a){var c;c=W.getNodesInRange(a,function(a){var c=NodeFilter.FILTER_SKIP;k(a)&&(c= +NodeFilter.FILTER_ACCEPT);return c},NodeFilter.SHOW_ELEMENT);D(a.startContainer,c,k);return c};this.getHyperlinkElements=function(a){var c=[],b=a.cloneRange();a.collapsed&&a.endContainer.nodeType===Node.ELEMENT_NODE&&(a=F(a.endContainer,a.endOffset),a.nodeType===Node.TEXT_NODE&&b.setEnd(a,1));G(b,!0,!1).forEach(function(a){for(a=a.parentNode;!d(a);){if(p(a)&&-1===c.indexOf(a)){c.push(a);break}a=a.parentNode}});b.detach();return c}}; +// Input 29 /* Copyright (C) 2012-2013 KO GmbH @@ -527,22 +659,49 @@ function(){for(;d.length;){var a=d[0],b=d.indexOf(a),c=a.node,e=c.parentNode.par @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 f(){w.setUnfilteredPosition(g.getNode(),0);return w}function p(a,b){var c,d=null;a&&0=b?d=r(a,b-1,c,!0):a.nodeType===Node.TEXT_NODE&& -0a?-1:1;for(a=Math.abs(a);0q?g.previousPosition():g.nextPosition());)if(R.check(),k.acceptPosition(g)===u&&(n+=1,m=g.container(),w=r(m,g.unfilteredDomOffset(),U),w.top!==F)){if(w.top!==Y&&Y!==F)break;Y=w.top;w=Math.abs(C-w.left);if(null===p||wa?(c=k.previousPosition,d=-1):(c=k.nextPosition,d=1);for(e=r(k.container(),k.unfilteredDomOffset(),m);c.call(k);)if(b.acceptPosition(k)===u){if(t.getParagraphElement(k.getCurrentNode())!==q)break;h=r(k.container(),k.unfilteredDomOffset(),m);if(h.bottom!==e.bottom&&(e=h.top>=e.top&&h.bottome.bottom,!e))break;g+= -d;e=h}m.detach();return g}function k(a,b,c){runtime.assert(null!==a,"SelectionMover.countStepsToPosition called with element===null");var d=f(),e=d.container(),h=d.unfilteredDomOffset(),k=0,q=new core.LoopWatchDog(1E4);for(d.setUnfilteredPosition(a,b);c.acceptPosition(d)!==u&&d.previousPosition();)q.check();a=d.container();runtime.assert(Boolean(a),"SelectionMover.countStepsToPosition: positionIterator.container() returned null");b=d.unfilteredDomOffset();for(d.setUnfilteredPosition(e,h);c.acceptPosition(d)!== -u&&d.previousPosition();)q.check();e=A.comparePoints(a,b,d.container(),d.unfilteredDomOffset());if(0>e)for(;d.nextPosition()&&(q.check(),c.acceptPosition(d)===u&&(k+=1),d.container()!==a||d.unfilteredDomOffset()!==b););else if(0=(g.getBoundingClientRect().top-n.bottom)/c?e.style.top=Math.abs(g.getBoundingClientRect().top-n.bottom)/c+20+"px":e.style.top="0px");f.style.left=d.getBoundingClientRect().width/c+"px";var d=f.style,g=f.getBoundingClientRect().left/c,l=f.getBoundingClientRect().top/c,n=e.getBoundingClientRect().left/c,h=e.getBoundingClientRect().top/c,q=0,A=0,q=n-g,q=q*q,A=h-l,A=A*A,g=Math.sqrt(q+A);d.width=g+"px";l=Math.asin((e.getBoundingClientRect().top- +f.getBoundingClientRect().top)/(c*parseFloat(f.style.width)));f.style.transform="rotate("+l+"rad)";f.style.MozTransform="rotate("+l+"rad)";f.style.WebkitTransform="rotate("+l+"rad)";f.style.msTransform="rotate("+l+"rad)"}}var r=[],l=h.ownerDocument,f=new odf.OdfUtils,c=runtime.getWindow();runtime.assert(Boolean(c),"Expected to be run in an environment which has a global window, like a browser.");this.rerenderAnnotations=q;this.getMinimumHeightForAnnotationPane=function(){return"none"!==b.style.display&& +0 + Copyright (C) 2014 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/ +*/ +(function(){function k(h,b){var p=this;this.getDistance=function(b){var n=p.x-b.x;b=p.y-b.y;return Math.sqrt(n*n+b*b)};this.getCenter=function(b){return new k((p.x+b.x)/2,(p.y+b.y)/2)};p.x=h;p.y=b}gui.ZoomHelper=function(){function h(c,b,e,d){c=d?"translate3d("+c+"px, "+b+"px, 0) scale3d("+e+", "+e+", 1)":"translate("+c+"px, "+b+"px) scale("+e+")";a.style.WebkitTransform=c;a.style.MozTransform=c;a.style.msTransform=c;a.style.OTransform=c;a.style.transform=c}function b(a){a?h(-m.x,-m.y,w,!0):(h(0, +0,w,!0),h(0,0,w,!1))}function p(a){if(v&&J){var c=v.style.overflow,b=v.classList.contains("customScrollbars");a&&b||!a&&!b||(a?(v.classList.add("customScrollbars"),v.style.overflow="hidden",runtime.requestAnimationFrame(function(){v.style.overflow=c})):v.classList.remove("customScrollbars"))}}function d(){h(-m.x,-m.y,w,!0);v.scrollLeft=0;v.scrollTop=0;p(!1)}function n(){h(0,0,w,!0);v.scrollLeft=m.x;v.scrollTop=m.y;p(!0)}function g(c){return new k(c.pageX-a.offsetLeft,c.pageY-a.offsetTop)}function q(c){e&& +(m.x-=c.x-e.x,m.y-=c.y-e.y,m=new k(Math.min(Math.max(m.x,a.offsetLeft),(a.offsetLeft+a.offsetWidth)*w-v.clientWidth),Math.min(Math.max(m.y,a.offsetTop),(a.offsetTop+a.offsetHeight)*w-v.clientHeight)));e=c}function r(a){var c=a.touches.length,b=0 @licstart The JavaScript code in this page is free software: you can redistribute it @@ -577,8 +736,10 @@ g.setSelectedRange(a)})()};gui.SelectionMover.createPositionIterator=function(g) @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 30 +(function(){function k(h,d,n,g,q){var r,l=0,f;for(f in h)if(h.hasOwnProperty(f)){if(l===n){r=f;break}l+=1}r?d.getPartData(h[r].href,function(c,a){if(c)runtime.log(c);else if(a){var f="@font-face { font-family: '"+(h[r].family||r)+"'; src: url(data:application/x-font-ttf;charset=binary;base64,"+b.convertUTF8ArrayToBase64(a)+') format("truetype"); }';try{g.insertRule(f,g.cssRules.length)}catch(e){runtime.log("Problem inserting rule in CSS: "+runtime.toJson(e)+"\nRule: "+f)}}else runtime.log("missing font data for "+ +h[r].href);k(h,d,n+1,g,q)}):q&&q()}var h=xmldom.XPath,b=new core.Base64;odf.FontLoader=function(){this.loadFonts=function(b,d){for(var n=b.rootElement.fontFaceDecls;d.cssRules.length;)d.deleteRule(d.cssRules.length-1);if(n){var g={},q,r,l,f;if(n)for(n=h.getODFElementsWithXPath(n,"style:font-face[svg:font-face-src]",odf.Namespaces.lookupNamespaceURI),q=0;q @@ -616,30 +777,20 @@ odf.OdfNodeFilter=function(){this.acceptNode=function(g){return"http://www.w3.or @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(g){this.derivedStyles={};this.element=g}; -odf.Style2CSS=function(){function g(a){var b,c,d,e={};if(!a)return e;for(a=a.firstElementChild;a;){if(c=a.namespaceURI!==k||"style"!==a.localName&&"default-style"!==a.localName?a.namespaceURI===w&&"list-style"===a.localName?"list":a.namespaceURI!==k||"page-layout"!==a.localName&&"default-page-layout"!==a.localName?void 0:"page":a.getAttributeNS(k,"family"))(b=a.getAttributeNS(k,"name"))||(b=""),e.hasOwnProperty(c)?d=e[c]:e[c]=d={},d[b]=a;a=a.nextElementSibling}return e}function l(a,b){if(a.hasOwnProperty(b))return a[b]; -var c,d=null;for(c in a)if(a.hasOwnProperty(c)&&(d=l(a[c].derivedStyles,b)))break;return d}function f(a,b,c){var d,e,h;if(!b.hasOwnProperty(a))return null;d=new odf.StyleTreeNode(b[a]);e=d.element.getAttributeNS(k,"parent-style-name");h=null;e&&(h=l(c,e)||f(e,b,c));h?h.derivedStyles[a]=d:c[a]=d;delete b[a];return d}function p(a,b){for(var c in a)a.hasOwnProperty(c)&&f(c,a,b)}function r(a,b,c){var d=[];c=c.derivedStyles;var e;var f=u[a],h;void 0===f?b=null:(h=b?"["+f+'|style-name="'+b+'"]':"","presentation"=== -f&&(f="draw",h=b?'[presentation|style-name="'+b+'"]':""),b=f+"|"+s[a].join(h+","+f+"|")+h);null!==b&&d.push(b);for(e in c)c.hasOwnProperty(e)&&(b=r(a,e,c[e]),d=d.concat(b));return d}function n(a,b,c){for(a=a&&a.firstElementChild;a&&(a.namespaceURI!==b||a.localName!==c);)a=a.nextElementSibling;return a}function h(a,b){var c="",d,e,f;for(d=0;dk.value&&(f="0.75pt"+q)}e[2]&&(c+=e[2]+":"+f+";")}return c}function d(a){return(a=n(a,k,"text-properties"))?O.parseFoFontSize(a.getAttributeNS(b,"font-size")):null}function m(a,b,c,d){return b+b+c+c+d+d}function c(a,c,d,e){c='text|list[text|style-name="'+c+'"]';var f=d.getAttributeNS(w,"level");d=n(d,k,"list-level-properties");d=n(d,k,"list-level-label-alignment");var h,q;d&&(h=d.getAttributeNS(b,"text-indent"),q=d.getAttributeNS(b, -"margin-left"));h||(h="-0.6cm");d="-"===h.charAt(0)?h.substring(1):"-"+h;for(f=f&&parseInt(f,10);1 text|list-item > text|list",f-=1;if(q){f=c+" > text|list-item > *:not(text|list):first-child";f+="{";f=f+("margin-left:"+q+";")+"}";try{a.insertRule(f,a.cssRules.length)}catch(g){runtime.log("cannot load rule: "+f)}}e=c+" > text|list-item > *:not(text|list):first-child:before{"+e+";";e=e+"counter-increment:list;"+("margin-left:"+h+";");e+="width:"+d+";";e+="display:inline-block}";try{a.insertRule(e, -a.cssRules.length)}catch(m){runtime.log("cannot load rule: "+e)}}function e(f,g,l,p){if("list"===g)for(var s=p.element.firstChild,t,u;s;){if(s.namespaceURI===w)if(t=s,"list-level-style-number"===s.localName){var G=t;u=G.getAttributeNS(k,"num-format");var T=G.getAttributeNS(k,"num-suffix")||"",G=G.getAttributeNS(k,"num-prefix")||"",$={1:"decimal",a:"lower-latin",A:"upper-latin",i:"lower-roman",I:"upper-roman"},X="";G&&(X+=' "'+G+'"');X=$.hasOwnProperty(u)?X+(" counter(list, "+$[u]+")"):u?X+(' "'+u+ -'"'):X+" ''";u="content:"+X+' "'+T+'"';c(f,l,t,u)}else"list-level-style-image"===s.localName?(u="content: none;",c(f,l,t,u)):"list-level-style-bullet"===s.localName&&(u="content: '"+t.getAttributeNS(w,"bullet-char")+"';",c(f,l,t,u));s=s.nextSibling}else if("page"===g){if(u=p.element,G=T=l="",s=n(u,k,"page-layout-properties"))if(t=u.getAttributeNS(k,"name"),l+=h(s,ja),(T=n(s,k,"background-image"))&&(G=T.getAttributeNS(x,"href"))&&(l=l+("background-image: url('odfkit:"+G+"');")+h(T,y)),"presentation"=== -aa)for(u=(u=n(u.parentNode.parentElement,q,"master-styles"))&&u.firstElementChild;u;){if(u.namespaceURI===k&&"master-page"===u.localName&&u.getAttributeNS(k,"page-layout-name")===t){G=u.getAttributeNS(k,"name");T="draw|page[draw|master-page-name="+G+"] {"+l+"}";G="office|body, draw|page[draw|master-page-name="+G+"] {"+h(s,ka)+" }";try{f.insertRule(T,f.cssRules.length),f.insertRule(G,f.cssRules.length)}catch(da){throw da;}}u=u.nextElementSibling}else if("text"===aa){T="office|text {"+l+"}";G="office|body {width: "+ -s.getAttributeNS(b,"page-width")+";}";try{f.insertRule(T,f.cssRules.length),f.insertRule(G,f.cssRules.length)}catch(S){throw S;}}}else{l=r(g,l,p).join(",");s="";if(t=n(p.element,k,"text-properties")){G=t;u=X="";T=1;t=""+h(G,H);$=G.getAttributeNS(k,"text-underline-style");"solid"===$&&(X+=" underline");$=G.getAttributeNS(k,"text-line-through-style");"solid"===$&&(X+=" line-through");X.length&&(t+="text-decoration:"+X+";");if(X=G.getAttributeNS(k,"font-name")||G.getAttributeNS(b,"font-family"))$=Z[X], -t+="font-family: "+($||X)+";";$=G.parentElement;if(G=d($)){for(;$;){if(G=d($)){if("%"!==G.unit){u="font-size: "+G.value*T+G.unit+";";break}T*=G.value/100}G=$;X=$="";$=null;"default-style"===G.localName?$=null:($=G.getAttributeNS(k,"parent-style-name"),X=G.getAttributeNS(k,"family"),$=C.getODFElementsWithXPath(J,$?"//style:*[@style:name='"+$+"'][@style:family='"+X+"']":"//style:default-style[@style:family='"+X+"']",odf.Namespaces.lookupNamespaceURI)[0])}u||(u="font-size: "+parseFloat(F)*T+Y.getUnits(F)+ -";");t+=u}s+=t}if(t=n(p.element,k,"paragraph-properties"))u=t,t=""+h(u,B),(T=n(u,k,"background-image"))&&(G=T.getAttributeNS(x,"href"))&&(t=t+("background-image: url('odfkit:"+G+"');")+h(T,y)),(u=u.getAttributeNS(b,"line-height"))&&"normal"!==u&&(u=O.parseFoLineHeight(u),t="%"!==u.unit?t+("line-height: "+u.value+u.unit+";"):t+("line-height: "+u.value/100+";")),s+=t;if(t=n(p.element,k,"graphic-properties"))G=t,t=""+h(G,L),u=G.getAttributeNS(a,"opacity"),T=G.getAttributeNS(a,"fill"),G=G.getAttributeNS(a, -"fill-color"),"solid"===T||"hatch"===T?G&&"none"!==G?(u=isNaN(parseFloat(u))?1:parseFloat(u)/100,T=G.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,m),(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)&&(t+="background-color: rgba("+G.r+","+G.g+","+G.b+","+u+");")):t+="background: none;":"none"===T&&(t+="background: none;"),s+=t;if(t=n(p.element,k,"drawing-page-properties"))u=""+h(t,L),"true"===t.getAttributeNS(v,"background-visible")&& -(u+="background: none;"),s+=u;if(t=n(p.element,k,"table-cell-properties"))t=""+h(t,I),s+=t;if(t=n(p.element,k,"table-row-properties"))t=""+h(t,Q),s+=t;if(t=n(p.element,k,"table-column-properties"))t=""+h(t,W),s+=t;if(t=n(p.element,k,"table-properties"))u=t,t=""+h(u,z),u=u.getAttributeNS(A,"border-model"),"collapsing"===u?t+="border-collapse:collapse;":"separating"===u&&(t+="border-collapse:separate;"),s+=t;if(0!==s.length)try{f.insertRule(l+"{"+s+"}",f.cssRules.length)}catch(ga){throw ga;}}for(var E in p.derivedStyles)p.derivedStyles.hasOwnProperty(E)&& -e(f,g,E,p.derivedStyles[E])}var a=odf.Namespaces.drawns,b=odf.Namespaces.fons,q=odf.Namespaces.officens,k=odf.Namespaces.stylens,t=odf.Namespaces.svgns,A=odf.Namespaces.tablens,w=odf.Namespaces.textns,x=odf.Namespaces.xlinkns,v=odf.Namespaces.presentationns,u={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"]},H=[[b,"color","color"],[b,"background-color","background-color"],[b,"font-weight","font-weight"],[b,"font-style","font-style"]],y=[[k,"repeat","background-repeat"]],B=[[b,"background-color","background-color"],[b,"text-align","text-align"],[b,"text-indent","text-indent"],[b,"padding","padding"],[b,"padding-left","padding-left"],[b,"padding-right","padding-right"],[b,"padding-top","padding-top"],[b,"padding-bottom","padding-bottom"],[b,"border-left","border-left"],[b,"border-right", -"border-right"],[b,"border-top","border-top"],[b,"border-bottom","border-bottom"],[b,"margin","margin"],[b,"margin-left","margin-left"],[b,"margin-right","margin-right"],[b,"margin-top","margin-top"],[b,"margin-bottom","margin-bottom"],[b,"border","border"]],L=[[b,"background-color","background-color"],[b,"min-height","min-height"],[a,"stroke","border"],[t,"stroke-color","border-color"],[t,"stroke-width","border-width"],[b,"border","border"],[b,"border-left","border-left"],[b,"border-right","border-right"], -[b,"border-top","border-top"],[b,"border-bottom","border-bottom"]],I=[[b,"background-color","background-color"],[b,"border-left","border-left"],[b,"border-right","border-right"],[b,"border-top","border-top"],[b,"border-bottom","border-bottom"],[b,"border","border"]],W=[[k,"column-width","width"]],Q=[[k,"row-height","height"],[b,"keep-together",null]],z=[[k,"width","width"],[b,"margin-left","margin-left"],[b,"margin-right","margin-right"],[b,"margin-top","margin-top"],[b,"margin-bottom","margin-bottom"]], -ja=[[b,"background-color","background-color"],[b,"padding","padding"],[b,"padding-left","padding-left"],[b,"padding-right","padding-right"],[b,"padding-top","padding-top"],[b,"padding-bottom","padding-bottom"],[b,"border","border"],[b,"border-left","border-left"],[b,"border-right","border-right"],[b,"border-top","border-top"],[b,"border-bottom","border-bottom"],[b,"margin","margin"],[b,"margin-left","margin-left"],[b,"margin-right","margin-right"],[b,"margin-top","margin-top"],[b,"margin-bottom", -"margin-bottom"]],ka=[[b,"page-width","width"],[b,"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,aa,J,F,C=xmldom.XPath,Y=new core.CSSUnits;this.style2css=function(a,b,c,d,f){for(var h,k,q,m;b.cssRules.length;)b.deleteRule(b.cssRules.length-1);h=null;d&&(h=d.ownerDocument,J=d.parentNode);f&&(h=f.ownerDocument,J=f.parentNode);if(h)for(m in odf.Namespaces.forEachPrefix(function(a,c){k="@namespace "+ -a+" url("+c+");";try{b.insertRule(k,b.cssRules.length)}catch(d){}}),Z=c,aa=a,F=runtime.getWindow().getComputedStyle(document.body,null).getPropertyValue("font-size")||"12pt",a=g(d),d=g(f),f={},u)if(u.hasOwnProperty(m))for(q in c=f[m]={},p(a[m],c),p(d[m],c),c)c.hasOwnProperty(q)&&e(b,m,q,c[q])}}; -// Input 31 +odf.Formatting=function(){function k(a){return(a=s[a])?u.mergeObjects({},a):{}}function h(a,c,b){for(a=a&&a.firstElementChild;a&&(a.namespaceURI!==c||a.localName!==b);)a=a.nextElementSibling;return a}function b(){for(var a=c.rootElement.fontFaceDecls,b={},d,f,a=a&&a.firstElementChild;a;){if(d=a.getAttributeNS(e,"name"))if((f=a.getAttributeNS(m,"font-family"))||0 @@ -677,39 +828,33 @@ a+" url("+c+");";try{b.insertRule(k,b.cssRules.length)}catch(d){}}),Z=c,aa=a,F=r @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){var c,d,e,f,h,k=0;if(c=B[a.localName])if(e=c[a.namespaceURI])k=e.length;for(c=0;cm.value&&(f="0.75pt"+l)}d[2]&&(b+=d[2]+":"+f+";")}return b}function q(c){return(c=n(c,e,"text-properties"))?y.parseFoFontSize(c.getAttributeNS(a,"font-size")):null}function r(a,c,b,e){return c+c+b+b+e+e}function l(c,b,d,f){b='text|list[text|style-name="'+b+'"]';var g=d.getAttributeNS(z,"level");d=n(d,e,"list-level-properties");d=n(d,e,"list-level-label-alignment");var m,l;d&&(m=d.getAttributeNS(a,"text-indent"),l=d.getAttributeNS(a, +"margin-left"));m||(m="-0.6cm");d="-"===m.charAt(0)?m.substring(1):"-"+m;for(g=g&&parseInt(g,10);1 text|list-item > text|list",g-=1;if(l){g=b+" > text|list-item > *:not(text|list):first-child";g+="{";g=g+("margin-left:"+l+";")+"}";try{c.insertRule(g,c.cssRules.length)}catch(h){runtime.log("cannot load rule: "+g)}}f=b+" > text|list-item > *:not(text|list):first-child:before{"+f+";";f=f+"counter-increment:list;"+("margin-left:"+m+";");f+="width:"+d+";";f+="display:inline-block}";try{c.insertRule(f, +c.cssRules.length)}catch(k){runtime.log("cannot load rule: "+f)}}function f(b,h,k,p){if("list"===h)for(var t=p.element.firstChild,s,u;t;){if(t.namespaceURI===z)if(s=t,"list-level-style-number"===t.localName){var H=s;u=H.getAttributeNS(e,"num-format");var P=H.getAttributeNS(e,"num-suffix")||"",H=H.getAttributeNS(e,"num-prefix")||"",X={1:"decimal",a:"lower-latin",A:"upper-latin",i:"lower-roman",I:"upper-roman"},V="";H&&(V+=' "'+H+'"');V=X.hasOwnProperty(u)?V+(" counter(list, "+X[u]+")"):u?V+(' "'+u+ +'"'):V+" ''";u="content:"+V+' "'+P+'"';l(b,k,s,u)}else"list-level-style-image"===t.localName?(u="content: none;",l(b,k,s,u)):"list-level-style-bullet"===t.localName&&(u="content: '"+s.getAttributeNS(z,"bullet-char")+"';",l(b,k,s,u));t=t.nextSibling}else if("page"===h){if(u=p.element,H=P=k="",t=n(u,e,"page-layout-properties"))if(s=u.getAttributeNS(e,"name"),k+=g(t,Q),(P=n(t,e,"background-image"))&&(H=P.getAttributeNS(x,"href"))&&(k=k+("background-image: url('odfkit:"+H+"');")+g(P,J)),"presentation"=== +aa)for(u=(u=n(u.parentNode.parentNode,m,"master-styles"))&&u.firstElementChild;u;){if(u.namespaceURI===e&&"master-page"===u.localName&&u.getAttributeNS(e,"page-layout-name")===s){H=u.getAttributeNS(e,"name");P="draw|page[draw|master-page-name="+H+"] {"+k+"}";H="office|body, draw|page[draw|master-page-name="+H+"] {"+g(t,W)+" }";try{b.insertRule(P,b.cssRules.length),b.insertRule(H,b.cssRules.length)}catch(ga){throw ga;}}u=u.nextElementSibling}else if("text"===aa){P="office|text {"+k+"}";H="office|body {width: "+ +t.getAttributeNS(a,"page-width")+";}";try{b.insertRule(P,b.cssRules.length),b.insertRule(H,b.cssRules.length)}catch(ha){throw ha;}}}else{k=d(h,k,p).join(",");t="";if(s=n(p.element,e,"text-properties")){H=s;u=V="";P=1;s=""+g(H,A);X=H.getAttributeNS(e,"text-underline-style");"solid"===X&&(V+=" underline");X=H.getAttributeNS(e,"text-line-through-style");"solid"===X&&(V+=" line-through");V.length&&(s+="text-decoration:"+V+";");if(V=H.getAttributeNS(e,"font-name")||H.getAttributeNS(a,"font-family"))X= +T[V],s+="font-family: "+(X||V)+";";X=H.parentNode;if(H=q(X)){for(;X;){if(H=q(X)){if("%"!==H.unit){u="font-size: "+H.value*P+H.unit+";";break}P*=H.value/100}H=X;V=X="";X=null;"default-style"===H.localName?X=null:(X=H.getAttributeNS(e,"parent-style-name"),V=H.getAttributeNS(e,"family"),X=I.getODFElementsWithXPath(N,X?"//style:*[@style:name='"+X+"'][@style:family='"+V+"']":"//style:default-style[@style:family='"+V+"']",odf.Namespaces.lookupNamespaceURI)[0])}u||(u="font-size: "+parseFloat(R)*P+ca.getUnits(R)+ +";");s+=u}t+=s}if(s=n(p.element,e,"paragraph-properties"))u=s,s=""+g(u,G),(P=n(u,e,"background-image"))&&(H=P.getAttributeNS(x,"href"))&&(s=s+("background-image: url('odfkit:"+H+"');")+g(P,J)),(u=u.getAttributeNS(a,"line-height"))&&"normal"!==u&&(u=y.parseFoLineHeight(u),s="%"!==u.unit?s+("line-height: "+u.value+u.unit+";"):s+("line-height: "+u.value/100+";")),t+=s;if(s=n(p.element,e,"graphic-properties"))H=s,s=""+g(H,D),u=H.getAttributeNS(c,"opacity"),P=H.getAttributeNS(c,"fill"),H=H.getAttributeNS(c, +"fill-color"),"solid"===P||"hatch"===P?H&&"none"!==H?(u=isNaN(parseFloat(u))?1:parseFloat(u)/100,P=H.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,r),(H=(P=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(P))?{r:parseInt(P[1],16),g:parseInt(P[2],16),b:parseInt(P[3],16)}:null)&&(s+="background-color: rgba("+H.r+","+H.g+","+H.b+","+u+");")):s+="background: none;":"none"===P&&(s+="background: none;"),t+=s;if(s=n(p.element,e,"drawing-page-properties"))u=""+g(s,D),"true"===s.getAttributeNS(v,"background-visible")&& +(u+="background: none;"),t+=u;if(s=n(p.element,e,"table-cell-properties"))s=""+g(s,F),t+=s;if(s=n(p.element,e,"table-row-properties"))s=""+g(s,K),t+=s;if(s=n(p.element,e,"table-column-properties"))s=""+g(s,O),t+=s;if(s=n(p.element,e,"table-properties"))u=s,s=""+g(u,Z),u=u.getAttributeNS(w,"border-model"),"collapsing"===u?s+="border-collapse:collapse;":"separating"===u&&(s+="border-collapse:separate;"),t+=s;if(0!==t.length)try{b.insertRule(k+"{"+t+"}",b.cssRules.length)}catch($){throw $;}}for(var ea in p.derivedStyles)p.derivedStyles.hasOwnProperty(ea)&& +f(b,h,ea,p.derivedStyles[ea])}var c=odf.Namespaces.drawns,a=odf.Namespaces.fons,m=odf.Namespaces.officens,e=odf.Namespaces.stylens,t=odf.Namespaces.svgns,w=odf.Namespaces.tablens,z=odf.Namespaces.textns,x=odf.Namespaces.xlinkns,v=odf.Namespaces.presentationns,u={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"]},A=[[a,"color","color"],[a,"background-color","background-color"],[a,"font-weight","font-weight"],[a,"font-style","font-style"]],J=[[e,"repeat","background-repeat"]],G=[[a,"background-color","background-color"],[a,"text-align","text-align"],[a,"text-indent","text-indent"],[a,"padding","padding"],[a,"padding-left","padding-left"],[a,"padding-right","padding-right"],[a,"padding-top","padding-top"],[a,"padding-bottom","padding-bottom"],[a,"border-left","border-left"],[a,"border-right", +"border-right"],[a,"border-top","border-top"],[a,"border-bottom","border-bottom"],[a,"margin","margin"],[a,"margin-left","margin-left"],[a,"margin-right","margin-right"],[a,"margin-top","margin-top"],[a,"margin-bottom","margin-bottom"],[a,"border","border"]],D=[[a,"background-color","background-color"],[a,"min-height","min-height"],[c,"stroke","border"],[t,"stroke-color","border-color"],[t,"stroke-width","border-width"],[a,"border","border"],[a,"border-left","border-left"],[a,"border-right","border-right"], +[a,"border-top","border-top"],[a,"border-bottom","border-bottom"]],F=[[a,"background-color","background-color"],[a,"border-left","border-left"],[a,"border-right","border-right"],[a,"border-top","border-top"],[a,"border-bottom","border-bottom"],[a,"border","border"]],O=[[e,"column-width","width"]],K=[[e,"row-height","height"],[a,"keep-together",null]],Z=[[e,"width","width"],[a,"margin-left","margin-left"],[a,"margin-right","margin-right"],[a,"margin-top","margin-top"],[a,"margin-bottom","margin-bottom"]], +Q=[[a,"background-color","background-color"],[a,"padding","padding"],[a,"padding-left","padding-left"],[a,"padding-right","padding-right"],[a,"padding-top","padding-top"],[a,"padding-bottom","padding-bottom"],[a,"border","border"],[a,"border-left","border-left"],[a,"border-right","border-right"],[a,"border-top","border-top"],[a,"border-bottom","border-bottom"],[a,"margin","margin"],[a,"margin-left","margin-left"],[a,"margin-right","margin-right"],[a,"margin-top","margin-top"],[a,"margin-bottom","margin-bottom"]], +W=[[a,"page-width","width"],[a,"page-height","height"]],H={border:!0,"border-left":!0,"border-right":!0,"border-top":!0,"border-bottom":!0,"stroke-width":!0},T={},y=new odf.OdfUtils,aa,N,R,I=xmldom.XPath,ca=new core.CSSUnits;this.style2css=function(a,c,b,e,d){for(var g,m,l,h;c.cssRules.length;)c.deleteRule(c.cssRules.length-1);g=null;e&&(g=e.ownerDocument,N=e.parentNode);d&&(g=d.ownerDocument,N=d.parentNode);if(g)for(h in odf.Namespaces.forEachPrefix(function(a,b){m="@namespace "+a+" url("+b+");"; +try{c.insertRule(m,c.cssRules.length)}catch(e){}}),T=b,aa=a,R=runtime.getWindow().getComputedStyle(document.body,null).getPropertyValue("font-size")||"12pt",a=k(e),e=k(d),d={},u)if(u.hasOwnProperty(h))for(l in b=d[h]={},p(a[h],b),p(e[h],b),b)b.hasOwnProperty(l)&&f(c,h,l,b[l])}}; +// Input 34 /* - 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 @@ -744,10 +889,8 @@ Node.ELEMENT_NODE){var e=b,f=d,h=e.getAttributeNS(k,"name"),q=void 0;h?q=k:(h=e. @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.OdfUtils"); -odf.TextSerializer=function(){function g(p){var r="",n=l.filter?l.filter.acceptNode(p):NodeFilter.FILTER_ACCEPT,h=p.nodeType,d;if(n===NodeFilter.FILTER_ACCEPT||n===NodeFilter.FILTER_SKIP)for(d=p.firstChild;d;)r+=g(d),d=d.nextSibling;n===NodeFilter.FILTER_ACCEPT&&(h===Node.ELEMENT_NODE&&f.isParagraph(p)?r+="\n":h===Node.TEXT_NODE&&p.textContent&&(r+=p.textContent));return r}var l=this,f=new odf.OdfUtils;this.filter=null;this.writeToString=function(f){if(!f)return"";f=g(f);"\n"===f[f.length-1]&&(f= -f.substr(0,f.length-1));return f}}; -// Input 33 +ops.Canvas=function(){};ops.Canvas.prototype.getZoomLevel=function(){};ops.Canvas.prototype.getElement=function(){}; +// Input 35 /* Copyright (C) 2012-2013 KO GmbH @@ -785,21 +928,67 @@ f.substr(0,f.length-1));return f}}; @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(d,g,c){var e,a;if(g){if(f.isInlineRoot(g)&&f.isGroupingElement(c))return h;e=f.lookLeftForCharacter(g);if(1===e||2===e&&(f.scanRightForAnyCharacter(c)||f.scanRightForAnyCharacter(f.nextNode(d))))return n}e=null===g&&f.isParagraph(d);a=f.lookRightForCharacter(c);if(e)return a?n:f.scanRightForAnyCharacter(c)?h:n;if(!a)return h;g=g||f.previousNode(d);return f.scanLeftForAnyCharacter(g)?h:n}var f=new odf.OdfUtils,p=Node.ELEMENT_NODE,r=Node.TEXT_NODE,n=core.PositionFilter.FilterResult.FILTER_ACCEPT, -h=core.PositionFilter.FilterResult.FILTER_REJECT;this.acceptPosition=function(d){var m=d.container(),c=m.nodeType,e,a,b;if(c!==p&&c!==r)return h;if(c===r){if(!f.isGroupingElement(m.parentNode)||f.isWithinTrackedChanges(m.parentNode,g()))return h;c=d.unfilteredDomOffset();e=m.data;runtime.assert(c!==e.length,"Unexpected offset.");if(0/g,">").replace(/'/g,"'").replace(/"/g,""")}function f(g,n){var h="",d=p.filter?p.filter.acceptNode(n):NodeFilter.FILTER_ACCEPT,m;if(d===NodeFilter.FILTER_ACCEPT&&n.nodeType===Node.ELEMENT_NODE){g.push();m=g.getQName(n);var c,e=n.attributes,a,b,q,k="",t;c="<"+m;a=e.length;for(b=0;b")}if(d===NodeFilter.FILTER_ACCEPT||d===NodeFilter.FILTER_SKIP){for(d=n.firstChild;d;)h+=f(g,d),d=d.nextSibling;n.nodeValue&&(h+=l(n.nodeValue))}m&&(h+="",g.pop());return h}var p=this;this.filter=null;this.writeToString=function(l,n){if(!l)return"";var h=new g(n);return f(h,l)}}; -// Input 35 +(function(){function k(){function a(e){b=!0;runtime.setTimeout(function(){try{e()}catch(d){runtime.log(String(d))}b=!1;0 text|list-item > *:first-child:before {"; +if(E=I.getAttributeNS(J,"style-name")){I=G[E];Q=Z.getFirstNonWhitespaceChild(I);I=void 0;if(Q)if("list-level-style-number"===Q.localName){I=Q.getAttributeNS(u,"num-format");E=Q.getAttributeNS(u,"num-suffix")||"";var R="",R={1:"decimal",a:"lower-latin",A:"upper-latin",i:"lower-roman",I:"upper-roman"},L=void 0,L=Q.getAttributeNS(u,"num-prefix")||"",L=R.hasOwnProperty(I)?L+(" counter(list, "+R[I]+")"):I?L+("'"+I+"';"):L+" ''";E&&(L+=" '"+E+"'");I=R="content: "+L+";"}else"list-level-style-image"===Q.localName? +I="content: none;":"list-level-style-bullet"===Q.localName&&(I="content: '"+Q.getAttributeNS(J,"bullet-char")+"';");Q=I}if(C){for(I=w[C];I;)I=w[I];N+="counter-increment:"+C+";";Q?(Q=Q.replace("list",C),N+=Q):N+="content:counter("+C+");"}else C="",Q?(Q=Q.replace("list",B),N+=Q):N+="content: counter("+B+");",N+="counter-increment:"+B+";",h.insertRule("text|list#"+B+" {counter-reset:"+B+"}",h.cssRules.length);N+="}";w[B]=C;N&&h.insertRule(N,h.cssRules.length)}S.insertBefore($,S.firstChild);ba.setZoomableElement(S); +aa(l);if(!e&&(l=[Y],ea.hasOwnProperty("statereadychange")))for(h=ea.statereadychange,Q=0;Q + @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.MemberProperties=function(){}; +ops.Member=function(k,h){var b=new ops.MemberProperties;this.getMemberId=function(){return k};this.getProperties=function(){return b};this.setProperties=function(h){Object.keys(h).forEach(function(d){b[d]=h[d]})};this.removeProperties=function(h){Object.keys(h).forEach(function(d){"fullName"!==d&&"color"!==d&&"imageUrl"!==d&&b.hasOwnProperty(d)&&delete b[d]})};runtime.assert(Boolean(k),"No memberId was supplied!");h.fullName||(h.fullName=runtime.tr("Unknown Author"));h.color||(h.color="black");h.imageUrl|| +(h.imageUrl="avatar-joe.png");b=h}; +// Input 37 +/* + + 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 @@ -833,10 +1022,14 @@ m+">",g.pop());return h}var p=this;this.filter=null;this.writeToString=function( @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,f;this.setDataFromRange=function(f,r){var n=!0,h,d=f.clipboardData;h=runtime.getWindow();var m=r.startContainer.ownerDocument;!d&&h&&(d=h.clipboardData);d?(m=m.createElement("span"),m.appendChild(r.cloneContents()),h=d.setData("text/plain",l.writeToString(m)),n=n&&h,h=d.setData("text/html",g.writeToString(m,odf.Namespaces.namespaceMap)),n=n&&h,f.preventDefault()):n=!1;return n};g=new xmldom.LSSerializer;l=new odf.TextSerializer;f=new odf.OdfNodeFilter;g.filter=f;l.filter= -f}; -// Input 36 +gui.SelectionMover=function(k,h){function b(){f.setUnfilteredPosition(k.getNode(),0);return f}function p(a,c){var b,d=null;a&&0=c?f=d(a,c-1,b,!0):a.nodeType===Node.TEXT_NODE&& +0a?-1:1;for(a=Math.abs(a);0q?p.previousPosition():p.nextPosition());)if(H.check(),n.acceptPosition(p)===c&&(A+=1,r=p.container(),F=d(r,p.unfilteredDomOffset(),W),F.top!==K)){if(F.top!==Q&&Q!==K)break;Q=F.top;F=Math.abs(Z-F.left);if(null===J||Fa?(e=q.previousPosition, +g=-1):(e=q.nextPosition,g=1);for(k=d(q.container(),q.unfilteredDomOffset(),s);e.call(q);)if(f.acceptPosition(q)===c){if(l.getParagraphElement(q.getCurrentNode())!==p)break;n=d(q.container(),q.unfilteredDomOffset(),s);if(n.bottom!==k.bottom&&(k=n.top>=k.top&&n.bottomk.bottom,!k))break;r+=g;k=n}s.detach();return r}var l=new odf.OdfUtils,f,c=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.getStepCounter=function(){return{convertForwardStepsBetweenFilters:n,convertBackwardStepsBetweenFilters:g, +countLinesSteps:q,countStepsToLineBoundary:r}};(function(){f=gui.SelectionMover.createPositionIterator(h);var a=h.ownerDocument.createRange();a.setStart(f.container(),f.unfilteredDomOffset());a.collapse(!0);k.setSelectedRange(a)})()}; +gui.SelectionMover.createPositionIterator=function(k){var h=new function(){this.acceptNode=function(b){return b&&"urn:webodf:names:cursor"!==b.namespaceURI&&"urn:webodf:names:editinfo"!==b.namespaceURI?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT}};return new core.PositionIterator(k,5,h,!1)};(function(){return gui.SelectionMover})(); +// Input 38 /* Copyright (C) 2012-2013 KO GmbH @@ -874,34 +1067,13 @@ f}; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.Base64");runtime.loadClass("core.Zip");runtime.loadClass("core.DomUtils");runtime.loadClass("xmldom.LSSerializer");runtime.loadClass("odf.StyleInfo");runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.OdfNodeFilter"); -(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=m.length;for(b=0;bc)break;e=e.nextSibling}a.insertBefore(b,e)}}}var n=new odf.StyleInfo, -h=new core.DomUtils,d=odf.Namespaces.stylens,m="meta settings scripts font-face-decls styles automatic-styles master-styles body".split(" "),c=(new Date).getTime()+"_webodf_",e=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,b,c,d){var e=this;this.size=0;this.type=null;this.name=a;this.container=c;this.url=null;this.mimetype=b;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=b,d.loadAsDataURL(a,b,function(a,b){a&&runtime.log(a);e.url=b;if(e.onchange)e.onchange(e); -if(e.onstatereadychange)e.onstatereadychange(e)}))}};odf.OdfPart.prototype.load=function(){};odf.OdfPart.prototype.getUrl=function(){return this.data?"data:;base64,"+e.toBase64(this.data):null};odf.OdfContainer=function b(q,k){function m(b){for(var c=b.firstChild,d;c;)d=c.nextSibling,c.nodeType===Node.ELEMENT_NODE?m(c):c.nodeType===Node.PROCESSING_INSTRUCTION_NODE&&b.removeChild(c),c=d}function l(b,c){for(var d=b&&b.firstChild;d;)d.nodeType===Node.ELEMENT_NODE&&d.setAttributeNS("urn:webodf:names:scope", -"scope",c),d=d.nextSibling}function w(b){var c={},e;for(b=b.firstChild;b;)b.nodeType===Node.ELEMENT_NODE&&b.namespaceURI===d&&"font-face"===b.localName&&(e=b.getAttributeNS(d,"name"),c[e]=b),b=b.nextSibling;return c}function x(b,c){var d=null,e,f,h;if(b)for(d=b.cloneNode(!0),e=d.firstElementChild;e;)f=e.nextElementSibling,(h=e.getAttributeNS("urn:webodf:names:scope","scope"))&&h!==c&&d.removeChild(e),e=f;return d}function v(b,c){var e,f,h,k=null,g={};if(b)for(c.forEach(function(b){n.collectUsedFontFaces(g, -b)}),k=b.cloneNode(!0),e=k.firstElementChild;e;)f=e.nextElementSibling,h=e.getAttributeNS(d,"name"),g[h]||k.removeChild(e),e=f;return k}function u(b){var c=R.rootElement.ownerDocument,d;if(b){m(b.documentElement);try{d=c.importNode(b.documentElement,!0)}catch(e){}}return d}function s(b){R.state=b;if(R.onchange)R.onchange(R);if(R.onstatereadychange)R.onstatereadychange(R)}function H(b){ba=null;R.rootElement=b;b.fontFaceDecls=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","font-face-decls"); -b.styles=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles");b.automaticStyles=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles");b.masterStyles=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles");b.body=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","body");b.meta=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","meta")}function y(d){var e=u(d),f=R.rootElement,h;e&&"document-styles"===e.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"=== -e.namespaceURI?(f.fontFaceDecls=g(e,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","font-face-decls"),r(f,f.fontFaceDecls),h=g(e,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles"),f.styles=h||d.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","styles"),r(f,f.styles),h=g(e,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles"),f.automaticStyles=h||d.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles"),l(f.automaticStyles, -"document-styles"),r(f,f.automaticStyles),e=g(e,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles"),f.masterStyles=e||d.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","master-styles"),r(f,f.masterStyles),n.prefixStyleNames(f.automaticStyles,c,f.masterStyles)):s(b.INVALID)}function B(c){c=u(c);var e,f,h,k;if(c&&"document-content"===c.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===c.namespaceURI){e=R.rootElement;h=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0", -"font-face-decls");if(e.fontFaceDecls&&h){k=e.fontFaceDecls;var q,m,p,t,v={};f=w(k);t=w(h);for(h=h.firstElementChild;h;){q=h.nextElementSibling;if(h.namespaceURI===d&&"font-face"===h.localName)if(m=h.getAttributeNS(d,"name"),f.hasOwnProperty(m)){if(!h.isEqualNode(f[m])){p=m;for(var y=f,I=t,z=0,B=void 0,B=p=p.replace(/\d+$/,"");y.hasOwnProperty(B)||I.hasOwnProperty(B);)z+=1,B=p+z;p=B;h.setAttributeNS(d,"style:name",p);k.appendChild(h);f[p]=h;delete t[m];v[m]=p}}else k.appendChild(h),f[m]=h,delete t[m]; -h=q}k=v}else h&&(e.fontFaceDecls=h,r(e,h));f=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","automatic-styles");l(f,"document-content");k&&n.changeFontFaceNames(f,k);if(e.automaticStyles&&f)for(k=f.firstChild;k;)e.automaticStyles.appendChild(k),k=f.firstChild;else f&&(e.automaticStyles=f,r(e,f));c=g(c,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","body");if(null===c)throw" tag is mising.";e.body=c;r(e,e.body)}else s(b.INVALID)}function L(b){b=u(b);var c;b&&"document-meta"=== -b.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===b.namespaceURI&&(c=R.rootElement,c.meta=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","meta"),r(c,c.meta))}function I(b){b=u(b);var c;b&&"document-settings"===b.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===b.namespaceURI&&(c=R.rootElement,c.settings=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","settings"),r(c,c.settings))}function W(b){b=u(b);var c;if(b&&"manifest"===b.localName&&"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"=== -b.namespaceURI)for(c=R.rootElement,c.manifest=b,b=c.manifest.firstElementChild;b;)"file-entry"===b.localName&&"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"===b.namespaceURI&&(M[b.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","full-path")]=b.getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","media-type")),b=b.nextElementSibling}function Q(c){var d=c.shift();d?P.loadAsDOM(d.path,function(e,f){d.handler(f);e||R.state===b.INVALID||Q(c)}):s(b.DONE)}function z(b){var c= -"";odf.Namespaces.forEachPrefix(function(b,d){c+=" xmlns:"+b+'="'+d+'"'});return''}function ja(){var b=new xmldom.LSSerializer,c=z("document-meta");b.filter=new odf.OdfNodeFilter;c+=b.writeToString(R.rootElement.meta,odf.Namespaces.namespaceMap);return c+""}function ka(b,c){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",b);d.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest:media-type",c);return d}function G(){var b=runtime.parseXML(''),c=g(b,"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0","manifest"),d=new xmldom.LSSerializer,e;for(e in M)M.hasOwnProperty(e)&&c.appendChild(ka(e,M[e]));d.filter=new odf.OdfNodeFilter;return'\n'+ -d.writeToString(b,odf.Namespaces.namespaceMap)}function Z(){var b=new xmldom.LSSerializer,c=z("document-settings");b.filter=new odf.OdfNodeFilter;c+=b.writeToString(R.rootElement.settings,odf.Namespaces.namespaceMap);return c+""}function O(){var b,d,e,h=odf.Namespaces.namespaceMap,k=new xmldom.LSSerializer,g=z("document-styles");d=x(R.rootElement.automaticStyles,"document-styles");e=R.rootElement.masterStyles.cloneNode(!0);b=v(R.rootElement.fontFaceDecls,[e,R.rootElement.styles, -d]);n.removePrefixFromStyleNames(d,c,e);k.filter=new f(e,d);g+=k.writeToString(b,h);g+=k.writeToString(R.rootElement.styles,h);g+=k.writeToString(d,h);g+=k.writeToString(e,h);return g+""}function aa(){var b,c,d=odf.Namespaces.namespaceMap,e=new xmldom.LSSerializer,f=z("document-content");c=x(R.rootElement.automaticStyles,"document-content");b=v(R.rootElement.fontFaceDecls,[c]);e.filter=new p(R.rootElement.body,c);f+=e.writeToString(b,d);f+=e.writeToString(c,d);f+=e.writeToString(R.rootElement.body, -d);return f+""}function J(c,d){runtime.loadXML(c,function(c,e){if(c)d(c);else{var f=u(e);f&&"document"===f.localName&&"urn:oasis:names:tc:opendocument:xmlns:office:1.0"===f.namespaceURI?(H(f),s(b.DONE)):s(b.INVALID)}})}function F(b,c){var d;d=R.rootElement;var e=d.meta;e||(d.meta=e=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","meta"),r(d,e));d=e;b&&h.mapKeyValObjOntoNode(d,b,odf.Namespaces.lookupNamespaceURI);c&&h.removeKeyElementsFromNode(d, -c,odf.Namespaces.lookupNamespaceURI)}function C(){function c(b,d){var e;d||(d=b);e=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0",d);f[b]=e;f.appendChild(e)}var d=new core.Zip("",null),e=runtime.byteArrayFromString("application/vnd.oasis.opendocument.text","utf8"),f=R.rootElement,h=document.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0","text");d.save("mimetype",e,!1,new Date);c("meta");c("settings");c("scripts");c("fontFaceDecls","font-face-decls"); -c("styles");c("automaticStyles","automatic-styles");c("masterStyles","master-styles");c("body");f.body.appendChild(h);s(b.DONE);return d}function Y(){var b,c=new Date,d=runtime.getWindow();b="WebODF/"+("undefined"!==String(typeof webodf_version)?webodf_version:"FromSource");d&&(b=b+" "+d.navigator.userAgent);F({"meta:generator":b},null);b=runtime.byteArrayFromString(Z(),"utf8");P.save("settings.xml",b,!0,c);b=runtime.byteArrayFromString(ja(),"utf8");P.save("meta.xml",b,!0,c);b=runtime.byteArrayFromString(O(), -"utf8");P.save("styles.xml",b,!0,c);b=runtime.byteArrayFromString(aa(),"utf8");P.save("content.xml",b,!0,c);b=runtime.byteArrayFromString(G(),"utf8");P.save("META-INF/manifest.xml",b,!0,c)}function U(b,c){Y();P.writeAs(b,function(b){c(b)})}var R=this,P,M={},ba;this.onstatereadychange=k;this.state=this.onchange=null;this.setRootElement=H;this.getContentElement=function(){var b;ba||(b=R.rootElement.body,ba=g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","text")||g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0", -"presentation")||g(b,"urn:oasis:names:tc:opendocument:xmlns:office:1.0","spreadsheet"));if(!ba)throw"Could not find content element in .";return ba};this.getDocumentType=function(){var b=R.getContentElement();return b&&b.localName};this.getPart=function(b){return new odf.OdfPart(b,M[b],R,P)};this.getPartData=function(b,c){P.load(b,c)};this.setMetadata=F;this.incrementEditingCycles=function(){var b;for(b=(b=R.rootElement.meta)&&b.firstChild;b&&(b.namespaceURI!==odf.Namespaces.metans|| -"editing-cycles"!==b.localName);)b=b.nextSibling;for(b=b&&b.firstChild;b&&b.nodeType!==Node.TEXT_NODE;)b=b.nextSibling;b=b?b.data:null;b=b?parseInt(b,10):0;isNaN(b)&&(b=0);F({"meta:editing-cycles":b+1},null)};this.createByteArray=function(b,c){Y();P.createByteArray(b,c)};this.saveAs=U;this.save=function(b){U(q,b)};this.getUrl=function(){return q};this.setBlob=function(b,c,d){d=e.convertBase64ToByteArray(d);P.save(b,d,!1,new Date);M.hasOwnProperty(b)&&runtime.log(b+" has been overwritten.");M[b]=c}; -this.removeBlob=function(b){var c=P.remove(b);runtime.assert(c,"file is not found: "+b);delete M[b]};this.state=b.LOADING;this.rootElement=function(b){var c=document.createElementNS(b.namespaceURI,b.localName),d;b=new b.Type;for(d in b)b.hasOwnProperty(d)&&(c[d]=b[d]);return c}({Type:odf.ODFDocumentElement,namespaceURI:odf.ODFDocumentElement.namespaceURI,localName:odf.ODFDocumentElement.localName});P=q?new core.Zip(q,function(c,d){P=d;c?J(q,function(d){c&&(P.error=c+"\n"+d,s(b.INVALID))}):Q([{path:"styles.xml", -handler:y},{path:"content.xml",handler:B},{path:"meta.xml",handler:L},{path:"settings.xml",handler:I},{path:"META-INF/manifest.xml",handler:W}])}):C()};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(b){return new odf.OdfContainer(b,null)};return odf.OdfContainer})(); -// Input 37 +ops.Document=function(){};ops.Document.prototype.getMemberIds=function(){};ops.Document.prototype.removeCursor=function(k){};ops.Document.prototype.getDocumentElement=function(){};ops.Document.prototype.getRootNode=function(){};ops.Document.prototype.getDOMDocument=function(){};ops.Document.prototype.cloneDocumentElement=function(){};ops.Document.prototype.setDocumentElement=function(k){};ops.Document.prototype.subscribe=function(k,h){};ops.Document.prototype.unsubscribe=function(k,h){}; +ops.Document.prototype.getCanvas=function(){};ops.Document.prototype.createRootFilter=function(k){};ops.Document.signalCursorAdded="cursor/added";ops.Document.signalCursorRemoved="cursor/removed";ops.Document.signalCursorMoved="cursor/moved";ops.Document.signalMemberAdded="member/added";ops.Document.signalMemberUpdated="member/updated";ops.Document.signalMemberRemoved="member/removed"; +// Input 39 +ops.OdtCursor=function(k,h){var b=this,p={},d,n,g,q=new core.EventNotifier([ops.OdtCursor.signalCursorUpdated]);this.removeFromDocument=function(){g.remove()};this.subscribe=function(b,d){q.subscribe(b,d)};this.unsubscribe=function(b,d){q.unsubscribe(b,d)};this.getStepCounter=function(){return n.getStepCounter()};this.getMemberId=function(){return k};this.getNode=function(){return g.getNode()};this.getAnchorNode=function(){return g.getAnchorNode()};this.getSelectedRange=function(){return g.getSelectedRange()}; +this.setSelectedRange=function(d,l){g.setSelectedRange(d,l);q.emit(ops.OdtCursor.signalCursorUpdated,b)};this.hasForwardSelection=function(){return g.hasForwardSelection()};this.getDocument=function(){return h};this.getSelectionType=function(){return d};this.setSelectionType=function(b){p.hasOwnProperty(b)?d=b:runtime.log("Invalid selection type: "+b)};this.resetSelectionType=function(){b.setSelectionType(ops.OdtCursor.RangeSelection)};g=new core.Cursor(h.getDOMDocument(),k);n=new gui.SelectionMover(g, +h.getRootNode());p[ops.OdtCursor.RangeSelection]=!0;p[ops.OdtCursor.RegionSelection]=!0;b.resetSelectionType()};ops.OdtCursor.RangeSelection="Range";ops.OdtCursor.RegionSelection="Region";ops.OdtCursor.signalCursorUpdated="cursorUpdated";(function(){return ops.OdtCursor})(); +// Input 40 /* Copyright (C) 2012-2013 KO GmbH @@ -939,11 +1111,41 @@ handler:y},{path:"content.xml",handler:B},{path:"meta.xml",handler:L},{path:"set @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.Base64");runtime.loadClass("xmldom.XPath");runtime.loadClass("odf.OdfContainer"); -(function(){function g(l,r,n,h,d){var m,c=0,e;for(e in l)if(l.hasOwnProperty(e)){if(c===n){m=e;break}c+=1}m?r.getPartData(l[m].href,function(a,b){if(a)runtime.log(a);else if(b){var c="@font-face { font-family: '"+(l[m].family||m)+"'; src: url(data:application/x-font-ttf;charset=binary;base64,"+f.convertUTF8ArrayToBase64(b)+') format("truetype"); }';try{h.insertRule(c,h.cssRules.length)}catch(e){runtime.log("Problem inserting rule in CSS: "+runtime.toJson(e)+"\nRule: "+c)}}else runtime.log("missing font data for "+ -l[m].href);g(l,r,n+1,h,d)}):d&&d()}var l=xmldom.XPath,f=new core.Base64;odf.FontLoader=function(){this.loadFonts=function(f,r){for(var n=f.rootElement.fontFaceDecls;r.cssRules.length;)r.deleteRule(r.cssRules.length-1);if(n){var h={},d,m,c,e;if(n)for(n=l.getODFElementsWithXPath(n,"style:font-face[svg:font-face-src]",odf.Namespaces.lookupNamespaceURI),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/ +*/ +(function(){var k=0;ops.StepsCache=function(h,b,p){function d(a,c,e){this.nodeId=a;this.steps=c;this.node=e;this.previousBookmark=this.nextBookmark=null;this.setIteratorPosition=function(a){a.setPositionBeforeElement(e);do if(b.acceptPosition(a)===u)break;while(a.nextPosition())}}function n(a,c,e){this.nodeId=a;this.steps=c;this.node=e;this.previousBookmark=this.nextBookmark=null;this.setIteratorPosition=function(a){a.setUnfilteredPosition(e,0);do if(b.acceptPosition(a)===u)break;while(a.nextPosition())}} +function g(a,c){var b="["+a.nodeId;c&&(b+=" => "+c.nodeId);return b+"]"}function q(){for(var a=x,c,b,e,d=new core.LoopWatchDog(0,1E5);a;){d.check();(c=a.previousBookmark)?runtime.assert(c.nextBookmark===a,"Broken bookmark link to previous @"+g(c,a)):(runtime.assert(a===x,"Broken bookmark link @"+g(a)),runtime.assert(void 0===v||x.steps<=v,"Base point is damaged @"+g(a)));(b=a.nextBookmark)&&runtime.assert(b.previousBookmark===a,"Broken bookmark link to next @"+g(a,b));if(void 0===v||a.steps<=v)runtime.assert(z.containsNode(h, +a.node),"Disconnected node is being reported as undamaged @"+g(a)),c&&(e=a.node.compareDocumentPosition(c.node),runtime.assert(0===e||0!==(e&Node.DOCUMENT_POSITION_PRECEDING),"Bookmark order with previous does not reflect DOM order @"+g(c,a))),b&&z.containsNode(h,b.node)&&(e=a.node.compareDocumentPosition(b.node),runtime.assert(0===e||0!==(e&Node.DOCUMENT_POSITION_FOLLOWING),"Bookmark order with next does not reflect DOM order @"+g(a,b)));a=a.nextBookmark}}function r(a){var c="";a.nodeType===Node.ELEMENT_NODE&& +(c=a.getAttributeNS(m,"nodeId"));return c}function l(a){var c=k.toString();a.setAttributeNS(m,"nodeId",c);k+=1;return c}function f(a){var c,b,d=new core.LoopWatchDog(0,1E4);void 0!==v&&a>v&&(a=v);for(c=Math.floor(a/p)*p;!b&&0!==c;)b=e[c],c-=p;for(b=b||x;b.nextBookmark&&b.nextBookmark.steps<=a;)d.check(),b=b.nextBookmark;return b}function c(a){a.previousBookmark&&(a.previousBookmark.nextBookmark=a.nextBookmark);a.nextBookmark&&(a.nextBookmark.previousBookmark=a.previousBookmark)}function a(a){for(var c, +b=null;!b&&a&&a!==h;)(c=r(a))&&(b=t[c])&&b.node!==a&&(runtime.log("Cloned node detected. Creating new bookmark"),b=null,a.removeAttributeNS(m,"nodeId")),a=a.parentNode;return b}var m="urn:webodf:names:steps",e={},t={},w=new odf.OdfUtils,z=new core.DomUtils,x,v,u=core.PositionFilter.FilterResult.FILTER_ACCEPT,s;this.updateCache=function(a,b,g){var m;m=b.getCurrentNode();if(b.isBeforeNode()&&w.isParagraph(m)){g||(a+=1);b=a;var k,n,q;if(void 0!==v&&vb.steps)e[a]=m;s()}}; +this.setToClosestStep=function(a,c){var b;s();b=f(a);b.setIteratorPosition(c);return b.steps};this.setToClosestDomPoint=function(c,b,d){var g,m;s();if(c===h&&0===b)g=x;else if(c===h&&b===h.childNodes.length)for(m in g=x,e)e.hasOwnProperty(m)&&(c=e[m],c.steps>g.steps&&(g=c));else if(g=a(c.childNodes.item(b)||c),!g)for(d.setUnfilteredPosition(c,b);!g&&d.previousNode();)g=a(d.getCurrentNode());g=g||x;void 0!==v&&g.steps>v&&(g=f(v));g.setIteratorPosition(d);return g.steps};this.damageCacheAfterStep=function(a){0> +a&&(a=0);void 0===v?v=a:a @@ -981,11 +1183,11 @@ l[m].href);g(l,r,n+1,h,d)}):d&&d()}var l=xmldom.XPath,f=new core.Base64;odf.Font @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 f(a,b){var c={};this.generateName=function(){var d=b(),e=0,f;do f=a+e,e+=1;while(c[f]||d[f]);c[f]=!0;return f}}function p(){var a={};[g.rootElement.automaticStyles,g.rootElement.styles].forEach(function(b){for(b=b.firstElementChild;b;)b.namespaceURI===r&&"style"===b.localName&&(a[b.getAttributeNS(r,"name")]=!0),b=b.nextElementSibling});return a}var r=odf.Namespaces.stylens,n=odf.Namespaces.drawns,h=odf.Namespaces.xlinkns,d=new core.DomUtils,m=(new core.Utils).hashString(l), -c=null,e=null,a=null,b={},q={};this.generateStyleName=function(){null===c&&(c=new f("auto"+m+"_",function(){return p()}));return c.generateName()};this.generateFrameName=function(){null===e&&(d.getElementsByTagNameNS(g.rootElement.body,n,"frame").forEach(function(a){b[a.getAttributeNS(n,"name")]=!0}),e=new f("fr"+m+"_",function(){return b}));return e.generateName()};this.generateImageName=function(){null===a&&(d.getElementsByTagNameNS(g.rootElement.body,n,"image").forEach(function(a){a=a.getAttributeNS(h, -"href");a=a.substring(9,a.lastIndexOf("."));q[a]=!0}),a=new f("img"+m+"_",function(){return q}));return a.generateName()}}; -// Input 39 +(function(){ops.StepsTranslator=function(k,h,b,p){function d(){var c=k();c!==g&&(runtime.log("Undo detected. Resetting steps cache"),g=c,q=new ops.StepsCache(g,b,p),l=h(g))}function n(c,a){if(!a||b.acceptPosition(c)===f)return!0;for(;c.previousPosition();)if(b.acceptPosition(c)===f){if(a(0,c.container(),c.unfilteredDomOffset()))return!0;break}for(;c.nextPosition();)if(b.acceptPosition(c)===f){if(a(1,c.container(),c.unfilteredDomOffset()))return!0;break}return!1}var g=k(),q=new ops.StepsCache(g,b, +p),r=new core.DomUtils,l=h(k()),f=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.convertStepsToDomPoint=function(c){var a,g;if(isNaN(c))throw new TypeError("Requested steps is not numeric ("+c+")");if(0>c)throw new RangeError("Requested steps is negative ("+c+")");d();for(a=q.setToClosestStep(c,l);ar.comparePoints(g,0,c,a),c=g,a=a?0:g.childNodes.length);l.setUnfilteredPosition(c,a);n(l,m)||l.setUnfilteredPosition(c,a);m=l.container();a=l.unfilteredDomOffset();c=q.setToClosestDomPoint(m,a,l);if(0>r.comparePoints(l.container(),l.unfilteredDomOffset(),m,a))return 0 @@ -1023,21 +1225,10 @@ c=null,e=null,a=null,b={},q={};this.generateStyleName=function(){null===c&&(c=ne @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"); -odf.Formatting=function(){function g(a){return(a=u[a])?v.mergeObjects({},a):{}}function l(a,b,c){for(a=a&&a.firstElementChild;a&&(a.namespaceURI!==b||a.localName!==c);)a=a.nextElementSibling;return a}function f(){for(var a=e.rootElement.fontFaceDecls,c={},d,f,a=a&&a.firstElementChild;a;){if(d=a.getAttributeNS(q,"name"))if((f=a.getAttributeNS(b,"font-family"))||0 @@ -1075,36 +1266,26 @@ w)break a}d=null}d||(d=l(e.rootElement.styles,q,"default-page-layout"));if(d=l(d @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 g(){function a(d){c=!0;runtime.setTimeout(function(){try{d()}catch(e){runtime.log(String(e))}c=!1;0 text|list-item > *:first-child:before {";if(P=J.getAttributeNS(H,"style-name")){J=D[P];K=Q.getFirstNonWhitespaceChild(J);J=void 0;if(K)if("list-level-style-number"=== -K.localName){J=K.getAttributeNS(v,"num-format");P=K.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=K.getAttributeNS(v,"num-prefix")||"",S=V.hasOwnProperty(J)?S+(" counter(list, "+V[J]+")"):J?S+("'"+J+"';"):S+" ''";P&&(S+=" '"+P+"'");J=V="content: "+S+";"}else"list-level-style-image"===K.localName?J="content: none;":"list-level-style-bullet"===K.localName&&(J="content: '"+K.getAttributeNS(H,"bullet-char")+"';"); -K=J}if(E){for(J=z[E];J;)J=z[J];F+="counter-increment:"+E+";";K?(K=K.replace("list",E),F+=K):F+="content:counter("+E+");"}else E="",K?(K=K.replace("list",O),F+=K):F+="content: counter("+O+");",F+="counter-increment:"+O+";",m.insertRule("text|list#"+O+" {counter-reset:"+O+"}",m.cssRules.length);F+="}";z[O]=E;F&&m.insertRule(F,m.cssRules.length)}M.insertBefore(da,M.firstChild);y();aa(q);if(!k&&(q=[U],ga.hasOwnProperty("statereadychange")))for(m=ga.statereadychange,K=0;K=d;d+=1){b=c.container();e=c.unfilteredDomOffset();if(b.nodeType===Node.TEXT_NODE&&" "===b.data[e]&&a.isSignificantWhitespace(b,e)){runtime.assert(" "===b.data[e],"upgradeWhitespaceToElement: textNode.data[offset] should be a literal space");var f=b.ownerDocument.createElementNS(odf.Namespaces.textns, +"text:s"),m=b.parentNode,l=b;f.appendChild(b.ownerDocument.createTextNode(" "));1===b.length?m.replaceChild(f,b):(b.deleteData(e,1),0 @@ -1142,12 +1323,11 @@ b.removeChild(T);b.removeChild($);b.removeChild(X);P.destroy(a)};ma=k(Y);P=new l @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,f){function p(c){function d(a,b){return"object"===typeof a&&"object"===typeof b?Object.keys(a).every(function(c){return d(a[c],b[c])}):a===b}this.isStyleApplied=function(a){a=l.getAppliedStylesForElement(a);return d(c,a)}}function r(c){var e={};this.applyStyleToContainer=function(a){var b;b=a.getAttributeNS(d,"style-name");var h=a.ownerDocument;b=b||"";if(!e.hasOwnProperty(b)){var k=b,n;n=b?l.createDerivedStyleObject(b,"text",c):c;h=h.createElementNS(m,"style:style"); -l.updateStyle(h,n);h.setAttributeNS(m,"style:name",g.generateStyleName());h.setAttributeNS(m,"style:family","text");h.setAttributeNS("urn:webodf:names:scope","scope","document-content");f.appendChild(h);e[k]=h}b=e[b].getAttributeNS(m,"name");a.setAttributeNS(d,"text:style-name",b)}}function n(c,e){var a=c.ownerDocument,b=c.parentNode,f,k,g=new core.LoopWatchDog(1E4);k=[];"span"!==b.localName||b.namespaceURI!==d?(f=a.createElementNS(d,"text:span"),b.insertBefore(f,c),b=!1):(c.previousSibling&&!h.rangeContainsNode(e, -b.firstChild)?(f=b.cloneNode(!1),b.parentNode.insertBefore(f,b.nextSibling)):f=b,b=!0);k.push(c);for(a=c.nextSibling;a&&h.rangeContainsNode(e,a);)g.check(),k.push(a),a=a.nextSibling;k.forEach(function(a){a.parentNode!==f&&f.appendChild(a)});if(a&&b)for(k=f.cloneNode(!1),f.parentNode.insertBefore(k,f.nextSibling);a;)g.check(),b=a.nextSibling,k.appendChild(a),a=b;return f}var h=new core.DomUtils,d=odf.Namespaces.textns,m=odf.Namespaces.stylens;this.applyStyle=function(c,d,a){var b={},f,h,g,m;runtime.assert(a&& -a.hasOwnProperty("style:text-properties"),"applyStyle without any text properties");b["style:text-properties"]=a["style:text-properties"];g=new r(b);m=new p(b);c.forEach(function(a){f=m.isStyleApplied(a);!1===f&&(h=n(a,d),g.applyStyleToContainer(h))})}}; -// Input 42 +ops.OpAddAnnotation=function(){function k(b,d,g){var f=b.getTextNodeAtStep(g,h);f&&(b=f.textNode,g=b.parentNode,f.offset!==b.length&&b.splitText(f.offset),g.insertBefore(d,b.nextSibling),0===b.length&&g.removeChild(b))}var h,b,p,d,n,g;this.init=function(g){h=g.memberid;b=parseInt(g.timestamp,10);p=parseInt(g.position,10);d=parseInt(g.length,10)||0;n=g.name};this.isEdit=!0;this.group=void 0;this.execute=function(q){var r=q.getCursor(h),l,f;f=new core.DomUtils;g=q.getDOMDocument();var c=new Date(b), +a,m,e,t;a=g.createElementNS(odf.Namespaces.officens,"office:annotation");a.setAttributeNS(odf.Namespaces.officens,"office:name",n);l=g.createElementNS(odf.Namespaces.dcns,"dc:creator");l.setAttributeNS("urn:webodf:names:editinfo","editinfo:memberid",h);l.textContent=q.getMember(h).getProperties().fullName;m=g.createElementNS(odf.Namespaces.dcns,"dc:date");m.appendChild(g.createTextNode(c.toISOString()));c=g.createElementNS(odf.Namespaces.textns,"text:list");e=g.createElementNS(odf.Namespaces.textns, +"text:list-item");t=g.createElementNS(odf.Namespaces.textns,"text:p");e.appendChild(t);c.appendChild(e);a.appendChild(l);a.appendChild(m);a.appendChild(c);d&&(l=g.createElementNS(odf.Namespaces.officens,"office:annotation-end"),l.setAttributeNS(odf.Namespaces.officens,"office:name",n),a.annotationEndElement=l,k(q,l,p+d));k(q,a,p);q.emit(ops.OdtDocument.signalStepsInserted,{position:p,length:d});r&&(l=g.createRange(),f=f.getElementsByTagNameNS(a,odf.Namespaces.textns,"p")[0],l.selectNodeContents(f), +r.setSelectedRange(l,!1),q.emit(ops.Document.signalCursorMoved,r));q.getOdfCanvas().addAnnotation(a);q.fixCursorPositions();return!0};this.spec=function(){return{optype:"AddAnnotation",memberid:h,timestamp:b,position:p,length:d,name:n}}}; +// Input 46 /* Copyright (C) 2012-2013 KO GmbH @@ -1185,36 +1365,8 @@ a.hasOwnProperty("style:text-properties"),"applyStyle without any text propertie @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.OdfUtils"); -gui.StyleHelper=function(g){function l(f,h,d){var g=!0,c;for(c=0;c>>8):(Aa(b&255),Aa(b>>>8))},Ba=function(){u=(u<<5^q[I+3-1]&255)&8191;s=A[32768+u];A[I&32767]=s;A[32768+u]=I},V=function(a,b){x>16-b?(w|=a<>16-x,x+=b-16):(w|=a<a;a++)q[a]=q[a+32768];W-=32768;I-=32768;v-=32768;for(a=0;8192>a;a++)b=A[32768+a],A[32768+a]=32768<=b?b-32768:0;for(a=0;32768>a;a++)b=A[a],A[a]=32768<=b?b-32768:0;c+=32768}Q||(a=na(q,I+z,c),0>=a?Q=!0:z+=a)},oa=function(a){var b=ja,c=I,d,e=L,f=32506=Z&&(b>>=2);do if(d=a,q[d+e]===g&&q[d+e-1]===k&&q[d]===q[c]&&q[++d]===q[c+1]){c+= -2;d++;do++c;while(q[c]===q[++d]&&q[++c]===q[++d]&&q[++c]===q[++d]&&q[++c]===q[++d]&&q[++c]===q[++d]&&q[++c]===q[++d]&&q[++c]===q[++d]&&q[++c]===q[++d]&&ce){W=a;e=d;if(258<=d)break;k=q[c+e-1];g=q[c+e]}a=A[a&32767]}while(a>f&&0!==--b);return e},Ca=function(a,b){t[S++]=b;0===a?O[b].fc++:(a--,O[ma[b]+256+1].fc++,aa[(256>a?T[a]:T[256+(a>>7)])&255].fc++,k[ga++]=a,fa|=D);D<<=1;0===(S&7)&&(da[E++]=fa,fa=0,D=1);if(2e;e++)c+=aa[e].fc* -(5+qa[e]);c>>=3;if(ga>=1,c<<=1;while(0<--b);return c>>1},za=function(a,b){var c=[];c.length=16;var d=0,e;for(e=1;15>=e;e++)d=d+P[e-1]<<1,c[e]=d;for(d=0;d<=b;d++)e=a[d].dl,0!==e&&(a[d].fc=Ea(c[e]++,e))},va=function(a){var b=a.dyn_tree,c=a.static_tree,d=a.elems,e,f= --1,h=d;ba=0;la=573;for(e=0;eba;)e=M[++ba]=2>f?++f:0,b[e].fc=1,ca[e]=0,pa--,null!==c&&(ha-=c[e].dl);a.max_code=f;for(e=ba>>1;1<=e;e--)ya(b,e);do e=M[1],M[1]=M[ba--],ya(b,1),c=M[1],M[--la]=e,M[--la]=c,b[h].fc=b[e].fc+b[c].fc,ca[h]=ca[e]>ca[c]+1?ca[e]:ca[c]+1,b[e].dl=b[c].dl=h,M[1]=h++,ya(b,1);while(2<=ba);M[--la]=M[1];h=a.dyn_tree;e=a.extra_bits;var d=a.extra_base,c=a.max_code,k=a.max_length,g=a.static_tree,q,m,l,p,n=0;for(m=0;15>=m;m++)P[m]= -0;h[M[la]].dl=0;for(a=la+1;573>a;a++)q=M[a],m=h[h[q].dl].dl+1,m>k&&(m=k,n++),h[q].dl=m,q>c||(P[m]++,l=0,q>=d&&(l=e[q-d]),p=h[q].fc,pa+=p*(m+l),null!==g&&(ha+=p*(g[q].dl+l)));if(0!==n){do{for(m=k-1;0===P[m];)m--;P[m]--;P[m+1]+=2;P[k]--;n-=2}while(0c||(h[e].dl!==m&&(pa+=(m-h[e].dl)*h[e].fc,h[e].fc=m),q--)}za(b,f)},Fa=function(a,b){var c,d=-1,e,f=a[0].dl,h=0,k=7,g=4;0===f&&(k=138,g=3);a[b+1].dl=65535;for(c=0;c<=b;c++)e=f,f=a[c+1].dl,++h=h?C[17].fc++:C[18].fc++,h=0,d=e,0===f?(k=138,g=3):e===f?(k=6,g=3):(k=7,g=4))},wa=function(){8c?T[c]:T[256+(c>>7)])&255,K(g,b),q=qa[g],0!==q&&(c-=X[g],V(c,q))),h>>=1;while(d=h?(K(17,C),V(h-3,3)):(K(18,C),V(h-11,7));h=0;d=e;0===f?(k=138,g=3):e===f?(k=6,g=3):(k=7,g=4)}},Ja=function(){var a;for(a=0;286>a;a++)O[a].fc=0;for(a=0;30>a;a++)aa[a].fc=0;for(a=0;19>a;a++)C[a].fc=0;O[256].fc=1;fa=S=ga=E=pa=ha=0;D=1},Ga=function(a){var b,c,d,e;e=I-v;da[E]=fa;va(Y);va(U);Fa(O,Y.max_code);Fa(aa,U.max_code);va(R);for(d=18;3<=d&&0===C[xa[d]].dl;d--); -pa+=3*(d+1)+14;b=pa+3+7>>3;c=ha+3+7>>3;c<=b&&(b=c);if(e+4<=b&&0<=v)for(V(0+a,3),wa(),ta(e),ta(~e),d=0;dh.len&&(g=h.len);for(q=0;qe-a&&(g=e-a);for(q=0;ql;l++)for($[l]=g,k=0;k< -1<l;l++)for(X[l]=g,k=0;k<1<>=7;30>l;l++)for(X[l]=g<<7,k=0;k<1<=k;k++)P[k]=0;for(k=0;143>=k;)J[k++].dl=8,P[8]++;for(;255>=k;)J[k++].dl=9,P[9]++;for(;279>=k;)J[k++].dl=7,P[7]++;for(;287>=k;)J[k++].dl=8,P[8]++;za(J,287);for(k=0;30>k;k++)F[k].dl=5,F[k].fc=Ea(k,5);Ja()}for(k=0;8192>k;k++)A[32768+k]=0;ka=ra[G].max_lazy;Z=ra[G].good_length;ja=ra[G].max_chain;v=I=0;z=na(q,0,65536);if(0>=z)Q=!0,z=0;else{for(Q= -!1;262>z&&!Q;)ea();for(k=u=0;2>k;k++)u=(u<<5^q[k]&255)&8191}h=null;a=e=0;3>=G?(L=2,B=0):(B=2,y=0);b=!1}m=!0;if(0===z)return b=!0,0}k=Ka(c,d,f);if(k===f)return f;if(b)return k;if(3>=G)for(;0!==z&&null===h;){Ba();0!==s&&32506>=I-s&&(B=oa(s),B>z&&(B=z));if(3<=B)if(l=Ca(I-W,B-3),z-=B,B<=ka){B--;do I++,Ba();while(0!==--B);I++}else I+=B,B=0,u=q[I]&255,u=(u<<5^q[I+1]&255)&8191;else l=Ca(0,q[I]&255),z--,I++;l&&(Ga(0),v=I);for(;262>z&&!Q;)ea()}else for(;0!==z&&null===h;){Ba();L=B;H=W;B=2;0!==s&&L= -I-s&&(B=oa(s),B>z&&(B=z),3===B&&4096z&&!Q;)ea()}0===z&&(0!==y&&Ca(0,q[I-1]&255),Ga(1),b=!0);return k+Ka(c,k+d,f-k)};this.deflate=function(a,b){var e,f;ia=a;Da=0;"undefined"===String(typeof b)&&(b=6);(e=b)?1>e?e=1:9e;e++)O[e]=new g;aa=[];aa.length=61;for(e=0;61>e;e++)aa[e]=new g;J=[];J.length=288;for(e=0;288>e;e++)J[e]=new g;F=[];F.length=30;for(e=0;30>e;e++)F[e]=new g;C=[];C.length=39;for(e=0;39>e;e++)C[e]=new g;Y=new l;U=new l;R=new l;P=[];P.length=16;M=[];M.length=573;ca=[];ca.length=573;ma=[];ma.length=256;T=[];T.length=512;$=[];$.length=29;X=[];X.length=30;da=[];da.length=1024}var p=Array(1024),v=[],s=[];for(e=La(p,0,p.length);0 @@ -1239,9 +1391,8 @@ odf.CommandLineTools=function(){this.roundTrip=function(g,l,f){return new odf.Od @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.Member=function(g,l){var f={};this.getMemberId=function(){return g};this.getProperties=function(){return f};this.setProperties=function(g){Object.keys(g).forEach(function(l){f[l]=g[l]})};this.removeProperties=function(g){delete g.fullName;delete g.color;delete g.imageUrl;Object.keys(g).forEach(function(g){f.hasOwnProperty(g)&&delete f[g]})};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");f=l}; -// Input 47 +ops.OpAddMember=function(){var k,h,b;this.init=function(p){k=p.memberid;h=parseInt(p.timestamp,10);b=p.setProperties};this.isEdit=!1;this.group=void 0;this.execute=function(h){var d;if(h.getMember(k))return!1;d=new ops.Member(k,b);h.addMember(d);h.emit(ops.Document.signalMemberAdded,d);return!0};this.spec=function(){return{optype:"AddMember",memberid:k,timestamp:h,setProperties:b}}}; +// Input 48 /* Copyright (C) 2012-2013 KO GmbH @@ -1279,33 +1430,9 @@ ops.Member=function(g,l){var f={};this.getMemberId=function(){return g};this.get @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(f,g,r){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)===t)break;while(a.nextPosition())}}function h(a){return a.nodeType===Node.ELEMENT_NODE&&a.getAttributeNS(c,"nodeId")}function d(a){var b=l;a.setAttributeNS(c,"nodeId",b.toString());l+=1;return b}function m(b,d){var e,k=null;for(b=b.childNodes[d]|| -b;!k&&b&&b!==f;)(e=h(b))&&(k=a[e])&&k.node!==b&&(runtime.log("Cloned node detected. Creating new bookmark"),k=null,b.removeAttributeNS(c,"nodeId")),b=b.parentNode;return k}var c="urn:webodf:names:steps",e={},a={},b=new odf.OdfUtils,q=new core.DomUtils,k,t=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.updateCache=function(c,f,k,g){var q;0===k&&b.isParagraph(f)?(q=!0,g||(c+=1)):f.hasChildNodes()&&f.childNodes[k]&&(f=f.childNodes[k],(q=b.isParagraph(f))&&(c+=1));q&&(k=h(f)||d(f),(g=a[k])?g.node=== -f?g.steps=c:(runtime.log("Cloned node detected. Creating new bookmark"),k=d(f),g=a[k]=new n(c,f)):g=a[k]=new n(c,f),k=g,c=Math.ceil(k.steps/r)*r,f=e[c],!f||k.steps>f.steps)&&(e[c]=k)};this.setToClosestStep=function(a,b){for(var c=Math.floor(a/r)*r,d;!d&&0!==c;)d=e[c],c-=r;d=d||k;d.setIteratorPosition(b);return d.steps};this.setToClosestDomPoint=function(a,b,c){var d;if(a===f&&0===b)d=k;else if(a===f&&b===f.childNodes.length)d=Object.keys(e).map(function(a){return e[a]}).reduce(function(a,b){return b.steps> -a.steps?b:a},k);else if(d=m(a,b),!d)for(c.setUnfilteredPosition(a,b);!d&&c.previousNode();)d=m(c.container(),c.unfilteredDomOffset());d=d||k;d.setIteratorPosition(c);return d.steps};this.updateCacheAtPoint=function(b,c){var d={};Object.keys(a).map(function(b){return a[b]}).filter(function(a){return a.steps>b}).forEach(function(b){var k=Math.ceil(b.steps/r)*r,g,m;if(q.containsNode(f,b.node)){if(c(b),g=Math.ceil(b.steps/r)*r,m=d[g],!m||b.steps>m.steps)d[g]=b}else delete a[h(b.node)];e[k]===b&&delete e[k]}); -Object.keys(d).forEach(function(a){e[a]=d[a]})};k=new function(a,b){this.steps=a;this.node=b;this.setIteratorPosition=function(a){a.setUnfilteredPosition(b,0);do if(g.acceptPosition(a)===t)break;while(a.nextPosition())}}(0,f)}var l=0;ops.StepsTranslator=function(f,l,r,n){function h(){var b=f();b!==m&&(runtime.log("Undo detected. Resetting steps cache"),m=b,c=new g(m,r,n),a=l(m))}function d(a,c){if(!c||r.acceptPosition(a)===b)return!0;for(;a.previousPosition();)if(r.acceptPosition(a)===b){if(c(0,a.container(), -a.unfilteredDomOffset()))return!0;break}for(;a.nextPosition();)if(r.acceptPosition(a)===b){if(c(1,a.container(),a.unfilteredDomOffset()))return!0;break}return!1}var m=f(),c=new g(m,r,n),e=new core.DomUtils,a=l(f()),b=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.convertStepsToDomPoint=function(d){var e,f;0>d&&(runtime.log("warn","Requested steps were negative ("+d+")"),d=0);h();for(e=c.setToClosestStep(d,a);ee.comparePoints(m,0,f,k),f=m,k=k?0:m.childNodes.length);a.setUnfilteredPosition(f,k);d(a,g)||a.setUnfilteredPosition(f,k);g=a.container();k=a.unfilteredDomOffset();f=c.setToClosestDomPoint(g,k,a);if(0>e.comparePoints(a.container(),a.unfilteredDomOffset(), -g,k))return 0b.steps&&(b.steps=0)})}};ops.StepsTranslator.PREVIOUS_STEP=0;ops.StepsTranslator.NEXT_STEP=1;return ops.StepsTranslator})(); -// Input 48 -xmldom.RNG={}; -xmldom.RelaxNGParser=function(){function g(c,d){this.message=function(){d&&(c+=1===d.nodeType?" Element ":" Node ",c+=d.nodeName,d.nodeValue&&(c+=" with value '"+d.nodeValue+"'"),c+=".");return c}}function l(c){if(2>=c.e.length)return c;var d={name:c.name,e:c.e.slice(0,2)};return l({name:c.name,e:[d].concat(c.e.slice(2))})}function f(c){c=c.split(":",2);var e="",a;1===c.length?c=["",c[0]]:e=c[0];for(a in d)d[a]===e&&(c[0]=a);return c}function p(c,d){for(var a=0,b,h,k=c.name;c.e&&a=d&&(c=-n.movePointBackward(-d,h));f.handleUpdate();return c};this.handleUpdate=function(){};this.getStepCounter=function(){return n.getStepCounter()};this.getMemberId=function(){return g};this.getNode=function(){return h.getNode()};this.getAnchorNode=function(){return h.getAnchorNode()};this.getSelectedRange=function(){return h.getSelectedRange()}; -this.setSelectedRange=function(d,g){h.setSelectedRange(d,g);f.handleUpdate()};this.hasForwardSelection=function(){return h.hasForwardSelection()};this.getOdtDocument=function(){return l};this.getSelectionType=function(){return r};this.setSelectionType=function(d){p.hasOwnProperty(d)?r=d:runtime.log("Invalid selection type: "+d)};this.resetSelectionType=function(){f.setSelectionType(ops.OdtCursor.RangeSelection)};h=new core.Cursor(l.getDOM(),g);n=new gui.SelectionMover(h,l.getRootNode());p[ops.OdtCursor.RangeSelection]= -!0;p[ops.OdtCursor.RegionSelection]=!0;f.resetSelectionType()};ops.OdtCursor.RangeSelection="Range";ops.OdtCursor.RegionSelection="Region";(function(){return ops.OdtCursor})(); -// Input 50 /* Copyright (C) 2012-2013 KO GmbH @@ -1343,25 +1470,10 @@ this.setSelectedRange=function(d,g){h.setSelectedRange(d,g);f.handleUpdate()};th @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(g){function l(){var a=g.odfContainer().getContentElement(),b=a&&a.localName;runtime.assert("text"===b,"Unsupported content element type '"+b+"' for OdtDocument");return a}function f(){return l().ownerDocument}function p(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 r(b){this.acceptPosition=function(c){c=c.container();var d;d="string"===typeof b? -a[b].getNode():b;return p(c)===p(d)?k:t}}function n(a){var b=gui.SelectionMover.createPositionIterator(l());a=w.convertStepsToDomPoint(a);b.setUnfilteredPosition(a.node,a.offset);return b}function h(a,b){return g.getFormatting().getStyleElement(a,b)}function d(a){return h(a,"paragraph")}var m=this,c,e,a={},b={},q=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]),k=core.PositionFilter.FilterResult.FILTER_ACCEPT,t=core.PositionFilter.FilterResult.FILTER_REJECT,A,w,x;this.getDOM= -f;this.getRootElement=p;this.getIteratorAtPosition=n;this.convertDomPointToCursorStep=function(a,b,c){return w.convertDomPointToSteps(a,b,c)};this.convertDomToCursorRange=function(a,b){var c,d;c=b(a.anchorNode,a.anchorOffset);c=w.convertDomPointToSteps(a.anchorNode,a.anchorOffset,c);b||a.anchorNode!==a.focusNode||a.anchorOffset!==a.focusOffset?(d=b(a.focusNode,a.focusOffset),d=w.convertDomPointToSteps(a.focusNode,a.focusOffset,d)):d=c;return{position:c,length:d-c}};this.convertCursorToDomRange=function(a, -b){var c=f().createRange(),d,e;d=w.convertStepsToDomPoint(a);b?(e=w.convertStepsToDomPoint(a+b),0=e;e+=1){b=a.container();d=a.unfilteredDomOffset();if(b.nodeType===Node.TEXT_NODE&&" "===b.data[d]&&c.isSignificantWhitespace(b, -d)){runtime.assert(" "===b.data[d],"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(d,1);0 @@ -1399,55 +1511,14 @@ ops.OdtDocument.signalCommonStyleDeleted="style/deleted";ops.OdtDocument.signalP @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 52 -runtime.loadClass("xmldom.RelaxNGParser"); -xmldom.RelaxNG=function(){function g(a){return function(){var b;return function(){void 0===b&&(b=a());return b}}()}function l(a,b){return function(){var c={},d=0;return function(e){var f=e.hash||e.toString();if(c.hasOwnProperty(f))return c[f];c[f]=e=b(e);e.hash=a+d.toString();d+=1;return e}}()}function f(a){return function(){var b={};return function(c){var d,e;if(b.hasOwnProperty(c.localName)){if(e=b[c.localName],d=e[c.namespaceURI],void 0!==d)return d}else b[c.localName]=e={};return e[c.namespaceURI]= -d=a(c)}}()}function p(a,b,c){return function(){var d={},e=0;return function(f,h){var g=b&&b(f,h),k;if(void 0!==g)return g;k=f.hash||f.toString();g=h.hash||h.toString();if(d.hasOwnProperty(k)){if(k=d[k],k.hasOwnProperty(g))return k[g]}else d[k]=k={};k[g]=g=c(f,h);g.hash=a+e.toString();e+=1;return g}}()}function r(a,b){"choice"===b.p1.type?r(a,b.p1):a[b.p1.hash]=b.p1;"choice"===b.p2.type?r(a,b.p2):a[b.p2.hash]=b.p2}function n(a,b){return{type:"element",nc:a,nullable:!1,textDeriv:function(){return y}, -startTagOpenDeriv:function(c){return a.contains(c)?q(b,B):y},attDeriv:function(){return y},startTagCloseDeriv:function(){return this}}}function h(){return{type:"list",nullable:!1,hash:"list",textDeriv:function(){return B}}}function d(a,b,e,f){if(b===y)return y;if(f>=e.length)return b;0===f&&(f=0);for(var h=e.item(f);h.namespaceURI===c;){f+=1;if(f>=e.length)return b;h=e.item(f)}return h=d(a,b.attDeriv(a,e.item(f)),e,f+1)}function m(a,b,c){c.e[0].a?(a.push(c.e[0].text),b.push(c.e[0].a.ns)):m(a,b,c.e[0]); -c.e[1].a?(a.push(c.e[1].text),b.push(c.e[1].a.ns)):m(a,b,c.e[1])}var c="http://www.w3.org/2000/xmlns/",e,a,b,q,k,t,A,w,x,v,u,s,H,y={type:"notAllowed",nullable:!1,hash:"notAllowed",nc:void 0,p:void 0,p1:void 0,p2:void 0,textDeriv:function(){return y},startTagOpenDeriv:function(){return y},attDeriv:function(){return y},startTagCloseDeriv:function(){return y},endTagDeriv:function(){return y}},B={type:"empty",nullable:!0,hash:"empty",nc:void 0,p:void 0,p1:void 0,p2:void 0,textDeriv:function(){return y}, -startTagOpenDeriv:function(){return y},attDeriv:function(){return y},startTagCloseDeriv:function(){return B},endTagDeriv:function(){return y}},L={type:"text",nullable:!0,hash:"text",nc:void 0,p:void 0,p1:void 0,p2:void 0,textDeriv:function(){return L},startTagOpenDeriv:function(){return y},attDeriv:function(){return y},startTagCloseDeriv:function(){return L},endTagDeriv:function(){return y}};e=p("choice",function(a,b){if(a===y)return b;if(b===y||a===b)return a},function(a,b){var c={},d;r(c,{p1:a, -p2:b});b=a=void 0;for(d in c)c.hasOwnProperty(d)&&(void 0===a?a=c[d]:b=void 0===b?c[d]:e(b,c[d]));return function(a,b){return{type:"choice",nullable:a.nullable||b.nullable,hash:void 0,nc:void 0,p:void 0,p1:a,p2:b,textDeriv:function(c,d){return e(a.textDeriv(c,d),b.textDeriv(c,d))},startTagOpenDeriv:f(function(c){return e(a.startTagOpenDeriv(c),b.startTagOpenDeriv(c))}),attDeriv:function(c,d){return e(a.attDeriv(c,d),b.attDeriv(c,d))},startTagCloseDeriv:g(function(){return e(a.startTagCloseDeriv(), -b.startTagCloseDeriv())}),endTagDeriv:g(function(){return e(a.endTagDeriv(),b.endTagDeriv())})}}(a,b)});a=function(a,b,c){return function(){var d={},e=0;return function(f,h){var g=b&&b(f,h),k,m;if(void 0!==g)return g;k=f.hash||f.toString();g=h.hash||h.toString();kNode.ELEMENT_NODE;){if(c!==Node.COMMENT_NODE&&(c!==Node.TEXT_NODE||!/^\s+$/.test(d.currentNode.nodeValue)))return[new g("Not allowed node of type "+ -c+".")];c=(m=d.nextSibling())?m.nodeType:0}if(!m)return[new g("Missing element "+f.names)];if(f.names&&-1===f.names.indexOf(n[m.namespaceURI]+":"+m.localName))return[new g("Found "+m.nodeName+" instead of "+f.names+".",m)];if(d.firstChild()){for(e=l(f.e[1],d,m);d.nextSibling();)if(c=d.currentNode.nodeType,!(d.currentNode&&d.currentNode.nodeType===Node.TEXT_NODE&&/^\s+$/.test(d.currentNode.nodeValue)||c===Node.COMMENT_NODE))return[new g("Spurious content.",d.currentNode)];if(d.parentNode()!==m)return[new g("Implementation error.")]}else e= -l(f.e[1],d,m);d.nextSibling();return e}var p,r,n;r=function(h,d,m,c){var e=h.name,a=null;if("text"===e)a:{for(var b=(h=d.currentNode)?h.nodeType:0;h!==m&&3!==b;){if(1===b){a=[new g("Element not allowed here.",h)];break a}b=(h=d.nextSibling())?h.nodeType:0}d.nextSibling();a=null}else if("data"===e)a=null;else if("value"===e)c!==h.text&&(a=[new g("Wrong value, should be '"+h.text+"', not '"+c+"'",m)]);else if("list"===e)a=null;else if("attribute"===e)a:{if(2!==h.e.length)throw"Attribute with wrong # of elements: "+ -h.e.length;e=h.localnames.length;for(a=0;ar(b,e))&&(e=d));a=e;b=g.getOdtDocument().getOdfCanvas().getZoomLevel();c&&g.getSelectionType()===ops.OdtCursor.RangeSelection? -h.style.visibility="visible":h.style.visibility="hidden";a?(h.style.top="0",e=q.getBoundingClientRect(h),8>a.height&&(a={top:a.top-(8-a.height)/2,height:8}),h.style.height=q.adaptRangeDifferenceToZoomLevel(a.height,b)+"px",h.style.top=q.adaptRangeDifferenceToZoomLevel(a.top-e.top,b)+"px"):(h.style.height="1em",h.style.top="5%")}var h,d,m,c=!0,e=!1,a=!1,b,q=new core.DomUtils;this.handleUpdate=n;this.refreshCursorBlinking=function(){f||g.getSelectedRange().collapsed?(e=!0,p(!0)):(e=!1,h.style.opacity= -"0")};this.setFocus=function(){e=!0;d.markAsFocussed(!0);p(!0)};this.removeFocus=function(){e=!1;d.markAsFocussed(!1);h.style.opacity="1"};this.show=function(){c=!0;n();d.markAsFocussed(!0)};this.hide=function(){c=!1;n();d.markAsFocussed(!1)};this.setAvatarImageUrl=function(a){d.setImageUrl(a)};this.setColor=function(a){h.style.borderColor=a;d.setColor(a)};this.getCursor=function(){return g};this.getFocusElement=function(){return h};this.toggleHandleVisibility=function(){d.isVisible()?d.hide():d.show()}; -this.showHandle=function(){d.show()};this.hideHandle=function(){d.hide()};this.ensureVisible=function(){var a,b,c,d,e=g.getOdtDocument().getOdfCanvas().getElement().parentNode,f;c=e.offsetWidth-e.clientWidth+5;d=e.offsetHeight-e.clientHeight+5;f=h.getBoundingClientRect();a=f.left-c;b=f.top-d;c=f.right+c;d=f.bottom+d;f=e.getBoundingClientRect();bf.bottom&&(e.scrollTop+=d-f.bottom);af.right&&(e.scrollLeft+=c-f.right);n()};this.destroy=function(a){d.destroy(function(b){b? -a(b):(m.removeChild(h),a())})};(function(){var a=g.getOdtDocument().getDOM();h=a.createElementNS(a.documentElement.namespaceURI,"span");h.style.top="5%";m=g.getNode();m.appendChild(h);d=new gui.Avatar(m,l);n()})()}; -// Input 55 -gui.EventManager=function(g){function l(){return g.getOdfCanvas().getElement()}function f(){var c=this,a=[];this.handlers=[];this.isSubscribed=!1;this.handleEvent=function(b){-1===a.indexOf(b)&&(a.push(b),c.handlers.forEach(function(a){a(b)}),runtime.setTimeout(function(){a.splice(a.indexOf(b),1)},0))}}function p(c){var a=c.scrollX,b=c.scrollY;this.restore=function(){c.scrollX===a&&c.scrollY===b||c.scrollTo(a,b)}}function r(c){var a=c.scrollTop,b=c.scrollLeft;this.restore=function(){if(c.scrollTop!== -a||c.scrollLeft!==b)c.scrollTop=a,c.scrollLeft=b}}function n(c,a,b){var f="on"+a,h=!1;c.attachEvent&&(h=c.attachEvent(f,b));!h&&c.addEventListener&&(c.addEventListener(a,b,!1),h=!0);h&&!d[a]||!c.hasOwnProperty(f)||(c[f]=b)}var h=runtime.getWindow(),d={beforecut:!0,beforepaste:!0},m,c;this.subscribe=function(d,a){var b=m[d],f=l();b?(b.handlers.push(a),b.isSubscribed||(b.isSubscribed=!0,n(h,d,b.handleEvent),n(f,d,b.handleEvent),n(c,d,b.handleEvent))):n(f,d,a)};this.unsubscribe=function(c,a){var b=m[c], -d=b&&b.handlers.indexOf(a),f=l();b?-1!==d&&b.handlers.splice(d,1):(b="on"+c,f.detachEvent&&f.detachEvent(b,a),f.removeEventListener&&f.removeEventListener(c,a,!1),f[b]===a&&(f[b]=null))};this.focus=function(){var d,a=l(),b=h.getSelection();if(g.getDOM().activeElement!==l()){for(d=a;d&&!d.scrollTop&&!d.scrollLeft;)d=d.parentNode;d=d?new r(d):new p(h);a.focus();d&&d.restore()}b&&b.extend&&(c.parentNode!==a&&a.appendChild(c),b.collapse(c.firstChild,0),b.extend(c,c.childNodes.length))};(function(){var d= -l(),a=d.ownerDocument;runtime.assert(Boolean(h),"EventManager requires a window object to operate correctly");m={mousedown:new f,mouseup:new f,focus:new f};c=a.createElement("div");c.id="eventTrap";c.setAttribute("contenteditable","true");c.style.position="absolute";c.style.left="-10000px";c.appendChild(a.createTextNode("dummy content"));d.appendChild(c)})()}; -// Input 56 -runtime.loadClass("gui.SelectionMover");gui.ShadowCursor=function(g){var l=g.getDOM().createRange(),f=!0;this.removeFromOdtDocument=function(){};this.getMemberId=function(){return gui.ShadowCursor.ShadowCursorMemberId};this.getSelectedRange=function(){return l};this.setSelectedRange=function(g,r){l=g;f=!1!==r};this.hasForwardSelection=function(){return f};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 57 +odf.TextStyleApplicator=function(k,h,b){function p(b){function d(a,c){return"object"===typeof a&&"object"===typeof c?Object.keys(a).every(function(b){return d(a[b],c[b])}):a===c}var c={};this.isStyleApplied=function(a){a=h.getAppliedStylesForElement(a,c);return d(b,a)}}function d(d){var f={};this.applyStyleToContainer=function(c){var a;a=c.getAttributeNS(q,"style-name");var g=c.ownerDocument;a=a||"";if(!f.hasOwnProperty(a)){var e=a,n;n=a?h.createDerivedStyleObject(a,"text",d):d;g=g.createElementNS(r, +"style:style");h.updateStyle(g,n);g.setAttributeNS(r,"style:name",k.generateStyleName());g.setAttributeNS(r,"style:family","text");g.setAttributeNS("urn:webodf:names:scope","scope","document-content");b.appendChild(g);f[e]=g}a=f[a].getAttributeNS(r,"name");c.setAttributeNS(q,"text:style-name",a)}}function n(b,d){var c=b.ownerDocument,a=b.parentNode,m,e,h=new core.LoopWatchDog(1E4);e=[];"span"!==a.localName||a.namespaceURI!==q?(m=c.createElementNS(q,"text:span"),a.insertBefore(m,b),a=!1):(b.previousSibling&& +!g.rangeContainsNode(d,a.firstChild)?(m=a.cloneNode(!1),a.parentNode.insertBefore(m,a.nextSibling)):m=a,a=!0);e.push(b);for(c=b.nextSibling;c&&g.rangeContainsNode(d,c);)h.check(),e.push(c),c=c.nextSibling;e.forEach(function(a){a.parentNode!==m&&m.appendChild(a)});if(c&&a)for(e=m.cloneNode(!1),m.parentNode.insertBefore(e,m.nextSibling);c;)h.check(),a=c.nextSibling,e.appendChild(c),c=a;return m}var g=new core.DomUtils,q=odf.Namespaces.textns,r=odf.Namespaces.stylens;this.applyStyle=function(b,f,c){var a= +{},g,e,h,k;runtime.assert(c&&c.hasOwnProperty("style:text-properties"),"applyStyle without any text properties");a["style:text-properties"]=c["style:text-properties"];h=new d(a);k=new p(a);b.forEach(function(a){g=k.isStyleApplied(a);!1===g&&(e=n(a,f),h.applyStyleToContainer(e))})}}; +// Input 51 /* - 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 @@ -1482,12 +1553,13 @@ gui.ShadowCursor.ShadowCursorMemberId="";(function(){return gui.ShadowCursor})() @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 58 +ops.OpApplyDirectStyling=function(){function k(b,d,f){var c=b.getOdfCanvas().odfContainer(),a=q.splitBoundaries(d),m=g.getTextNodes(d,!1);d={startContainer:d.startContainer,startOffset:d.startOffset,endContainer:d.endContainer,endOffset:d.endOffset};(new odf.TextStyleApplicator(new odf.ObjectNameGenerator(c,h),b.getFormatting(),c.rootElement.automaticStyles)).applyStyle(m,d,f);a.forEach(q.normalizeTextNodes)}var h,b,p,d,n,g=new odf.OdfUtils,q=new core.DomUtils;this.init=function(g){h=g.memberid;b= +g.timestamp;p=parseInt(g.position,10);d=parseInt(g.length,10);n=g.setProperties};this.isEdit=!0;this.group=void 0;this.execute=function(q){var l=q.convertCursorToDomRange(p,d),f=g.getParagraphElements(l);k(q,l,n);l.detach();q.getOdfCanvas().refreshCSS();q.fixCursorPositions();f.forEach(function(c){q.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:c,memberId:h,timeStamp:b})});q.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"ApplyDirectStyling",memberid:h, +timestamp:b,position:p,length:d,setProperties:n}}}; +// Input 52 /* - 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 @@ -1522,12 +1594,13 @@ 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(f){return f.spec().optype}function l(f){return f.isEdit}this.getOpType=g;this.isEditOperation=l;this.isPartOfOperationSet=function(f,p){if(f.isEdit){if(0===p.length)return!0;var r;if(r=p[p.length-1].isEdit)a:{r=p.filter(l);var n=g(f),h;b:switch(n){case "RemoveText":case "InsertText":h=!0;break b;default:h=!1}if(h&&n===g(r[0])){if(1===r.length){r=!0;break a}n=r[r.length-2].spec().position;r=r[r.length-1].spec().position;h=f.spec().position;if(r===h-(r-n)){r= -!0;break a}}r=!1}return r}return!0}}; -// Input 59 +ops.OpApplyHyperlink=function(){function k(b){for(;b;){if(q.isHyperlink(b))return!0;b=b.parentNode}return!1}var h,b,p,d,n,g=new core.DomUtils,q=new odf.OdfUtils;this.init=function(g){h=g.memberid;b=g.timestamp;p=g.position;d=g.length;n=g.hyperlink};this.isEdit=!0;this.group=void 0;this.execute=function(r){var l=r.getDOMDocument(),f=r.convertCursorToDomRange(p,d),c=g.splitBoundaries(f),a=[],m=q.getTextNodes(f,!1);if(0===m.length)return!1;m.forEach(function(c){var b=q.getParagraphElement(c);runtime.assert(!1=== +k(c),"The given range should not contain any link.");var d=n,f=l.createElementNS(odf.Namespaces.textns,"text:a");f.setAttributeNS(odf.Namespaces.xlinkns,"xlink:type","simple");f.setAttributeNS(odf.Namespaces.xlinkns,"xlink:href",d);c.parentNode.insertBefore(f,c);f.appendChild(c);-1===a.indexOf(b)&&a.push(b)});c.forEach(g.normalizeTextNodes);f.detach();r.getOdfCanvas().refreshSize();r.getOdfCanvas().rerenderAnnotations();a.forEach(function(a){r.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:a, +memberId:h,timeStamp:b})});return!0};this.spec=function(){return{optype:"ApplyHyperlink",memberid:h,timestamp:b,position:p,length:d,hyperlink:n}}}; +// Input 53 /* - 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 @@ -1562,12 +1635,13 @@ gui.UndoStateRules=function(){function g(f){return f.spec().optype}function l(f) @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.EditInfo=function(g,l){function f(){var f=[],h;for(h in r)r.hasOwnProperty(h)&&f.push({memberid:h,time:r[h].time});f.sort(function(d,f){return d.time-f.time});return f}var p,r={};this.getNode=function(){return p};this.getOdtDocument=function(){return l};this.getEdits=function(){return r};this.getSortedEdits=function(){return f()};this.addEdit=function(f,h){r[f]={time:h}};this.clearEdits=function(){r={}};this.destroy=function(f){g.parentNode&&g.removeChild(p);f()};p=l.getDOM().createElementNS("urn:webodf:names:editinfo", -"editinfo");g.insertBefore(p,g.firstChild)}; -// Input 60 +ops.OpInsertImage=function(){var k,h,b,p,d,n,g,q,r=odf.Namespaces.drawns,l=odf.Namespaces.svgns,f=odf.Namespaces.textns,c=odf.Namespaces.xlinkns;this.init=function(a){k=a.memberid;h=a.timestamp;b=a.position;p=a.filename;d=a.frameWidth;n=a.frameHeight;g=a.frameStyleName;q=a.frameName};this.isEdit=!0;this.group=void 0;this.execute=function(a){var m=a.getOdfCanvas(),e=a.getTextNodeAtStep(b,k),t,w;if(!e)return!1;t=e.textNode;w=a.getParagraphElement(t);var e=e.offset!==t.length?t.splitText(e.offset):t.nextSibling, +z=a.getDOMDocument(),x=z.createElementNS(r,"draw:image"),z=z.createElementNS(r,"draw:frame");x.setAttributeNS(c,"xlink:href",p);x.setAttributeNS(c,"xlink:type","simple");x.setAttributeNS(c,"xlink:show","embed");x.setAttributeNS(c,"xlink:actuate","onLoad");z.setAttributeNS(r,"draw:style-name",g);z.setAttributeNS(r,"draw:name",q);z.setAttributeNS(f,"text:anchor-type","as-char");z.setAttributeNS(l,"svg:width",d);z.setAttributeNS(l,"svg:height",n);z.appendChild(x);t.parentNode.insertBefore(z,e);a.emit(ops.OdtDocument.signalStepsInserted, +{position:b,length:1});0===t.length&&t.parentNode.removeChild(t);m.addCssForFrameWithImage(z);m.refreshCSS();a.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:w,memberId:k,timeStamp:h});m.rerenderAnnotations();return!0};this.spec=function(){return{optype:"InsertImage",memberid:k,timestamp:h,filename:p,position:b,frameWidth:d,frameHeight:n,frameStyleName:g,frameName:q}}}; +// Input 54 /* - 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 @@ -1602,12 +1676,12 @@ ops.EditInfo=function(g,l){function f(){var f=[],h;for(h in r)r.hasOwnProperty(h @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils"); -ops.OpAddAnnotation=function(){function g(d,f,c){var e=d.getTextNodeAtStep(c,l);e&&(d=e.textNode,c=d.parentNode,e.offset!==d.length&&d.splitText(e.offset),c.insertBefore(f,d.nextSibling),0===d.length&&c.removeChild(d))}var l,f,p,r,n,h;this.init=function(d){l=d.memberid;f=parseInt(d.timestamp,10);p=parseInt(d.position,10);r=parseInt(d.length,10)||0;n=d.name};this.isEdit=!0;this.execute=function(d){var m={},c=d.getCursor(l),e,a;a=new core.DomUtils;h=d.getDOM();var b=new Date(f),q,k,t,A;e=h.createElementNS(odf.Namespaces.officens, -"office:annotation");e.setAttributeNS(odf.Namespaces.officens,"office:name",n);q=h.createElementNS(odf.Namespaces.dcns,"dc:creator");q.setAttributeNS("urn:webodf:names:editinfo","editinfo:memberid",l);q.textContent=d.getMember(l).getProperties().fullName;k=h.createElementNS(odf.Namespaces.dcns,"dc:date");k.appendChild(h.createTextNode(b.toISOString()));b=h.createElementNS(odf.Namespaces.textns,"text:list");t=h.createElementNS(odf.Namespaces.textns,"text:list-item");A=h.createElementNS(odf.Namespaces.textns, -"text:p");t.appendChild(A);b.appendChild(t);e.appendChild(q);e.appendChild(k);e.appendChild(b);m.node=e;if(!m.node)return!1;if(r){e=h.createElementNS(odf.Namespaces.officens,"office:annotation-end");e.setAttributeNS(odf.Namespaces.officens,"office:name",n);m.end=e;if(!m.end)return!1;g(d,m.end,p+r)}g(d,m.node,p);d.emit(ops.OdtDocument.signalStepsInserted,{position:p,length:r});c&&(e=h.createRange(),a=a.getElementsByTagNameNS(m.node,odf.Namespaces.textns,"p")[0],e.selectNodeContents(a),c.setSelectedRange(e), -d.emit(ops.OdtDocument.signalCursorMoved,c));d.getOdfCanvas().addAnnotation(m);d.fixCursorPositions();return!0};this.spec=function(){return{optype:"AddAnnotation",memberid:l,timestamp:f,position:p,length:r,name:n}}}; -// Input 61 +ops.OpInsertTable=function(){function k(b,c){var a;if(1===l.length)a=l[0];else if(3===l.length)switch(b){case 0:a=l[0];break;case p-1:a=l[2];break;default:a=l[1]}else a=l[b];if(1===a.length)return a[0];if(3===a.length)switch(c){case 0:return a[0];case d-1:return a[2];default:return a[1]}return a[c]}var h,b,p,d,n,g,q,r,l;this.init=function(f){h=f.memberid;b=f.timestamp;n=f.position;p=f.initialRows;d=f.initialColumns;g=f.tableName;q=f.tableStyleName;r=f.tableColumnStyleName;l=f.tableCellStyleMatrix}; +this.isEdit=!0;this.group=void 0;this.execute=function(f){var c=f.getTextNodeAtStep(n),a=f.getRootNode();if(c){var m=f.getDOMDocument(),e=m.createElementNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:table"),l=m.createElementNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:table-column"),w,z,x,v;q&&e.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:style-name",q);g&&e.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:name",g);l.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0", +"table:number-columns-repeated",d);r&&l.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:style-name",r);e.appendChild(l);for(x=0;x @@ -1645,34 +1719,50 @@ d.emit(ops.OdtDocument.signalCursorMoved,c));d.getOdfCanvas().addAnnotation(m);d @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpAddCursor=function(){var g,l;this.init=function(f){g=f.memberid;l=f.timestamp};this.isEdit=!1;this.execute=function(f){var l=f.getCursor(g);if(l)return!1;l=new ops.OdtCursor(g,f);f.addCursor(l);f.emit(ops.OdtDocument.signalCursorAdded,l);return!0};this.spec=function(){return{optype:"AddCursor",memberid:g,timestamp:l}}}; -// Input 62 +ops.OpInsertText=function(){var k,h,b,p,d;this.init=function(n){k=n.memberid;h=n.timestamp;b=n.position;d=n.text;p="true"===n.moveCursor||!0===n.moveCursor};this.isEdit=!0;this.group=void 0;this.execute=function(n){var g,q,r,l=null,f=n.getDOMDocument(),c,a=0,m,e=n.getCursor(k),t;n.upgradeWhitespacesAtPosition(b);if(g=n.getTextNodeAtStep(b)){q=g.textNode;l=q.nextSibling;r=q.parentNode;c=n.getParagraphElement(q);for(t=0;t + 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.Member");ops.OpAddMember=function(){var g,l,f;this.init=function(p){g=p.memberid;l=parseInt(p.timestamp,10);f=p.setProperties};this.isEdit=!1;this.execute=function(l){if(l.getMember(g))return!1;var r=new ops.Member(g,f);l.addMember(r);l.emit(ops.OdtDocument.signalMemberAdded,r);return!0};this.spec=function(){return{optype:"AddMember",memberid:g,timestamp:l,setProperties:f}}}; -// Input 63 +ops.OpMoveCursor=function(){var k,h,b,p,d;this.init=function(n){k=n.memberid;h=n.timestamp;b=n.position;p=n.length||0;d=n.selectionType||ops.OdtCursor.RangeSelection};this.isEdit=!1;this.group=void 0;this.execute=function(h){var g=h.getCursor(k),q;if(!g)return!1;q=h.convertCursorToDomRange(b,p);g.setSelectedRange(q,0<=p);g.setSelectionType(d);h.emit(ops.Document.signalCursorMoved,g);return!0};this.spec=function(){return{optype:"MoveCursor",memberid:k,timestamp:h,position:b,length:p,selectionType:d}}}; +// Input 57 /* Copyright (C) 2012-2013 KO GmbH @@ -1710,10 +1800,9 @@ runtime.loadClass("ops.Member");ops.OpAddMember=function(){var g,l,f;this.init=f @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.Namespaces"); -ops.OpAddStyle=function(){var g,l,f,p,r,n,h=odf.Namespaces.stylens;this.init=function(d){g=d.memberid;l=d.timestamp;f=d.styleName;p=d.styleFamily;r="true"===d.isAutomaticStyle||!0===d.isAutomaticStyle;n=d.setProperties};this.isEdit=!0;this.execute=function(d){var g=d.getOdfCanvas().odfContainer(),c=d.getFormatting(),e=d.getDOM().createElementNS(h,"style:style");if(!e)return!1;n&&c.updateStyle(e,n);e.setAttributeNS(h,"style:family",p);e.setAttributeNS(h,"style:name",f);r?g.rootElement.automaticStyles.appendChild(e): -g.rootElement.styles.appendChild(e);d.getOdfCanvas().refreshCSS();r||d.emit(ops.OdtDocument.signalCommonStyleCreated,{name:f,family:p});return!0};this.spec=function(){return{optype:"AddStyle",memberid:g,timestamp:l,styleName:f,styleFamily:p,isAutomaticStyle:r,setProperties:n}}}; -// Input 64 +ops.OpRemoveAnnotation=function(){var k,h,b,p,d;this.init=function(n){k=n.memberid;h=n.timestamp;b=parseInt(n.position,10);p=parseInt(n.length,10);d=new core.DomUtils};this.isEdit=!0;this.group=void 0;this.execute=function(h){function g(b){r.parentNode.insertBefore(b,r)}for(var k=h.getIteratorAtPosition(b).container(),r;k.namespaceURI!==odf.Namespaces.officens||"annotation"!==k.localName;)k=k.parentNode;if(null===k)return!1;r=k;k=r.annotationEndElement;h.getOdfCanvas().forgetAnnotations();d.getElementsByTagNameNS(r, +"urn:webodf:names:cursor","cursor").forEach(g);d.getElementsByTagNameNS(r,"urn:webodf:names:cursor","anchor").forEach(g);r.parentNode.removeChild(r);k&&k.parentNode.removeChild(k);h.emit(ops.OdtDocument.signalStepsRemoved,{position:0 @@ -1751,11 +1840,8 @@ g.rootElement.styles.appendChild(e);d.getOdfCanvas().refreshCSS();r||d.emit(ops. @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.DomUtils");runtime.loadClass("odf.OdfUtils");runtime.loadClass("odf.TextStyleApplicator"); -ops.OpApplyDirectStyling=function(){function g(f,c,e){var a=f.getOdfCanvas().odfContainer(),b=d.splitBoundaries(c),g=h.getTextNodes(c,!1);c={startContainer:c.startContainer,startOffset:c.startOffset,endContainer:c.endContainer,endOffset:c.endOffset};(new odf.TextStyleApplicator(new odf.ObjectNameGenerator(a,l),f.getFormatting(),a.rootElement.automaticStyles)).applyStyle(g,c,e);b.forEach(d.normalizeTextNodes)}var l,f,p,r,n,h=new odf.OdfUtils,d=new core.DomUtils;this.init=function(d){l=d.memberid;f= -d.timestamp;p=parseInt(d.position,10);r=parseInt(d.length,10);n=d.setProperties};this.isEdit=!0;this.execute=function(d){var c=d.convertCursorToDomRange(p,r),e=h.getImpactedParagraphs(c);g(d,c,n);c.detach();d.getOdfCanvas().refreshCSS();d.fixCursorPositions();e.forEach(function(a){d.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:a,memberId:l,timeStamp:f})});d.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"ApplyDirectStyling",memberid:l,timestamp:f, -position:p,length:r,setProperties:n}}}; -// Input 65 +ops.OpRemoveBlob=function(){var k,h,b;this.init=function(p){k=p.memberid;h=p.timestamp;b=p.filename};this.isEdit=!0;this.group=void 0;this.execute=function(h){h.getOdfCanvas().odfContainer().removeBlob(b);return!0};this.spec=function(){return{optype:"RemoveBlob",memberid:k,timestamp:h,filename:b}}}; +// Input 59 /* Copyright (C) 2012-2013 KO GmbH @@ -1793,13 +1879,11 @@ position:p,length:r,setProperties:n}}}; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpInsertImage=function(){var g,l,f,p,r,n,h,d,m=odf.Namespaces.drawns,c=odf.Namespaces.svgns,e=odf.Namespaces.textns,a=odf.Namespaces.xlinkns;this.init=function(a){g=a.memberid;l=a.timestamp;f=a.position;p=a.filename;r=a.frameWidth;n=a.frameHeight;h=a.frameStyleName;d=a.frameName};this.isEdit=!0;this.execute=function(b){var q=b.getOdfCanvas(),k=b.getTextNodeAtStep(f,g),t,A;if(!k)return!1;t=k.textNode;A=b.getParagraphElement(t);var k=k.offset!==t.length?t.splitText(k.offset):t.nextSibling,w=b.getDOM(), -x=w.createElementNS(m,"draw:image"),w=w.createElementNS(m,"draw:frame");x.setAttributeNS(a,"xlink:href",p);x.setAttributeNS(a,"xlink:type","simple");x.setAttributeNS(a,"xlink:show","embed");x.setAttributeNS(a,"xlink:actuate","onLoad");w.setAttributeNS(m,"draw:style-name",h);w.setAttributeNS(m,"draw:name",d);w.setAttributeNS(e,"text:anchor-type","as-char");w.setAttributeNS(c,"svg:width",r);w.setAttributeNS(c,"svg:height",n);w.appendChild(x);t.parentNode.insertBefore(w,k);b.emit(ops.OdtDocument.signalStepsInserted, -{position:f,length:1});0===t.length&&t.parentNode.removeChild(t);q.addCssForFrameWithImage(w);q.refreshCSS();b.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:A,memberId:g,timeStamp:l});q.rerenderAnnotations();return!0};this.spec=function(){return{optype:"InsertImage",memberid:g,timestamp:l,filename:p,position:f,frameWidth:r,frameHeight:n,frameStyleName:h,frameName:d}}}; -// Input 66 +ops.OpRemoveCursor=function(){var k,h;this.init=function(b){k=b.memberid;h=b.timestamp};this.isEdit=!1;this.group=void 0;this.execute=function(b){return b.removeCursor(k)?!0:!1};this.spec=function(){return{optype:"RemoveCursor",memberid:k,timestamp:h}}}; +// Input 60 /* - 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 @@ -1834,12 +1918,35 @@ x=w.createElementNS(m,"draw:image"),w=w.createElementNS(m,"draw:frame");x.setAtt @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpInsertTable=function(){function g(d,a){var b;if(1===c.length)b=c[0];else if(3===c.length)switch(d){case 0:b=c[0];break;case p-1:b=c[2];break;default:b=c[1]}else b=c[d];if(1===b.length)return b[0];if(3===b.length)switch(a){case 0:return b[0];case r-1:return b[2];default:return b[1]}return b[a]}var l,f,p,r,n,h,d,m,c;this.init=function(e){l=e.memberid;f=e.timestamp;n=e.position;p=e.initialRows;r=e.initialColumns;h=e.tableName;d=e.tableStyleName;m=e.tableColumnStyleName;c=e.tableCellStyleMatrix}; -this.isEdit=!0;this.execute=function(c){var a=c.getTextNodeAtStep(n),b=c.getRootNode();if(a){var q=c.getDOM(),k=q.createElementNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:table"),t=q.createElementNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:table-column"),A,w,x,v;d&&k.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:style-name",d);h&&k.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:name",h);t.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0", -"table:number-columns-repeated",r);m&&t.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:table:1.0","table:style-name",m);k.appendChild(t);for(x=0;x + + @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.OpRemoveMember=function(){var k,h;this.init=function(b){k=b.memberid;h=parseInt(b.timestamp,10)};this.isEdit=!1;this.group=void 0;this.execute=function(b){if(!b.getMember(k))return!1;b.removeMember(k);b.emit(ops.Document.signalMemberRemoved,k);return!0};this.spec=function(){return{optype:"RemoveMember",memberid:k,timestamp:h}}}; +// Input 62 /* Copyright (C) 2012-2013 KO GmbH @@ -1877,11 +1984,8 @@ tableColumnStyleName:m,tableCellStyleMatrix:c}}}; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpInsertText=function(){var g,l,f,p,r;this.init=function(n){g=n.memberid;l=n.timestamp;f=n.position;p=n.text;r="true"===n.moveCursor||!0===n.moveCursor};this.isEdit=!0;this.execute=function(n){var h,d,m,c=null,e=n.getDOM(),a,b=0,q,k=n.getCursor(g),t;n.upgradeWhitespacesAtPosition(f);if(h=n.getTextNodeAtStep(f)){d=h.textNode;c=d.nextSibling;m=d.parentNode;a=n.getParagraphElement(d);for(t=0;t @@ -1919,8 +2023,12 @@ return!0}return!1};this.spec=function(){return{optype:"InsertText",memberid:g,ti @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpMoveCursor=function(){var g,l,f,p,r;this.init=function(n){g=n.memberid;l=n.timestamp;f=n.position;p=n.length||0;r=n.selectionType||ops.OdtCursor.RangeSelection};this.isEdit=!1;this.execute=function(l){var h=l.getCursor(g),d;if(!h)return!1;d=l.convertCursorToDomRange(f,p);h.setSelectedRange(d,0<=p);h.setSelectionType(r);l.emit(ops.OdtDocument.signalCursorMoved,h);return!0};this.spec=function(){return{optype:"MoveCursor",memberid:g,timestamp:l,position:f,length:p,selectionType:r}}}; -// Input 69 +ops.OpRemoveText=function(){function k(b){function d(a){return q.hasOwnProperty(a.namespaceURI)||"br"===a.localName&&n.isLineBreak(a.parentNode)||a.nodeType===Node.TEXT_NODE&&q.hasOwnProperty(a.parentNode.namespaceURI)}function f(a){if(n.isCharacterElement(a))return!1;if(a.nodeType===Node.TEXT_NODE)return 0===a.textContent.length;for(a=a.firstChild;a;){if(q.hasOwnProperty(a.namespaceURI)||!f(a))return!1;a=a.nextSibling}return!0}function c(a){var m;a.nodeType===Node.TEXT_NODE?(m=a.parentNode,m.removeChild(a)): +m=g.removeUnwantedNodes(a,d);return m&&!n.isParagraph(m)&&m!==b&&f(m)?c(m):m}this.isEmpty=f;this.mergeChildrenIntoParent=c}var h,b,p,d,n,g,q={};this.init=function(k){runtime.assert(0<=k.length,"OpRemoveText only supports positive lengths");h=k.memberid;b=k.timestamp;p=parseInt(k.position,10);d=parseInt(k.length,10);n=new odf.OdfUtils;g=new core.DomUtils;q[odf.Namespaces.dbns]=!0;q[odf.Namespaces.dcns]=!0;q[odf.Namespaces.dr3dns]=!0;q[odf.Namespaces.drawns]=!0;q[odf.Namespaces.chartns]=!0;q[odf.Namespaces.formns]= +!0;q[odf.Namespaces.numberns]=!0;q[odf.Namespaces.officens]=!0;q[odf.Namespaces.presentationns]=!0;q[odf.Namespaces.stylens]=!0;q[odf.Namespaces.svgns]=!0;q[odf.Namespaces.tablens]=!0;q[odf.Namespaces.textns]=!0};this.isEdit=!0;this.group=void 0;this.execute=function(q){var l,f,c,a,m=q.getCursor(h),e=new k(q.getRootNode());q.upgradeWhitespacesAtPosition(p);q.upgradeWhitespacesAtPosition(p+d);f=q.convertCursorToDomRange(p,d);g.splitBoundaries(f);l=q.getParagraphElement(f.startContainer);c=n.getTextElements(f, +!1,!0);a=n.getParagraphElements(f);f.detach();c.forEach(function(a){e.mergeChildrenIntoParent(a)});f=a.reduce(function(a,c){var b,d=a,f=c,g,m=null;e.isEmpty(a)&&(c.parentNode!==a.parentNode&&(g=c.parentNode,a.parentNode.insertBefore(c,a.nextSibling)),f=a,d=c,m=d.getElementsByTagNameNS("urn:webodf:names:editinfo","editinfo").item(0)||d.firstChild);for(;f.firstChild;)b=f.firstChild,f.removeChild(b),"editinfo"!==b.localName&&d.insertBefore(b,m);g&&e.isEmpty(g)&&e.mergeChildrenIntoParent(g);e.mergeChildrenIntoParent(f); +return d});q.emit(ops.OdtDocument.signalStepsRemoved,{position:p,length:d});q.downgradeWhitespacesAtPosition(p);q.fixCursorPositions();q.getOdfCanvas().refreshSize();q.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:f||l,memberId:h,timeStamp:b});m&&(m.resetSelectionType(),q.emit(ops.Document.signalCursorMoved,m));q.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"RemoveText",memberid:h,timestamp:b,position:p,length:d}}}; +// Input 64 /* Copyright (C) 2012-2013 KO GmbH @@ -1958,11 +2066,8 @@ ops.OpMoveCursor=function(){var g,l,f,p,r;this.init=function(n){g=n.memberid;l=n @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,f,p,r;this.init=function(n){g=n.memberid;l=n.timestamp;f=parseInt(n.position,10);p=parseInt(n.length,10);r=new core.DomUtils};this.isEdit=!0;this.execute=function(g){for(var h=g.getIteratorAtPosition(f).container(),d,l,c;h.namespaceURI!==odf.Namespaces.officens||"annotation"!==h.localName;)h=h.parentNode;if(null===h)return!1;(d=h.getAttributeNS(odf.Namespaces.officens,"name"))&&(l=r.getElementsByTagNameNS(g.getRootNode(),odf.Namespaces.officens,"annotation-end").filter(function(c){return d=== -c.getAttributeNS(odf.Namespaces.officens,"name")})[0]||null);g.getOdfCanvas().forgetAnnotations();for(c=r.getElementsByTagNameNS(h,"urn:webodf:names:cursor","cursor");c.length;)h.parentNode.insertBefore(c.pop(),h);h.parentNode.removeChild(h);l&&l.parentNode.removeChild(l);g.emit(ops.OdtDocument.signalStepsRemoved,{position:0 @@ -2000,8 +2105,9 @@ length:p}}}; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpRemoveBlob=function(){var g,l,f;this.init=function(p){g=p.memberid;l=p.timestamp;f=p.filename};this.isEdit=!0;this.execute=function(g){g.getOdfCanvas().odfContainer().removeBlob(f);return!0};this.spec=function(){return{optype:"RemoveBlob",memberid:g,timestamp:l,filename:f}}}; -// Input 71 +ops.OpSetParagraphStyle=function(){var k,h,b,p;this.init=function(d){k=d.memberid;h=d.timestamp;b=d.position;p=d.styleName};this.isEdit=!0;this.group=void 0;this.execute=function(d){var n;n=d.getIteratorAtPosition(b);return(n=d.getParagraphElement(n.container()))?(""!==p?n.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0","text:style-name",p):n.removeAttributeNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0","style-name"),d.getOdfCanvas().refreshSize(),d.emit(ops.OdtDocument.signalParagraphChanged, +{paragraphElement:n,timeStamp:h,memberId:k}),d.getOdfCanvas().rerenderAnnotations(),!0):!1};this.spec=function(){return{optype:"SetParagraphStyle",memberid:k,timestamp:h,position:b,styleName:p}}}; +// Input 66 /* Copyright (C) 2012-2013 KO GmbH @@ -2039,8 +2145,10 @@ ops.OpRemoveBlob=function(){var g,l,f;this.init=function(p){g=p.memberid;l=p.tim @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpRemoveCursor=function(){var g,l;this.init=function(f){g=f.memberid;l=f.timestamp};this.isEdit=!1;this.execute=function(f){return f.removeCursor(g)?!0:!1};this.spec=function(){return{optype:"RemoveCursor",memberid:g,timestamp:l}}}; -// Input 72 +ops.OpSplitParagraph=function(){var k,h,b,p,d;this.init=function(n){k=n.memberid;h=n.timestamp;b=n.position;p="true"===n.moveCursor||!0===n.moveCursor;d=new odf.OdfUtils};this.isEdit=!0;this.group=void 0;this.execute=function(n){var g,q,r,l,f,c,a,m=n.getCursor(k);n.upgradeWhitespacesAtPosition(b);g=n.getTextNodeAtStep(b);if(!g)return!1;q=n.getParagraphElement(g.textNode);if(!q)return!1;r=d.isListItem(q.parentNode)?q.parentNode:q;0===g.offset?(a=g.textNode.previousSibling,c=null):(a=g.textNode,c=g.offset>= +g.textNode.length?null:g.textNode.splitText(g.offset));for(l=g.textNode;l!==r;){l=l.parentNode;f=l.cloneNode(!1);c&&f.appendChild(c);if(a)for(;a&&a.nextSibling;)f.appendChild(a.nextSibling);else for(;l.firstChild;)f.appendChild(l.firstChild);l.parentNode.insertBefore(f,l.nextSibling);a=l;c=f}d.isListItem(c)&&(c=c.childNodes.item(0));0===g.textNode.length&&g.textNode.parentNode.removeChild(g.textNode);n.emit(ops.OdtDocument.signalStepsInserted,{position:b,length:1});m&&p&&(n.moveCursor(k,b+1,0),n.emit(ops.Document.signalCursorMoved, +m));n.fixCursorPositions();n.getOdfCanvas().refreshSize();n.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:q,memberId:k,timeStamp:h});n.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:c,memberId:k,timeStamp:h});n.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"SplitParagraph",memberid:k,timestamp:h,position:b,moveCursor:p}}}; +// Input 67 /* Copyright (C) 2013 KO GmbH @@ -2065,8 +2173,35 @@ ops.OpRemoveCursor=function(){var g,l;this.init=function(f){g=f.memberid;l=f.tim @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("ops.Member");ops.OpRemoveMember=function(){var g,l;this.init=function(f){g=f.memberid;l=parseInt(f.timestamp,10)};this.isEdit=!1;this.execute=function(f){if(!f.getMember(g))return!1;f.removeMember(g);f.emit(ops.OdtDocument.signalMemberRemoved,g);return!0};this.spec=function(){return{optype:"RemoveMember",memberid:g,timestamp:l}}}; -// Input 73 +ops.OpUpdateMember=function(){function k(b){var d="//dc:creator[@editinfo:memberid='"+h+"']";b=xmldom.XPath.getODFElementsWithXPath(b.getRootNode(),d,function(b){return"editinfo"===b?"urn:webodf:names:editinfo":odf.Namespaces.lookupNamespaceURI(b)});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/ +*/ +ops.OpUpdateMetadata=function(){var k,h,b,p;this.init=function(d){k=d.memberid;h=parseInt(d.timestamp,10);b=d.setProperties;p=d.removedProperties};this.isEdit=!0;this.group=void 0;this.execute=function(d){d=d.getOdfCanvas().odfContainer();var h=[];p&&(h=p.attributes.split(","));d.setMetadata(b,h);return!0};this.spec=function(){return{optype:"UpdateMetadata",memberid:k,timestamp:h,setProperties:b,removedProperties:p}}}; +// Input 69 /* Copyright (C) 2012-2013 KO GmbH @@ -2104,8 +2239,10 @@ runtime.loadClass("ops.Member");ops.OpRemoveMember=function(){var g,l;this.init= @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpRemoveStyle=function(){var g,l,f,p;this.init=function(r){g=r.memberid;l=r.timestamp;f=r.styleName;p=r.styleFamily};this.isEdit=!0;this.execute=function(g){var l=g.getStyleElement(f,p);if(!l)return!1;l.parentNode.removeChild(l);g.getOdfCanvas().refreshCSS();g.emit(ops.OdtDocument.signalCommonStyleDeleted,{name:f,family:p});return!0};this.spec=function(){return{optype:"RemoveStyle",memberid:g,timestamp:l,styleName:f,styleFamily:p}}}; -// Input 74 +ops.OpUpdateParagraphStyle=function(){function k(b,d){var g,f,c=d?d.split(","):[];for(g=0;g @@ -2143,16 +2280,12 @@ ops.OpRemoveStyle=function(){var g,l,f,p;this.init=function(r){g=r.memberid;l=r. @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(){function g(f){function c(a){return d.hasOwnProperty(a.namespaceURI)||"br"===a.localName&&n.isLineBreak(a.parentNode)||a.nodeType===Node.TEXT_NODE&&d.hasOwnProperty(a.parentNode.namespaceURI)}function e(a){if(n.isCharacterElement(a))return!1;if(a.nodeType===Node.TEXT_NODE)return 0===a.textContent.length;for(a=a.firstChild;a;){if(d.hasOwnProperty(a.namespaceURI)||!e(a))return!1;a=a.nextSibling}return!0}function a(b){var d;b.nodeType===Node.TEXT_NODE?(d=b.parentNode,d.removeChild(b)): -d=h.removeUnwantedNodes(b,c);return!n.isParagraph(d)&&d!==f&&e(d)?a(d):d}this.isEmpty=e;this.mergeChildrenIntoParent=a}var l,f,p,r,n,h,d={};this.init=function(g){runtime.assert(0<=g.length,"OpRemoveText only supports positive lengths");l=g.memberid;f=g.timestamp;p=parseInt(g.position,10);r=parseInt(g.length,10);n=new odf.OdfUtils;h=new core.DomUtils;d[odf.Namespaces.dbns]=!0;d[odf.Namespaces.dcns]=!0;d[odf.Namespaces.dr3dns]=!0;d[odf.Namespaces.drawns]=!0;d[odf.Namespaces.chartns]=!0;d[odf.Namespaces.formns]= -!0;d[odf.Namespaces.numberns]=!0;d[odf.Namespaces.officens]=!0;d[odf.Namespaces.presentationns]=!0;d[odf.Namespaces.stylens]=!0;d[odf.Namespaces.svgns]=!0;d[odf.Namespaces.tablens]=!0;d[odf.Namespaces.textns]=!0};this.isEdit=!0;this.execute=function(d){var c,e,a,b,q=d.getCursor(l),k=new g(d.getRootNode());d.upgradeWhitespacesAtPosition(p);d.upgradeWhitespacesAtPosition(p+r);e=d.convertCursorToDomRange(p,r);h.splitBoundaries(e);c=d.getParagraphElement(e.startContainer);a=n.getTextElements(e,!1,!0); -b=n.getParagraphElements(e);e.detach();a.forEach(function(a){k.mergeChildrenIntoParent(a)});e=b.reduce(function(a,b){var c,d=!1,e=a,f=b,h,g=null;k.isEmpty(a)&&(d=!0,b.parentNode!==a.parentNode&&(h=b.parentNode,a.parentNode.insertBefore(b,a.nextSibling)),f=a,e=b,g=e.getElementsByTagNameNS("urn:webodf:names:editinfo","editinfo")[0]||e.firstChild);for(;f.hasChildNodes();)c=d?f.lastChild:f.firstChild,f.removeChild(c),"editinfo"!==c.localName&&e.insertBefore(c,g);h&&k.isEmpty(h)&&k.mergeChildrenIntoParent(h); -k.mergeChildrenIntoParent(f);return e});d.emit(ops.OdtDocument.signalStepsRemoved,{position:p,length:r});d.downgradeWhitespacesAtPosition(p);d.fixCursorPositions();d.getOdfCanvas().refreshSize();d.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:e||c,memberId:l,timeStamp:f});q&&(q.resetSelectionType(),d.emit(ops.OdtDocument.signalCursorMoved,q));d.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"RemoveText",memberid:l,timestamp:f,position:p,length:r}}}; -// Input 75 +ops.OperationFactory=function(){var k;this.register=function(h,b){k[h]=b};this.create=function(h){var b=null,p=k[h.optype];p&&(b=new p,b.init(h));return b};k={AddMember:ops.OpAddMember,UpdateMember:ops.OpUpdateMember,RemoveMember:ops.OpRemoveMember,AddCursor:ops.OpAddCursor,ApplyDirectStyling:ops.OpApplyDirectStyling,SetBlob:ops.OpSetBlob,RemoveBlob:ops.OpRemoveBlob,InsertImage:ops.OpInsertImage,InsertTable:ops.OpInsertTable,InsertText:ops.OpInsertText,RemoveText:ops.OpRemoveText,SplitParagraph:ops.OpSplitParagraph, +SetParagraphStyle:ops.OpSetParagraphStyle,UpdateParagraphStyle:ops.OpUpdateParagraphStyle,AddStyle:ops.OpAddStyle,RemoveStyle:ops.OpRemoveStyle,MoveCursor:ops.OpMoveCursor,RemoveCursor:ops.OpRemoveCursor,AddAnnotation:ops.OpAddAnnotation,RemoveAnnotation:ops.OpRemoveAnnotation,UpdateMetadata:ops.OpUpdateMetadata,ApplyHyperlink:ops.OpApplyHyperlink,RemoveHyperlink:ops.OpRemoveHyperlink}}; +// Input 71 /* - 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 @@ -2187,11 +2320,12 @@ k.mergeChildrenIntoParent(f);return e});d.emit(ops.OdtDocument.signalStepsRemove @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpSetBlob=function(){var g,l,f,p,r;this.init=function(n){g=n.memberid;l=n.timestamp;f=n.filename;p=n.mimetype;r=n.content};this.isEdit=!0;this.execute=function(g){g.getOdfCanvas().odfContainer().setBlob(f,p,r);return!0};this.spec=function(){return{optype:"SetBlob",memberid:g,timestamp:l,filename:f,mimetype:p,content:r}}}; -// Input 76 +ops.OperationRouter=function(){};ops.OperationRouter.prototype.setOperationFactory=function(k){};ops.OperationRouter.prototype.setPlaybackFunction=function(k){};ops.OperationRouter.prototype.push=function(k){};ops.OperationRouter.prototype.close=function(k){};ops.OperationRouter.prototype.subscribe=function(k,h){};ops.OperationRouter.prototype.unsubscribe=function(k,h){};ops.OperationRouter.prototype.hasLocalUnsyncedOps=function(){};ops.OperationRouter.prototype.hasSessionHostConnection=function(){}; +ops.OperationRouter.signalProcessingBatchStart="router/batchstart";ops.OperationRouter.signalProcessingBatchEnd="router/batchend"; +// Input 72 /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2012 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2226,9 +2360,9 @@ ops.OpSetBlob=function(){var g,l,f,p,r;this.init=function(n){g=n.memberid;l=n.ti @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpSetParagraphStyle=function(){var g,l,f,p;this.init=function(r){g=r.memberid;l=r.timestamp;f=r.position;p=r.styleName};this.isEdit=!0;this.execute=function(r){var n;n=r.getIteratorAtPosition(f);return(n=r.getParagraphElement(n.container()))?(""!==p?n.setAttributeNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0","text:style-name",p):n.removeAttributeNS("urn:oasis:names:tc:opendocument:xmlns:text:1.0","style-name"),r.getOdfCanvas().refreshSize(),r.emit(ops.OdtDocument.signalParagraphChanged, -{paragraphElement:n,timeStamp:l,memberId:g}),r.getOdfCanvas().rerenderAnnotations(),!0):!1};this.spec=function(){return{optype:"SetParagraphStyle",memberid:g,timestamp:l,position:f,styleName:p}}}; -// Input 77 +ops.TrivialOperationRouter=function(){var k=new core.EventNotifier([ops.OperationRouter.signalProcessingBatchStart,ops.OperationRouter.signalProcessingBatchEnd]),h,b,p=0;this.setOperationFactory=function(b){h=b};this.setPlaybackFunction=function(d){b=d};this.push=function(d){p+=1;k.emit(ops.OperationRouter.signalProcessingBatchStart,{});d.forEach(function(d){d=d.spec();d.timestamp=(new Date).getTime();d=h.create(d);d.group="g"+p;b(d)});k.emit(ops.OperationRouter.signalProcessingBatchEnd,{})};this.close= +function(b){b()};this.subscribe=function(b,h){k.subscribe(b,h)};this.unsubscribe=function(b,h){k.unsubscribe(b,h)};this.hasLocalUnsyncedOps=function(){return!1};this.hasSessionHostConnection=function(){return!0}}; +// Input 73 /* Copyright (C) 2012-2013 KO GmbH @@ -2266,10 +2400,9 @@ ops.OpSetParagraphStyle=function(){var g,l,f,p;this.init=function(r){g=r.memberi @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpSplitParagraph=function(){var g,l,f,p,r;this.init=function(n){g=n.memberid;l=n.timestamp;f=n.position;p="true"===n.moveCursor||!0===n.moveCursor;r=new odf.OdfUtils};this.isEdit=!0;this.execute=function(n){var h,d,m,c,e,a,b,q=n.getCursor(g);n.upgradeWhitespacesAtPosition(f);h=n.getTextNodeAtStep(f);if(!h)return!1;d=n.getParagraphElement(h.textNode);if(!d)return!1;m=r.isListItem(d.parentNode)?d.parentNode:d;0===h.offset?(b=h.textNode.previousSibling,a=null):(b=h.textNode,a=h.offset>=h.textNode.length? -null:h.textNode.splitText(h.offset));for(c=h.textNode;c!==m;){c=c.parentNode;e=c.cloneNode(!1);a&&e.appendChild(a);if(b)for(;b&&b.nextSibling;)e.appendChild(b.nextSibling);else for(;c.firstChild;)e.appendChild(c.firstChild);c.parentNode.insertBefore(e,c.nextSibling);b=c;a=e}r.isListItem(a)&&(a=a.childNodes[0]);0===h.textNode.length&&h.textNode.parentNode.removeChild(h.textNode);n.emit(ops.OdtDocument.signalStepsInserted,{position:f,length:1});q&&p&&(n.moveCursor(g,f+1,0),n.emit(ops.OdtDocument.signalCursorMoved, -q));n.fixCursorPositions();n.getOdfCanvas().refreshSize();n.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:d,memberId:g,timeStamp:l});n.emit(ops.OdtDocument.signalParagraphChanged,{paragraphElement:a,memberId:g,timeStamp:l});n.getOdfCanvas().rerenderAnnotations();return!0};this.spec=function(){return{optype:"SplitParagraph",memberid:g,timestamp:l,position:f,moveCursor:p}}}; -// Input 78 +ops.Session=function(k){function h(b){d.emit(ops.OdtDocument.signalProcessingBatchStart,b)}function b(b){d.emit(ops.OdtDocument.signalProcessingBatchEnd,b)}var p=new ops.OperationFactory,d=new ops.OdtDocument(k),n=null;this.setOperationFactory=function(b){p=b;n&&n.setOperationFactory(p)};this.setOperationRouter=function(g){n&&(n.unsubscribe(ops.OperationRouter.signalProcessingBatchStart,h),n.unsubscribe(ops.OperationRouter.signalProcessingBatchEnd,b));n=g;n.subscribe(ops.OperationRouter.signalProcessingBatchStart, +h);n.subscribe(ops.OperationRouter.signalProcessingBatchEnd,b);g.setPlaybackFunction(function(b){d.emit(ops.OdtDocument.signalOperationStart,b);return b.execute(d)?(d.emit(ops.OdtDocument.signalOperationEnd,b),!0):!1});g.setOperationFactory(p)};this.getOperationFactory=function(){return p};this.getOdtDocument=function(){return d};this.enqueue=function(b){n.push(b)};this.close=function(b){n.close(function(h){h?b(h):d.close(b)})};this.destroy=function(b){d.destroy(b)};this.setOperationRouter(new ops.TrivialOperationRouter)}; +// Input 74 /* Copyright (C) 2013 KO GmbH @@ -2294,10 +2427,63 @@ q));n.fixCursorPositions();n.getOdfCanvas().refreshSize();n.emit(ops.OdtDocument @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 f="//dc:creator[@editinfo:memberid='"+l+"']",f=xmldom.XPath.getODFElementsWithXPath(n.getRootNode(),f,function(d){return"editinfo"===d?"urn:webodf:names:editinfo":odf.Namespaces.lookupNamespaceURI(d)}),d;for(d=0;dd(f,h))&&(h=g));f=h;h=k.getDocument().getCanvas();e=h.getZoomLevel();h=a.getBoundingClientRect(h.getSizer());f?(r.style.top="0",g=a.getBoundingClientRect(r),8>f.height&&(f={top:f.top-(8-f.height)/2,height:8}),r.style.height=a.adaptRangeDifferenceToZoomLevel(f.height, +e)+"px",r.style.top=a.adaptRangeDifferenceToZoomLevel(f.top-g.top,e)+"px"):(r.style.height="1em",r.style.top="5%");c&&(f=runtime.getWindow().getComputedStyle(r,null),g=a.getBoundingClientRect(r),c.style.bottom=a.adaptRangeDifferenceToZoomLevel(h.bottom-g.bottom,e)+"px",c.style.left=a.adaptRangeDifferenceToZoomLevel(g.right-h.left,e)+"px",f.font?c.style.font=f.font:(c.style.fontStyle=f.fontStyle,c.style.fontVariant=f.fontVariant,c.style.fontWeight=f.fontWeight,c.style.fontSize=f.fontSize,c.style.lineHeight= +f.lineHeight,c.style.fontFamily=f.fontFamily))}if(z){var h=k.getDocument().getCanvas().getElement().parentNode,p;g=h.offsetWidth-h.clientWidth+5;m=h.offsetHeight-h.clientHeight+5;p=r.getBoundingClientRect();e=p.left-g;f=p.top-m;g=p.right+g;m=p.bottom+m;p=h.getBoundingClientRect();fp.bottom&&(h.scrollTop+=m-p.bottom);ep.right&&(h.scrollLeft+=g-p.right)}}u.isFocused!==v.isFocused&&l.markAsFocussed(v.isFocused);n();x=z=w=!1}function q(a){f.removeChild(r); +a()}var r,l,f,c,a=new core.DomUtils,m=new core.Async,e,t,w=!1,z=!1,x=!1,v={isFocused:!1,isShown:!0,visibility:"hidden"},u={isFocused:!v.isFocused,isShown:!v.isShown,visibility:"hidden"};this.handleUpdate=function(){x=!0;"hidden"!==v.visibility&&(v.visibility="hidden",r.style.visibility="hidden");e.trigger()};this.refreshCursorBlinking=function(){w=!0;e.trigger()};this.setFocus=function(){v.isFocused=!0;e.trigger()};this.removeFocus=function(){v.isFocused=!1;e.trigger()};this.show=function(){v.isShown= +!0;e.trigger()};this.hide=function(){v.isShown=!1;e.trigger()};this.setAvatarImageUrl=function(a){l.setImageUrl(a)};this.setColor=function(a){r.style.borderColor=a;l.setColor(a)};this.getCursor=function(){return k};this.getFocusElement=function(){return r};this.toggleHandleVisibility=function(){l.isVisible()?l.hide():l.show()};this.showHandle=function(){l.show()};this.hideHandle=function(){l.hide()};this.setOverlayElement=function(a){c=a;x=!0;e.trigger()};this.ensureVisible=function(){z=!0;e.trigger()}; +this.destroy=function(a){m.destroyAll([e.destroy,t.destroy,l.destroy,q],a)};(function(){var a=k.getDocument().getDOMDocument();r=a.createElementNS(a.documentElement.namespaceURI,"span");r.className="caret";r.style.top="5%";f=k.getNode();f.appendChild(r);l=new gui.Avatar(f,h);e=new core.ScheduledTask(g,0);t=new core.ScheduledTask(p,500);e.triggerImmediate()})()}; +// Input 77 +/* + + 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.TextSerializer=function(){function k(p){var d="",n=h.filter?h.filter.acceptNode(p):NodeFilter.FILTER_ACCEPT,g=p.nodeType,q;if((n===NodeFilter.FILTER_ACCEPT||n===NodeFilter.FILTER_SKIP)&&b.isTextContentContainingNode(p))for(q=p.firstChild;q;)d+=k(q),q=q.nextSibling;n===NodeFilter.FILTER_ACCEPT&&(g===Node.ELEMENT_NODE&&b.isParagraph(p)?d+="\n":g===Node.TEXT_NODE&&p.textContent&&(d+=p.textContent));return d}var h=this,b=new odf.OdfUtils;this.filter=null;this.writeToString=function(b){if(!b)return""; +b=k(b);"\n"===b[b.length-1]&&(b=b.substr(0,b.length-1));return b}}; +// Input 78 /* Copyright (C) 2013 KO GmbH @@ -2322,12 +2508,11 @@ d.removeProperties(r);p&&(d.setProperties(p),p.fullName&&g());f.emit(ops.OdtDocu @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.OpUpdateMetadata=function(){var g,l,f,p;this.init=function(r){g=r.memberid;l=parseInt(r.timestamp,10);f=r.setProperties;p=r.removedProperties};this.isEdit=!0;this.execute=function(g){g=g.getOdfCanvas().odfContainer();var l=[],h=["dc:date","dc:creator","meta:editing-cycles"];f&&h.forEach(function(d){if(f[d])return!1});p&&(h.forEach(function(d){if(-1!==l.indexOf(d))return!1}),l=p.attributes.split(","));g.setMetadata(f,l);return!0};this.spec=function(){return{optype:"UpdateMetadata",memberid:g,timestamp:l, -setProperties:f,removedProperties:p}}}; -// Input 80 +gui.MimeDataExporter=function(){var k,h;this.exportRangeToDataTransfer=function(b,h){var d;d=h.startContainer.ownerDocument.createElement("span");d.appendChild(h.cloneContents());d=k.writeToString(d);try{b.setData("text/plain",d)}catch(n){b.setData("Text",d)}};k=new odf.TextSerializer;h=new odf.OdfNodeFilter;k.filter=h}; +// Input 79 /* - 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 @@ -2362,14 +2547,11 @@ setProperties:f,removedProperties:p}}}; @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("odf.Namespaces"); -ops.OpUpdateParagraphStyle=function(){function g(d,f){var c,e,a=f?f.split(","):[];for(c=0;c + Copyright (C) 2012-2014 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2404,11 +2586,10 @@ function(){return{optype:"UpdateParagraphStyle",memberid:l,timestamp:f,styleName @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(f){return function(){return new f}}var l;this.register=function(f,g){l[f]=g};this.create=function(f){var g=null,r=l[f.optype];r&&(g=r(f),g.init(f));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 82 +gui.StyleSummary=function(k){function h(b,g){var h=b+"|"+g,p;d.hasOwnProperty(h)||(p=[],k.forEach(function(d){d=(d=d[b])&&d[g];-1===p.indexOf(d)&&p.push(d)}),d[h]=p);return d[h]}function b(b,d,k){return function(){var p=h(b,d);return k.length>=p.length&&p.every(function(b){return-1!==k.indexOf(b)})}}function p(b,d){var k=h(b,d);return 1===k.length?k[0]:void 0}var d={};this.getPropertyValues=h;this.getCommonValue=p;this.isBold=b("style:text-properties","fo:font-weight",["bold"]);this.isItalic=b("style:text-properties", +"fo:font-style",["italic"]);this.hasUnderline=b("style:text-properties","style:text-underline-style",["solid"]);this.hasStrikeThrough=b("style:text-properties","style:text-line-through-style",["solid"]);this.fontSize=function(){var b=p("style:text-properties","fo:font-size");return b&&parseFloat(b)};this.fontName=function(){return p("style:text-properties","style:font-name")};this.isAlignedLeft=b("style:paragraph-properties","fo:text-align",["left","start"]);this.isAlignedCenter=b("style:paragraph-properties", +"fo:text-align",["center"]);this.isAlignedRight=b("style:paragraph-properties","fo:text-align",["right","end"]);this.isAlignedJustified=b("style:paragraph-properties","fo:text-align",["justify"]);this.text={isBold:this.isBold,isItalic:this.isItalic,hasUnderline:this.hasUnderline,hasStrikeThrough:this.hasStrikeThrough,fontSize:this.fontSize,fontName:this.fontName};this.paragraph={isAlignedLeft:this.isAlignedLeft,isAlignedCenter:this.isAlignedCenter,isAlignedRight:this.isAlignedRight,isAlignedJustified:this.isAlignedJustified}}; +// Input 81 /* Copyright (C) 2013 KO GmbH @@ -2446,11 +2627,38 @@ InsertText:g(ops.OpInsertText),RemoveText:g(ops.OpRemoveText),SplitParagraph:g(o @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){};ops.OperationRouter.prototype.subscribe=function(g,l){};ops.OperationRouter.prototype.unsubscribe=function(g,l){};ops.OperationRouter.prototype.hasLocalUnsyncedOps=function(){};ops.OperationRouter.prototype.hasSessionHostConnection=function(){}; +gui.DirectFormattingController=function(k,h,b,p){function d(a){var b;a.collapsed?(b=a.startContainer,b.hasChildNodes()&&a.startOffseta.clientWidth||a.scrollHeight>a.clientHeight)&&c.push(new p(a)), +a=a.parentNode;c.push(new b(r));return c}var r=runtime.getWindow(),l={beforecut:!0,beforepaste:!0},f={mousedown:!0,mouseup:!0,focus:!0},c={},a,m=k.getCanvas().getElement();this.addFilter=function(a,b){n(a,!0).filters.push(b)};this.removeFilter=function(a,b){var c=n(a,!0),d=c.filters.indexOf(b);-1!==d&&c.filters.splice(d,1)};this.subscribe=function(a,b){n(a,!0).handlers.push(b)};this.unsubscribe=function(a,b){var c=n(a,!1),d=c&&c.handlers.indexOf(b);c&&-1!==d&&c.handlers.splice(d,1)};this.hasFocus= +g;this.focus=function(){var b;g()||(b=q(a),a.focus(),b.forEach(function(a){a.restore()}))};this.getEventTrap=function(){return a};this.blur=function(){g()&&a.blur()};this.destroy=function(b){a.parentNode.removeChild(a);b()};(function(){var b=k.getOdfCanvas().getSizer(),c=b.ownerDocument;runtime.assert(Boolean(r),"EventManager requires a window object to operate correctly");a=c.createElement("input");a.id="eventTrap";a.setAttribute("tabindex",-1);b.appendChild(a)})()}; +// Input 85 /* - Copyright (C) 2013 KO GmbH + Copyright (C) 2014 KO GmbH @licstart This file is part of WebODF. @@ -2472,59 +2680,68 @@ ops.OperationRouter=function(){};ops.OperationRouter.prototype.setOperationFacto @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 f(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 p(a,b){a&&["style:parent-style-name","style:next-style-name"].forEach(function(c){a[c]===b&&delete a[c]})}function r(a){var b={};Object.keys(a).forEach(function(c){b[c]="object"===typeof a[c]?r(a[c]):a[c]});return b}function n(a, -b,c,d){var e,f,h=!1,g=!1,l,m,n=d&&d.attributes?d.attributes.split(","):[];a&&(c||0=b.position+b.length)){d=c?a:b;e=c?b:a;if(a.position!==b.position||a.length!==b.length)n=r(d),p=r(e);b=m(e,d,"style:text-properties");if(b.majorChanged||b.minorChanged)f=[],a=[],g=d.position+d.length,l=e.position+e.length,e.positiong?b.minorChanged&&(n=p,n.position=g,n.length=l-g,a.push(n),e.length=g-e.position):g>l&&b.majorChanged&&(n.position=l,n.length=g-l,f.push(n),d.length=l-d.position),d.setProperties&&h(d.setProperties)&&f.push(d),e.setProperties&&h(e.setProperties)&&a.push(e),c?(g=f,f=a):g=a}return{opSpecsA:g,opSpecsB:f}},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:c,RemoveCursor:c,RemoveStyle:c,RemoveText:function(a,b){var c=a.position+a.length,d=b.position+b.length,e=[a],f=[b];d<=a.position?a.position-=b.length:b.positionb.position?a.position+=b.text.length:c?b.position+=a.text.length:a.position+=b.text.length;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:c,UpdateMetadata:c,UpdateParagraphStyle:c},MoveCursor:{MoveCursor:c,RemoveCursor:function(a,b){return{opSpecsA:a.memberid===b.memberid?[]:[a],opSpecsB:[b]}},RemoveMember:c,RemoveStyle:c,RemoveText:function(a,b){var c=l(a),d=a.position+a.length,e=b.position+b.length;e<=a.position?a.position-=b.length: -b.positionb.position?a.position+=1:a.position===b.position&&(c?b.position+=1:a.position+=1);return{opSpecsA:[a],opSpecsB:[b]}},UpdateMember:c,UpdateMetadata:c,UpdateParagraphStyle:c},UpdateMember:{UpdateMetadata:c,UpdateParagraphStyle:c},UpdateMetadata:{UpdateMetadata:function(a,b,c){var e,f=[a],g=[b];e=c?a:b;a=c?b:a;n(a.setProperties||null, -a.removedProperties||null,e.setProperties||null,e.removedProperties||null);e.setProperties&&h(e.setProperties)||e.removedProperties&&d(e.removedProperties)||(c?f=[]:g=[]);a.setProperties&&h(a.setProperties)||a.removedProperties&&d(a.removedProperties)||(c?g=[]:f=[]);return{opSpecsA:f,opSpecsB:g}},UpdateParagraphStyle:c},UpdateParagraphStyle:{UpdateParagraphStyle:function(a,b,c){var e,f=[a],g=[b];a.styleName===b.styleName&&(e=c?a:b,a=c?b:a,m(a,e,"style:paragraph-properties"),m(a,e,"style:text-properties"), -n(a.setProperties||null,a.removedProperties||null,e.setProperties||null,e.removedProperties||null),e.setProperties&&h(e.setProperties)||e.removedProperties&&d(e.removedProperties)||(c?f=[]:g=[]),a.setProperties&&h(a.setProperties)||a.removedProperties&&d(a.removedProperties)||(c?g=[]:f=[]));return{opSpecsA:f,opSpecsB:g}}}};this.passUnchanged=c;this.extendTransformations=function(a){Object.keys(a).forEach(function(b){var c=a[b],d,f=e.hasOwnProperty(b);runtime.log((f?"Extending":"Adding")+" map for optypeA: "+ -b);f||(e[b]={});d=e[b];Object.keys(c).forEach(function(a){var e=d.hasOwnProperty(a);runtime.assert(b<=a,"Wrong order:"+b+", "+a);runtime.log(" "+(e?"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=e[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 84 +gui.IOSSafariSupport=function(k){function h(){b.innerHeight!==b.outerHeight&&(p.style.display="none",runtime.requestAnimationFrame(function(){p.style.display="block"}))}var b=runtime.getWindow(),p=k.getEventTrap();this.destroy=function(b){k.unsubscribe("focus",h);p.removeAttribute("autocapitalize");b()};k.subscribe("focus",h);p.setAttribute("autocapitalize","off")}; +// Input 86 +gui.ImageController=function(k,h,b){var p={"image/gif":".gif","image/jpeg":".jpg","image/png":".png"},d=odf.Namespaces.textns,n=k.getOdtDocument(),g=n.getFormatting(),q={};this.insertImage=function(r,l,f,c){var a;runtime.assert(0a.width&&(m=a.width/f);c>a.height&&(e=a.height/c);m=Math.min(m,e);a=f*m;f=c*m;e=n.getOdfCanvas().odfContainer().rootElement.styles;c=r.toLowerCase();var m=p.hasOwnProperty(c)?p[c]:null,t;c=[];runtime.assert(null!==m,"Image type is not supported: "+r);m="Pictures/"+b.generateImageName()+m;t=new ops.OpSetBlob;t.init({memberid:h,filename:m,mimetype:r,content:l});c.push(t);g.getStyleElement("Graphics","graphic",[e])||(r=new ops.OpAddStyle,r.init({memberid:h,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"}}}),c.push(r));r=b.generateStyleName();l=new ops.OpAddStyle;l.init({memberid:h,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"}}}); +c.push(l);t=new ops.OpInsertImage;t.init({memberid:h,position:n.getCursorPosition(h),filename:m,frameWidth:a+"cm",frameHeight:f+"cm",frameStyleName:r,frameName:b.generateFrameName()});c.push(t);k.enqueue(c)}}; +// Input 87 +gui.ImageSelector=function(k){function h(){var b=k.getSizer(),h=d.createElement("div");h.id="imageSelector";h.style.borderWidth="1px";b.appendChild(h);p.forEach(function(b){var g=d.createElement("div");g.className=b;h.appendChild(g)});return h}var b=odf.Namespaces.svgns,p="topLeft topRight bottomRight bottomLeft topMiddle rightMiddle bottomMiddle leftMiddle".split(" "),d=k.getElement().ownerDocument,n=!1;this.select=function(g){var p,r,l=d.getElementById("imageSelector");l||(l=h());n=!0;p=l.parentNode; +r=g.getBoundingClientRect();var f=p.getBoundingClientRect(),c=k.getZoomLevel();p=(r.left-f.left)/c-1;r=(r.top-f.top)/c-1;l.style.display="block";l.style.left=p+"px";l.style.top=r+"px";l.style.width=g.getAttributeNS(b,"width");l.style.height=g.getAttributeNS(b,"height")};this.clearSelection=function(){var b;n&&(b=d.getElementById("imageSelector"))&&(b.style.display="none");n=!1};this.isSelectorElement=function(b){var h=d.getElementById("imageSelector");return h?b===h||b.parentNode===h:!1}}; +// Input 88 +(function(){function k(h){function b(b){g=b.which&&String.fromCharCode(b.which)===n;n=void 0;return!1===g}function k(){g=!1}function d(b){n=b.data;g=!1}var n,g=!1;this.destroy=function(g){h.unsubscribe("textInput",k);h.unsubscribe("compositionend",d);h.removeFilter("keypress",b);g()};h.subscribe("textInput",k);h.subscribe("compositionend",d);h.addFilter("keypress",b)}gui.InputMethodEditor=function(h,b){function p(b){m&&(b?m.getNode().setAttributeNS(a,"composing","true"):(m.getNode().removeAttributeNS(a, +"composing"),w.textContent=""))}function d(){u&&(u=!1,p(!1),A.emit(gui.InputMethodEditor.signalCompositionEnd,{data:s}),s="")}function n(){d();m&&m.getSelectedRange().collapsed?e.value="":e.value=x;e.setSelectionRange(0,e.value.length)}function g(){J=void 0;v.cancel();p(!0);u||A.emit(gui.InputMethodEditor.signalCompositionStart,{data:""})}function q(a){a=J=a.data;u=!0;s+=a;v.trigger()}function r(a){a.data!==J&&(a=a.data,u=!0,s+=a,v.trigger());J=void 0}function l(){w.textContent=e.value}function f(){b.blur(); +e.setAttribute("disabled",!0)}function c(){var a=b.hasFocus();a&&b.blur();F?e.removeAttribute("disabled"):e.setAttribute("disabled",!0);a&&b.focus()}var a="urn:webodf:names:cursor",m=null,e=b.getEventTrap(),t=e.ownerDocument,w,z=new core.Async,x="b",v,u=!1,s="",A=new core.EventNotifier([gui.InputMethodEditor.signalCompositionStart,gui.InputMethodEditor.signalCompositionEnd]),J,G=[],D,F=!1;this.subscribe=A.subscribe;this.unsubscribe=A.unsubscribe;this.registerCursor=function(a){a.getMemberId()===h&& +(m=a,m.getNode().appendChild(w),b.subscribe("input",l),b.subscribe("compositionupdate",l))};this.removeCursor=function(a){m&&a===h&&(m.getNode().removeChild(w),b.unsubscribe("input",l),b.unsubscribe("compositionupdate",l),m=null)};this.setEditing=function(a){F=a;c()};this.destroy=function(a){b.unsubscribe("compositionstart",g);b.unsubscribe("compositionend",q);b.unsubscribe("textInput",r);b.unsubscribe("keypress",d);b.unsubscribe("mousedown",f);b.unsubscribe("mouseup",c);b.unsubscribe("focus",n); +z.destroyAll(D,a)};(function(){b.subscribe("compositionstart",g);b.subscribe("compositionend",q);b.subscribe("textInput",r);b.subscribe("keypress",d);b.subscribe("mousedown",f);b.subscribe("mouseup",c);b.subscribe("focus",n);G.push(new k(b));D=G.map(function(a){return a.destroy});w=t.createElement("span");w.setAttribute("id","composer");v=new core.ScheduledTask(n,1);D.push(v.destroy)})()};gui.InputMethodEditor.signalCompositionStart="input/compositionstart";gui.InputMethodEditor.signalCompositionEnd= +"input/compositionend";return gui.InputMethodEditor})(); +// Input 89 /* - 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(g){var l=[];g.forEach(function(h){l.push(f.create(h))});return l}function l(f,g){for(var h,d,m=[],c=[];0 + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2559,11 +2776,11 @@ this.setOperationFactory=function(g){f=g};this.getOperationTransformMatrix=funct @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -ops.TrivialOperationRouter=function(){var g,l;this.setOperationFactory=function(f){g=f};this.setPlaybackFunction=function(f){l=f};this.push=function(f){f.forEach(function(f){f=f.spec();f.timestamp=(new Date).getTime();f=g.create(f);l(f)})};this.close=function(f){f()};this.subscribe=function(f,g){};this.unsubscribe=function(f,g){};this.hasLocalUnsyncedOps=function(){return!1};this.hasSessionHostConnection=function(){return!0}}; -// Input 86 +gui.PlainTextPasteboard=function(k,h){function b(b,d){b.init(d);return b}this.createPasteOps=function(p){var d=k.getCursorPosition(h),n=d,g=[];p.replace(/\r/g,"").split("\n").forEach(function(d){g.push(b(new ops.OpSplitParagraph,{memberid:h,position:n,moveCursor:!0}));n+=1;g.push(b(new ops.OpInsertText,{memberid:h,position:n,text:d,moveCursor:!0}));n+=d.length});g.push(b(new ops.OpRemoveText,{memberid:h,position:d,length:1}));return g}}; +// Input 91 /* - Copyright (C) 2012-2013 KO GmbH + Copyright (C) 2014 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2598,11 +2815,25 @@ ops.TrivialOperationRouter=function(){var g,l;this.setOperationFactory=function( @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 f(c,d){return runtime.setTimeout(function(){h.style.opacity=c},d)}var p=this,r,n,h,d,m;this.addEdit=function(c,e){var a=Date.now()-e;g.addEdit(c,e);n.setEdits(g.getSortedEdits());h.setAttributeNS("urn:webodf:names:editinfo","editinfo:memberid",c);d&&runtime.clearTimeout(d);m&&runtime.clearTimeout(m);1E4>a?(f(1,0),d=f(0.5,1E4-a),m=f(0.2,2E4-a)):1E4<=a&&2E4>a?(f(0.5,0),m=f(0.2,2E4-a)):f(0.2,0)};this.getEdits=function(){return g.getEdits()};this.clearEdits=function(){g.clearEdits(); -n.setEdits([]);h.hasAttributeNS("urn:webodf:names:editinfo","editinfo:memberid")&&h.removeAttributeNS("urn:webodf:names:editinfo","editinfo:memberid")};this.getEditInfo=function(){return g};this.show=function(){h.style.display="block"};this.hide=function(){p.hideHandle();h.style.display="none"};this.showHandle=function(){n.show()};this.hideHandle=function(){n.hide()};this.destroy=function(c){r.removeChild(h);n.destroy(function(d){d?c(d):g.destroy(c)})};(function(){var c=g.getOdtDocument().getDOM(); -h=c.createElementNS(c.documentElement.namespaceURI,"div");h.setAttribute("class","editInfoMarker");h.onmouseover=function(){p.showHandle()};h.onmouseout=function(){p.hideHandle()};r=g.getNode();r.appendChild(h);n=new gui.EditInfoHandle(r);l||p.hide()})()}; -// Input 87 +odf.WordBoundaryFilter=function(k,h){function b(a,b,c){for(var d=null,f=k.getRootNode(),g;a!==f&&null!==a&&null===d;)g=0>b?a.previousSibling:a.nextSibling,c(g)===NodeFilter.FILTER_ACCEPT&&(d=g),a=a.parentNode;return d}function p(a,b){var c;return null===a?m.NO_NEIGHBOUR:g.isCharacterElement(a)?m.SPACE_CHAR:a.nodeType===d||g.isTextSpan(a)||g.isHyperlink(a)?(c=a.textContent.charAt(b()),r.test(c)?m.SPACE_CHAR:q.test(c)?m.PUNCTUATION_CHAR:m.WORD_CHAR):m.OTHER}var d=Node.TEXT_NODE,n=Node.ELEMENT_NODE, +g=new odf.OdfUtils,q=/[!-#%-*,-\/:-;?-@\[-\]_{}\u00a1\u00ab\u00b7\u00bb\u00bf;\u00b7\u055a-\u055f\u0589-\u058a\u05be\u05c0\u05c3\u05c6\u05f3-\u05f4\u0609-\u060a\u060c-\u060d\u061b\u061e-\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0964-\u0965\u0970\u0df4\u0e4f\u0e5a-\u0e5b\u0f04-\u0f12\u0f3a-\u0f3d\u0f85\u0fd0-\u0fd4\u104a-\u104f\u10fb\u1361-\u1368\u166d-\u166e\u169b-\u169c\u16eb-\u16ed\u1735-\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u180a\u1944-\u1945\u19de-\u19df\u1a1e-\u1a1f\u1b5a-\u1b60\u1c3b-\u1c3f\u1c7e-\u1c7f\u2000-\u206e\u207d-\u207e\u208d-\u208e\u3008-\u3009\u2768-\u2775\u27c5-\u27c6\u27e6-\u27ef\u2983-\u2998\u29d8-\u29db\u29fc-\u29fd\u2cf9-\u2cfc\u2cfe-\u2cff\u2e00-\u2e7e\u3000-\u303f\u30a0\u30fb\ua60d-\ua60f\ua673\ua67e\ua874-\ua877\ua8ce-\ua8cf\ua92e-\ua92f\ua95f\uaa5c-\uaa5f\ufd3e-\ufd3f\ufe10-\ufe19\ufe30-\ufe52\ufe54-\ufe61\ufe63\ufe68\ufe6a-\ufe6b\uff01-\uff03\uff05-\uff0a\uff0c-\uff0f\uff1a-\uff1b\uff1f-\uff20\uff3b-\uff3d\uff3f\uff5b\uff5d\uff5f-\uff65]|\ud800[\udd00-\udd01\udf9f\udfd0]|\ud802[\udd1f\udd3f\ude50-\ude58]|\ud809[\udc00-\udc7e]/, +r=/\s/,l=core.PositionFilter.FilterResult.FILTER_ACCEPT,f=core.PositionFilter.FilterResult.FILTER_REJECT,c=odf.WordBoundaryFilter.IncludeWhitespace.TRAILING,a=odf.WordBoundaryFilter.IncludeWhitespace.LEADING,m={NO_NEIGHBOUR:0,SPACE_CHAR:1,PUNCTUATION_CHAR:2,WORD_CHAR:3,OTHER:4};this.acceptPosition=function(e){var d=e.container(),g=e.leftNode(),k=e.rightNode(),q=e.unfilteredDomOffset,r=function(){return e.unfilteredDomOffset()-1};d.nodeType===n&&(null===k&&(k=b(d,1,e.getNodeFilter())),null===g&&(g= +b(d,-1,e.getNodeFilter())));d!==k&&(q=function(){return 0});d!==g&&null!==g&&(r=function(){return g.textContent.length-1});d=p(g,r);k=p(k,q);return d===m.WORD_CHAR&&k===m.WORD_CHAR||d===m.PUNCTUATION_CHAR&&k===m.PUNCTUATION_CHAR||h===c&&d!==m.NO_NEIGHBOUR&&k===m.SPACE_CHAR||h===a&&d===m.SPACE_CHAR&&k!==m.NO_NEIGHBOUR?f:l}};odf.WordBoundaryFilter.IncludeWhitespace={None:0,TRAILING:1,LEADING:2};(function(){return odf.WordBoundaryFilter})(); +// Input 92 +gui.SelectionController=function(k,h){function b(){var a=x.getCursor(h).getNode();return x.createStepIterator(a,0,[s,J],x.getRootElement(a))}function p(a,b,c){c=new odf.WordBoundaryFilter(x,c);return x.createStepIterator(a,b,[s,J,c],x.getRootElement(a))}function d(a){return function(b){var c=a(b);return function(b,d){return a(d)===c}}}function n(a,b){return b?{anchorNode:a.startContainer,anchorOffset:a.startOffset,focusNode:a.endContainer,focusOffset:a.endOffset}:{anchorNode:a.endContainer,anchorOffset:a.endOffset, +focusNode:a.startContainer,focusOffset:a.startOffset}}function g(a,b,c){var d=new ops.OpMoveCursor;d.init({memberid:h,position:a,length:b||0,selectionType:c});return d}function q(a){var b;b=p(a.startContainer,a.startOffset,G);b.roundToPreviousStep()&&a.setStart(b.container(),b.offset());b=p(a.endContainer,a.endOffset,D);b.roundToNextStep()&&a.setEnd(b.container(),b.offset())}function r(a){var b=u.getParagraphElements(a),c=b[0],b=b[b.length-1];c&&a.setStart(c,0);b&&(u.isParagraph(a.endContainer)&& +0===a.endOffset?a.setEndBefore(b):a.setEnd(b,b.childNodes.length))}function l(a){var b=x.getCursorSelection(h),c=x.getCursor(h).getStepCounter();0!==a&&(a=0a?(c.focusNode=e,c.focusOffset=0):(c.focusNode=e,c.focusOffset=e.childNodes.length);e=x.convertDomToCursorRange(c,d(b));k.enqueue([g(e.position,e.length)])}function z(a){var b=x.getCursor(h),b=x.getRootElement(b.getNode());runtime.assert(Boolean(b),"SelectionController: Cursor outside root");a=0>a?x.convertDomPointToCursorStep(b,0,function(a){return a===ops.StepsTranslator.NEXT_STEP}):x.convertDomPointToCursorStep(b, +b.childNodes.length);k.enqueue([g(a,0)]);return!0}var x=k.getOdtDocument(),v=new core.DomUtils,u=new odf.OdfUtils,s=x.getPositionFilter(),A=new core.PositionFilterChain,J=x.createRootFilter(h),G=odf.WordBoundaryFilter.IncludeWhitespace.TRAILING,D=odf.WordBoundaryFilter.IncludeWhitespace.LEADING;this.selectionToRange=function(a){var b=0<=v.comparePoints(a.anchorNode,a.anchorOffset,a.focusNode,a.focusOffset),c=a.focusNode.ownerDocument.createRange();b?(c.setStart(a.anchorNode,a.anchorOffset),c.setEnd(a.focusNode, +a.focusOffset)):(c.setStart(a.focusNode,a.focusOffset),c.setEnd(a.anchorNode,a.anchorOffset));return{range:c,hasForwardSelection:b}};this.rangeToSelection=n;this.selectImage=function(a){var b=x.getRootElement(a),c=x.createRootFilter(b),b=x.createStepIterator(a,0,[c,x.getPositionFilter()],b),d;b.roundToPreviousStep()||runtime.assert(!1,"No walkable position before frame");c=b.container();d=b.offset();b.setPosition(a,a.childNodes.length);b.roundToNextStep()||runtime.assert(!1,"No walkable position after frame"); +a=x.convertDomToCursorRange({anchorNode:c,anchorOffset:d,focusNode:b.container(),focusOffset:b.offset()});a=g(a.position,a.length,ops.OdtCursor.RegionSelection);k.enqueue([a])};this.expandToWordBoundaries=q;this.expandToParagraphBoundaries=r;this.selectRange=function(a,b,c){var e=x.getOdfCanvas().getElement(),f;f=v.containsNode(e,a.startContainer);e=v.containsNode(e,a.endContainer);if(f||e)if(f&&e&&(2===c?q(a):3<=c&&r(a)),a=n(a,b),b=x.convertDomToCursorRange(a,d(u.getParagraphElement)),a=x.getCursorSelection(h), +b.position!==a.position||b.length!==a.length)a=g(b.position,b.length,ops.OdtCursor.RangeSelection),k.enqueue([a])};this.moveCursorToLeft=function(){a(function(a){return a.previousStep()});return!0};this.moveCursorToRight=function(){a(function(a){return a.nextStep()});return!0};this.extendSelectionToLeft=function(){f(function(a){return a.previousStep()});return!0};this.extendSelectionToRight=function(){f(function(a){return a.nextStep()});return!0};this.moveCursorUp=function(){m(-1,!1);return!0};this.moveCursorDown= +function(){m(1,!1);return!0};this.extendSelectionUp=function(){m(-1,!0);return!0};this.extendSelectionDown=function(){m(1,!0);return!0};this.moveCursorBeforeWord=function(){t(-1,!1);return!0};this.moveCursorPastWord=function(){t(1,!1);return!0};this.extendSelectionBeforeWord=function(){t(-1,!0);return!0};this.extendSelectionPastWord=function(){t(1,!0);return!0};this.moveCursorToLineStart=function(){e(-1,!1);return!0};this.moveCursorToLineEnd=function(){e(1,!1);return!0};this.extendSelectionToLineStart= +function(){e(-1,!0);return!0};this.extendSelectionToLineEnd=function(){e(1,!0);return!0};this.extendSelectionToParagraphStart=function(){w(-1,x.getParagraphElement);return!0};this.extendSelectionToParagraphEnd=function(){w(1,x.getParagraphElement);return!0};this.moveCursorToDocumentStart=function(){z(-1);return!0};this.moveCursorToDocumentEnd=function(){z(1);return!0};this.extendSelectionToDocumentStart=function(){w(-1,x.getRootElement);return!0};this.extendSelectionToDocumentEnd=function(){w(1,x.getRootElement); +return!0};this.extendSelectionToEntireDocument=function(){var a=x.getCursor(h),a=x.getRootElement(a.getNode());runtime.assert(Boolean(a),"SelectionController: Cursor outside root");a=x.convertDomToCursorRange({anchorNode:a,anchorOffset:0,focusNode:a,focusOffset:a.childNodes.length},d(x.getRootElement));k.enqueue([g(a.position,a.length)]);return!0};A.addFilter(s);A.addFilter(x.createRootFilter(h))}; +// Input 93 /* Copyright (C) 2013 KO GmbH @@ -2640,21 +2871,11 @@ h=c.createElementNS(c.documentElement.namespaceURI,"div");h.setAttribute("class" @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -gui.PlainTextPasteboard=function(g,l){function f(f,g){f.init(g);return f}this.createPasteOps=function(p){var r=g.getCursorPosition(l),n=r,h=[];p.replace(/\r/g,"").split("\n").forEach(function(d){h.push(f(new ops.OpSplitParagraph,{memberid:l,position:n,moveCursor:!0}));n+=1;h.push(f(new ops.OpInsertText,{memberid:l,position:n,text:d,moveCursor:!0}));n+=d.length});h.push(f(new ops.OpRemoveText,{memberid:l,position:r,length:1}));return h}}; -// Input 88 -runtime.loadClass("core.DomUtils");runtime.loadClass("odf.OdfUtils");runtime.loadClass("odf.OdfNodeFilter");runtime.loadClass("gui.SelectionMover"); -gui.SelectionView=function(g){function l(){var a=q.getRootNode();k!==a&&(k=a,t=k.parentNode.parentNode.parentNode,t.appendChild(w),t.appendChild(x),t.appendChild(v))}function f(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 p(a){H=a;w.style.display=x.style.display=v.style.display=!0===a?"block":"none"}function r(a){var b=s.getBoundingClientRect(t),c=q.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 h(a){var b=u.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&&hb.length&&(b.position+=b.length,b.length=-b.length);return b}function g(b,d){var c=new core.PositionFilterChain,a=gui.SelectionMover.createPositionIterator(q.getRootElement(b)),g=d?a.nextPosition:a.previousPosition;c.addFilter(q.getPositionFilter());c.addFilter(q.createRootFilter(h));for(a.setUnfilteredPosition(b,0);g();)if(c.acceptPosition(a)=== +r)return!0;return!1}var q=k.getOdtDocument(),r=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.enqueueParagraphSplittingOps=function(){var b=n(q.getCursorSelection(h)),f,c=[];0 @@ -2692,13 +2913,40 @@ a);w.className=x.className=v.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(f){return l[f]})}var l={};this.getSelectionView=function(f){return l.hasOwnProperty(f)?l[f]:null};this.getSelectionViews=g;this.removeSelectionView=function(f){l.hasOwnProperty(f)&&(l[f].destroy(function(){}),delete l[f])};this.hideSelectionView=function(f){l.hasOwnProperty(f)&&l[f].hide()};this.showSelectionView=function(f){l.hasOwnProperty(f)&&l[f].show()};this.rerenderSelectionViews=function(){Object.keys(l).forEach(function(f){l[f].visible()&& -l[f].rerender()})};this.registerCursor=function(f,g){var r=f.getMemberId(),n=new gui.SelectionView(f);g?n.show():n.hide();return l[r]=n};this.destroy=function(f){var l=g();(function n(g,d){d?f(d):gc.right&&(c=w(d)))b.anchorNode=b.focusNode= +c.container,b.anchorOffset=b.focusOffset=c.offset}else W.isImage(b.focusNode.firstChild)&&1===b.focusOffset&&W.isCharacterFrame(b.focusNode)&&(c=w(b.focusNode))&&(b.anchorNode=b.focusNode=c.container,b.anchorOffset=b.focusOffset=c.offset);b.anchorNode&&b.focusNode&&(b=L.selectionToRange(b),L.selectRange(b.range,b.hasForwardSelection,a.detail));E.focus()}function x(a){var b=a.target||a.srcElement||null,c,d;V.processRequests();W.isImage(b)&&W.isCharacterFrame(b.parentNode)&&O.getSelection().isCollapsed? +(L.selectImage(b.parentNode),E.focus()):P.isSelectorElement(b)?E.focus():R&&(ca?(b=p.getSelectedRange(),c=b.collapsed,W.isImage(b.endContainer)&&0===b.endOffset&&W.isCharacterFrame(b.endContainer.parentNode)&&(d=b.endContainer.parentNode,d=w(d))&&(b.setEnd(d.container,d.offset),c&&b.collapse(!1)),L.selectRange(b,p.hasForwardSelection(),a.detail),E.focus()):ma?z(a):Y=runtime.setTimeout(function(){z(a)},0));ea=0;ca=R=!1}function v(a){var c=K.getCursor(b).getSelectedRange();c.collapsed||H.exportRangeToDataTransfer(a.dataTransfer, +c)}function u(){R&&E.focus();ea=0;ca=R=!1}function s(a){x(a)}function A(a){var b=a.target||a.srcElement||null,c=null;"annotationRemoveButton"===b.className?(c=Q.getElementsByTagNameNS(b.parentNode,odf.Namespaces.officens,"annotation")[0],S.removeAnnotation(c),E.focus()):x(a)}function J(a){(a=a.data)&&da.insertText(a)}function G(a){return function(){a();return!0}}function D(a){return function(c){return K.getCursor(b).getSelectionType()===ops.OdtCursor.RangeSelection?a(c):!0}}function F(a){E.unsubscribe("keydown", +y.handleEvent);E.unsubscribe("keypress",aa.handleEvent);E.unsubscribe("keyup",N.handleEvent);E.unsubscribe("copy",q);E.unsubscribe("mousedown",t);E.unsubscribe("mousemove",V.trigger);E.unsubscribe("mouseup",A);E.unsubscribe("contextmenu",s);E.unsubscribe("dragstart",v);E.unsubscribe("dragend",u);E.unsubscribe("click",fa.handleClick);K.unsubscribe(ops.OdtDocument.signalOperationEnd,ga.trigger);K.unsubscribe(ops.Document.signalCursorAdded,$.registerCursor);K.unsubscribe(ops.Document.signalCursorRemoved, +$.removeCursor);K.unsubscribe(ops.OdtDocument.signalOperationEnd,f);a()}var O=runtime.getWindow(),K=h.getOdtDocument(),Z=new core.Async,Q=new core.DomUtils,W=new odf.OdfUtils,H=new gui.MimeDataExporter,T=new gui.Clipboard(H),y=new gui.KeyboardHandler,aa=new gui.KeyboardHandler,N=new gui.KeyboardHandler,R=!1,I=new odf.ObjectNameGenerator(K.getOdfCanvas().odfContainer(),b),ca=!1,ia=null,Y,M=null,E=new gui.EventManager(K),S=new gui.AnnotationController(h,b),U=new gui.DirectFormattingController(h,b,I, +d.directParagraphStylingEnabled),da=new gui.TextController(h,b,U.createCursorStyleOp,U.createParagraphStyleOps),ka=new gui.ImageController(h,b,I),P=new gui.ImageSelector(K.getOdfCanvas()),X=gui.SelectionMover.createPositionIterator(K.getRootNode()),V,ga,ha=new gui.PlainTextPasteboard(K,b),$=new gui.InputMethodEditor(b,E),ea=0,fa=new gui.HyperlinkClickHandler(K.getRootNode),ja=new gui.HyperlinkController(h,b),L=new gui.SelectionController(h,b),B=gui.KeyboardHandler.Modifier,C=gui.KeyboardHandler.KeyCode, +ba=-1!==O.navigator.appVersion.toLowerCase().indexOf("mac"),ma=-1!==["iPad","iPod","iPhone"].indexOf(O.navigator.platform),la;runtime.assert(null!==O,"Expected to be run in an environment which has a global window, like a browser.");this.undo=a;this.redo=m;this.insertLocalCursor=function(){runtime.assert(void 0===h.getOdtDocument().getCursor(b),"Inserting local cursor a second time.");var a=new ops.OpAddCursor;a.init({memberid:b});h.enqueue([a]);E.focus()};this.removeLocalCursor=function(){runtime.assert(void 0!== +h.getOdtDocument().getCursor(b),"Removing local cursor without inserting before.");var a=new ops.OpRemoveCursor;a.init({memberid:b});h.enqueue([a])};this.startEditing=function(){$.subscribe(gui.InputMethodEditor.signalCompositionStart,da.removeCurrentSelection);$.subscribe(gui.InputMethodEditor.signalCompositionEnd,J);E.subscribe("beforecut",g);E.subscribe("cut",n);E.subscribe("beforepaste",l);E.subscribe("paste",r);O.addEventListener("focus",fa.showTextCursor,!1);M&&M.initialize();$.setEditing(!0); +fa.setModifier(ba?gui.HyperlinkClickHandler.Modifier.Meta:gui.HyperlinkClickHandler.Modifier.Ctrl);y.bind(C.Backspace,B.None,G(da.removeTextByBackspaceKey),!0);y.bind(C.Delete,B.None,da.removeTextByDeleteKey);y.bind(C.Tab,B.None,D(function(){da.insertText("\t");return!0}));ba?(y.bind(C.Clear,B.None,da.removeCurrentSelection),y.bind(C.B,B.Meta,D(U.toggleBold)),y.bind(C.I,B.Meta,D(U.toggleItalic)),y.bind(C.U,B.Meta,D(U.toggleUnderline)),y.bind(C.L,B.MetaShift,D(U.alignParagraphLeft)),y.bind(C.E,B.MetaShift, +D(U.alignParagraphCenter)),y.bind(C.R,B.MetaShift,D(U.alignParagraphRight)),y.bind(C.J,B.MetaShift,D(U.alignParagraphJustified)),y.bind(C.C,B.MetaShift,S.addAnnotation),y.bind(C.Z,B.Meta,a),y.bind(C.Z,B.MetaShift,m),y.bind(C.LeftMeta,B.Meta,fa.showPointerCursor),y.bind(C.MetaInMozilla,B.Meta,fa.showPointerCursor),N.bind(C.LeftMeta,B.None,fa.showTextCursor),N.bind(C.MetaInMozilla,B.None,fa.showTextCursor)):(y.bind(C.B,B.Ctrl,D(U.toggleBold)),y.bind(C.I,B.Ctrl,D(U.toggleItalic)),y.bind(C.U,B.Ctrl,D(U.toggleUnderline)), +y.bind(C.L,B.CtrlShift,D(U.alignParagraphLeft)),y.bind(C.E,B.CtrlShift,D(U.alignParagraphCenter)),y.bind(C.R,B.CtrlShift,D(U.alignParagraphRight)),y.bind(C.J,B.CtrlShift,D(U.alignParagraphJustified)),y.bind(C.C,B.CtrlAlt,S.addAnnotation),y.bind(C.Z,B.Ctrl,a),y.bind(C.Z,B.CtrlShift,m),y.bind(C.Ctrl,B.Ctrl,fa.showPointerCursor),N.bind(C.Ctrl,B.None,fa.showTextCursor));aa.setDefault(D(function(a){var b;b=null===a.which||void 0===a.which?String.fromCharCode(a.keyCode):0!==a.which&&0!==a.charCode?String.fromCharCode(a.which): +null;return!b||a.altKey||a.ctrlKey||a.metaKey?!1:(da.insertText(b),!0)}));aa.bind(C.Enter,B.None,D(da.enqueueParagraphSplittingOps))};this.endEditing=function(){$.unsubscribe(gui.InputMethodEditor.signalCompositionStart,da.removeCurrentSelection);$.unsubscribe(gui.InputMethodEditor.signalCompositionEnd,J);E.unsubscribe("cut",n);E.unsubscribe("beforecut",g);E.unsubscribe("paste",r);E.unsubscribe("beforepaste",l);O.removeEventListener("focus",fa.showTextCursor,!1);$.setEditing(!1);fa.setModifier(gui.HyperlinkClickHandler.Modifier.None); +y.bind(C.Backspace,B.None,function(){return!0},!0);y.unbind(C.Delete,B.None);y.unbind(C.Tab,B.None);ba?(y.unbind(C.Clear,B.None),y.unbind(C.B,B.Meta),y.unbind(C.I,B.Meta),y.unbind(C.U,B.Meta),y.unbind(C.L,B.MetaShift),y.unbind(C.E,B.MetaShift),y.unbind(C.R,B.MetaShift),y.unbind(C.J,B.MetaShift),y.unbind(C.C,B.MetaShift),y.unbind(C.Z,B.Meta),y.unbind(C.Z,B.MetaShift),y.unbind(C.LeftMeta,B.Meta),y.unbind(C.MetaInMozilla,B.Meta),N.unbind(C.LeftMeta,B.None),N.unbind(C.MetaInMozilla,B.None)):(y.unbind(C.B, +B.Ctrl),y.unbind(C.I,B.Ctrl),y.unbind(C.U,B.Ctrl),y.unbind(C.L,B.CtrlShift),y.unbind(C.E,B.CtrlShift),y.unbind(C.R,B.CtrlShift),y.unbind(C.J,B.CtrlShift),y.unbind(C.C,B.CtrlAlt),y.unbind(C.Z,B.Ctrl),y.unbind(C.Z,B.CtrlShift),y.unbind(C.Ctrl,B.Ctrl),N.unbind(C.Ctrl,B.None));aa.setDefault(null);aa.unbind(C.Enter,B.None)};this.getInputMemberId=function(){return b};this.getSession=function(){return h};this.setUndoManager=function(a){M&&M.unsubscribe(gui.UndoManager.signalUndoStackChanged,c);if(M=a)M.setDocument(K), +M.setPlaybackFunction(h.enqueue),M.subscribe(gui.UndoManager.signalUndoStackChanged,c)};this.getUndoManager=function(){return M};this.getAnnotationController=function(){return S};this.getDirectFormattingController=function(){return U};this.getHyperlinkController=function(){return ja};this.getImageController=function(){return ka};this.getSelectionController=function(){return L};this.getTextController=function(){return da};this.getEventManager=function(){return E};this.getKeyboardHandlers=function(){return{keydown:y, +keypress:aa}};this.destroy=function(a){var b=[];la&&b.push(la.destroy);b=b.concat([V.destroy,ga.destroy,U.destroy,$.destroy,E.destroy,F]);runtime.clearTimeout(Y);Z.destroyAll(b,a)};V=new core.ScheduledTask(e,0);ga=new core.ScheduledTask(function(){var a=K.getCursor(b);if(a&&a.getSelectionType()===ops.OdtCursor.RegionSelection&&(a=W.getImageElements(a.getSelectedRange())[0])){P.select(a.parentNode);return}P.clearSelection()},0);y.bind(C.Left,B.None,D(L.moveCursorToLeft));y.bind(C.Right,B.None,D(L.moveCursorToRight)); +y.bind(C.Up,B.None,D(L.moveCursorUp));y.bind(C.Down,B.None,D(L.moveCursorDown));y.bind(C.Left,B.Shift,D(L.extendSelectionToLeft));y.bind(C.Right,B.Shift,D(L.extendSelectionToRight));y.bind(C.Up,B.Shift,D(L.extendSelectionUp));y.bind(C.Down,B.Shift,D(L.extendSelectionDown));y.bind(C.Home,B.None,D(L.moveCursorToLineStart));y.bind(C.End,B.None,D(L.moveCursorToLineEnd));y.bind(C.Home,B.Ctrl,D(L.moveCursorToDocumentStart));y.bind(C.End,B.Ctrl,D(L.moveCursorToDocumentEnd));y.bind(C.Home,B.Shift,D(L.extendSelectionToLineStart)); +y.bind(C.End,B.Shift,D(L.extendSelectionToLineEnd));y.bind(C.Up,B.CtrlShift,D(L.extendSelectionToParagraphStart));y.bind(C.Down,B.CtrlShift,D(L.extendSelectionToParagraphEnd));y.bind(C.Home,B.CtrlShift,D(L.extendSelectionToDocumentStart));y.bind(C.End,B.CtrlShift,D(L.extendSelectionToDocumentEnd));ba?(y.bind(C.Left,B.Alt,D(L.moveCursorBeforeWord)),y.bind(C.Right,B.Alt,D(L.moveCursorPastWord)),y.bind(C.Left,B.Meta,D(L.moveCursorToLineStart)),y.bind(C.Right,B.Meta,D(L.moveCursorToLineEnd)),y.bind(C.Home, +B.Meta,D(L.moveCursorToDocumentStart)),y.bind(C.End,B.Meta,D(L.moveCursorToDocumentEnd)),y.bind(C.Left,B.AltShift,D(L.extendSelectionBeforeWord)),y.bind(C.Right,B.AltShift,D(L.extendSelectionPastWord)),y.bind(C.Left,B.MetaShift,D(L.extendSelectionToLineStart)),y.bind(C.Right,B.MetaShift,D(L.extendSelectionToLineEnd)),y.bind(C.Up,B.AltShift,D(L.extendSelectionToParagraphStart)),y.bind(C.Down,B.AltShift,D(L.extendSelectionToParagraphEnd)),y.bind(C.Up,B.MetaShift,D(L.extendSelectionToDocumentStart)), +y.bind(C.Down,B.MetaShift,D(L.extendSelectionToDocumentEnd)),y.bind(C.A,B.Meta,D(L.extendSelectionToEntireDocument))):(y.bind(C.Left,B.Ctrl,D(L.moveCursorBeforeWord)),y.bind(C.Right,B.Ctrl,D(L.moveCursorPastWord)),y.bind(C.Left,B.CtrlShift,D(L.extendSelectionBeforeWord)),y.bind(C.Right,B.CtrlShift,D(L.extendSelectionPastWord)),y.bind(C.A,B.Ctrl,D(L.extendSelectionToEntireDocument)));ma&&(la=new gui.IOSSafariSupport(E));E.subscribe("keydown",y.handleEvent);E.subscribe("keypress",aa.handleEvent);E.subscribe("keyup", +N.handleEvent);E.subscribe("copy",q);E.subscribe("mousedown",t);E.subscribe("mousemove",V.trigger);E.subscribe("mouseup",A);E.subscribe("contextmenu",s);E.subscribe("dragstart",v);E.subscribe("dragend",u);E.subscribe("click",fa.handleClick);K.subscribe(ops.OdtDocument.signalOperationEnd,ga.trigger);K.subscribe(ops.Document.signalCursorAdded,$.registerCursor);K.subscribe(ops.Document.signalCursorRemoved,$.removeCursor);K.subscribe(ops.OdtDocument.signalOperationEnd,f)};return gui.SessionController})(); +// Input 96 /* - 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 @@ -2733,17 +2981,17 @@ l[f].rerender()})};this.registerCursor=function(f,g){var r=f.getMemberId(),n=new @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(){t.emit(gui.UndoManager.signalUndoStackChanged,{undoAvailable:h.hasUndoStates(),redoAvailable:h.hasRedoStates()})}function f(){b!==c&&b!==q[q.length-1]&&q.push(b)}function p(a){var b=a.previousSibling||a.nextSibling;a.parentNode.removeChild(a);d.normalizeTextNodes(b)}function r(a){return Object.keys(a).map(function(b){return a[b]})}function n(b){function c(a){var b=a.spec();if(f[b.memberid])switch(b.optype){case "AddCursor":d[b.memberid]||(d[b.memberid]= -a,delete f[b.memberid],g-=1);break;case "MoveCursor":e[b.memberid]||(e[b.memberid]=a)}}var d={},e={},f={},g,h=b.pop();a.getCursors().forEach(function(a){f[a.getMemberId()]=!0});for(g=Object.keys(f).length;h&&0 + Copyright (C) 2012 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2778,40 +3026,55 @@ f.refreshCSS(),b=q[q.length-1]||c,l());return h}};gui.TrivialUndoManager.signalD @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(g){var l=new ops.OperationFactory,f=new ops.OdtDocument(g),p=null;this.setOperationFactory=function(f){l=f;p&&p.setOperationFactory(l)};this.setOperationRouter=function(g){p=g;g.setPlaybackFunction(function(g){return g.execute(f)?(f.emit(ops.OdtDocument.signalOperationExecuted,g),!0):!1});g.setOperationFactory(l)};this.getOperationFactory=function(){return l};this.getOdtDocument=function(){return f};this.enqueue=function(f){p.push(f)};this.close=function(g){p.close(function(l){l? -g(l):f.close(g)})};this.destroy=function(g){f.destroy(g)};this.setOperationRouter(new ops.TrivialOperationRouter)}; -// Input 92 +ops.EditInfo=function(k,h){function b(){var b=[],g;for(g in d)d.hasOwnProperty(g)&&b.push({memberid:g,time:d[g].time});b.sort(function(b,d){return b.time-d.time});return b}var p,d={};this.getNode=function(){return p};this.getOdtDocument=function(){return h};this.getEdits=function(){return d};this.getSortedEdits=function(){return b()};this.addEdit=function(b,g){d[b]={time:g}};this.clearEdits=function(){d={}};this.destroy=function(b){k.parentNode&&k.removeChild(p);b()};p=h.getDOMDocument().createElementNS("urn:webodf:names:editinfo", +"editinfo");k.insertBefore(p,k.firstChild)}; +// Input 99 /* - 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("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,l){function f(){var e=h.getCursor(l),e=e&&e.getNode(),a=!1;if(e){a:{for(a=h.getRootNode();e&&e!==a;){if(e.namespaceURI===c&&"annotation"===e.localName){e=!0;break a}e=e.parentNode}e=!1}a=!e}a!==d&&(d=a,m.emit(gui.AnnotationController.annotatableChanged,d))}function p(c){c.getMemberId()===l&&f()}function r(c){c===l&&f()}function n(c){c.getMemberId()===l&&f()}var h=g.getOdtDocument(),d=!1,m=new core.EventNotifier([gui.AnnotationController.annotatableChanged]),c=odf.Namespaces.officens; -this.isAnnotatable=function(){return d};this.addAnnotation=function(){var c=new ops.OpAddAnnotation,a=h.getCursorSelection(l),b=a.length,a=a.position;d&&(a=0<=b?a:a+b,b=Math.abs(b),c.init({memberid:l,position:a,length:b,name:l+Date.now()}),g.enqueue([c]))};this.removeAnnotation=function(c){var a,b;a=h.convertDomPointToCursorStep(c,0)+1;b=h.convertDomPointToCursorStep(c,c.childNodes.length);c=new ops.OpRemoveAnnotation;c.init({memberid:l,position:a,length:b-a});b=new ops.OpMoveCursor;b.init({memberid:l, -position:0a?(q=b(1,0),r=b(0.5,1E4-a),l=b(0.2,2E4-a)):1E4<=a&&2E4>a?(q=b(0.5,0),l=b(0.2,2E4-a)):q=b(0.2,0)};this.getEdits=function(){return k.getEdits()};this.clearEdits= +function(){k.clearEdits();n.setEdits([]);g.hasAttributeNS("urn:webodf:names:editinfo","editinfo:memberid")&&g.removeAttributeNS("urn:webodf:names:editinfo","editinfo:memberid")};this.getEditInfo=function(){return k};this.show=function(){g.style.display="block"};this.hide=function(){p.hideHandle();g.style.display="none"};this.showHandle=function(){n.show()};this.hideHandle=function(){n.hide()};this.destroy=function(b){runtime.clearTimeout(q);runtime.clearTimeout(r);runtime.clearTimeout(l);d.removeChild(g); +n.destroy(function(c){c?b(c):k.destroy(b)})};(function(){var b=k.getOdtDocument().getDOMDocument();g=b.createElementNS(b.documentElement.namespaceURI,"div");g.setAttribute("class","editInfoMarker");g.onmouseover=function(){p.showHandle()};g.onmouseout=function(){p.hideHandle()};d=k.getNode();d.appendChild(g);n=new gui.EditInfoHandle(d);h||p.hide()})()}; +// Input 100 +gui.ShadowCursor=function(k){var h=k.getDOMDocument().createRange(),b=!0;this.removeFromDocument=function(){};this.getMemberId=function(){return gui.ShadowCursor.ShadowCursorMemberId};this.getSelectedRange=function(){return h};this.setSelectedRange=function(k,d){h=k;b=!1!==d};this.hasForwardSelection=function(){return b};this.getDocument=function(){return k};this.getSelectionType=function(){return ops.OdtCursor.RangeSelection};h.setStart(k.getRootNode(),0)};gui.ShadowCursor.ShadowCursorMemberId=""; +(function(){return gui.ShadowCursor})(); +// Input 101 +gui.SelectionView=function(k){};gui.SelectionView.prototype.rerender=function(){};gui.SelectionView.prototype.show=function(){};gui.SelectionView.prototype.hide=function(){};gui.SelectionView.prototype.destroy=function(k){}; +// Input 102 /* Copyright (C) 2013 KO GmbH @@ -2849,17 +3112,12 @@ gui.AnnotationController.annotatableChanged="annotatable/changed";(function(){re @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,f){function p(){function a(b,d,e){b!==d&&(void 0===c&&(c={}),c[e]=d);return d}var b=k.getCursor(l),b=b&&b.getSelectedRange(),c;v=a(v,b?w.isAlignedLeft(b):!1,"isAlignedLeft");u=a(u,b?w.isAlignedCenter(b):!1,"isAlignedCenter");s=a(s,b?w.isAlignedRight(b):!1,"isAlignedRight");H=a(H,b?w.isAlignedJustified(b):!1,"isAlignedJustified");c&&x.emit(gui.DirectParagraphStyler.paragraphStylingChanged,c)}function r(a){a.getMemberId()===l&&p()}function n(a){a===l&&p()}function h(a){a.getMemberId()=== -l&&p()}function d(){p()}function m(a){var b=k.getCursor(l);b&&k.getParagraphElement(b.getNode())===a.paragraphElement&&p()}function c(a){return a===ops.StepsTranslator.NEXT_STEP}function e(a){var b=k.getCursor(l).getSelectedRange(),b=A.getParagraphElements(b),d=k.getFormatting();b.forEach(function(b){var e=k.convertDomPointToCursorStep(b,0,c),h=b.getAttributeNS(odf.Namespaces.textns,"style-name");b=f.generateStyleName();var m;h&&(m=d.createDerivedStyleObject(h,"paragraph",{}));m=a(m||{});h=new ops.OpAddStyle; -h.init({memberid:l,styleName:b,styleFamily:"paragraph",isAutomaticStyle:!0,setProperties:m});m=new ops.OpSetParagraphStyle;m.init({memberid:l,styleName:b,position:e});g.enqueue([h,m])})}function a(a){e(function(b){return t.mergeObjects(b,a)})}function b(b){a({"style:paragraph-properties":{"fo:text-align":b}})}function q(a,b){var c=k.getFormatting().getDefaultTabStopDistance(),d=b["style:paragraph-properties"],d=(d=d&&d["fo:margin-left"])&&A.parseLength(d);return t.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 k=g.getOdtDocument(),t=new core.Utils,A=new odf.OdfUtils,w=new gui.StyleHelper(k.getFormatting()),x=new core.EventNotifier([gui.DirectParagraphStyler.paragraphStylingChanged]),v,u,s,H;this.isAlignedLeft=function(){return v};this.isAlignedCenter=function(){return u};this.isAlignedRight=function(){return s};this.isAlignedJustified=function(){return H};this.alignParagraphLeft=function(){b("left");return!0};this.alignParagraphCenter=function(){b("center"); -return!0};this.alignParagraphRight=function(){b("right");return!0};this.alignParagraphJustified=function(){b("justify");return!0};this.indent=function(){e(q.bind(null,1));return!0};this.outdent=function(){e(q.bind(null,-1));return!0};this.subscribe=function(a,b){x.subscribe(a,b)};this.unsubscribe=function(a,b){x.unsubscribe(a,b)};this.destroy=function(a){k.unsubscribe(ops.OdtDocument.signalCursorAdded,r);k.unsubscribe(ops.OdtDocument.signalCursorRemoved,n);k.unsubscribe(ops.OdtDocument.signalCursorMoved, -h);k.unsubscribe(ops.OdtDocument.signalParagraphStyleModified,d);k.unsubscribe(ops.OdtDocument.signalParagraphChanged,m);a()};k.subscribe(ops.OdtDocument.signalCursorAdded,r);k.subscribe(ops.OdtDocument.signalCursorRemoved,n);k.subscribe(ops.OdtDocument.signalCursorMoved,h);k.subscribe(ops.OdtDocument.signalParagraphStyleModified,d);k.subscribe(ops.OdtDocument.signalParagraphChanged,m);p()};gui.DirectParagraphStyler.paragraphStylingChanged="paragraphStyling/changed";(function(){return gui.DirectParagraphStyler})(); -// Input 94 +gui.SelectionViewManager=function(k){function h(){return Object.keys(b).map(function(h){return b[h]})}var b={};this.getSelectionView=function(h){return b.hasOwnProperty(h)?b[h]:null};this.getSelectionViews=h;this.removeSelectionView=function(h){b.hasOwnProperty(h)&&(b[h].destroy(function(){}),delete b[h])};this.hideSelectionView=function(h){b.hasOwnProperty(h)&&b[h].hide()};this.showSelectionView=function(h){b.hasOwnProperty(h)&&b[h].show()};this.rerenderSelectionViews=function(){Object.keys(b).forEach(function(h){b[h].rerender()})}; +this.registerCursor=function(h,d){var n=h.getMemberId(),g=new k(h);d?g.show():g.hide();return b[n]=g};this.destroy=function(b){function d(g,h){h?b(h):g + Copyright (C) 2012-2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -2894,23 +3152,27 @@ h);k.unsubscribe(ops.OdtDocument.signalParagraphStyleModified,d);k.unsubscribe(o @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 f(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 p(a,b){var c=f(a[0],b);return a.every(function(a){return c===f(a,b)})?c:void 0}function r(){var a=u.getCursor(l),a=(a=a&&a.getSelectedRange())&&s.getAppliedStyles(a)||[];a[0]&&y&&(a[0]=v.mergeObjects(a[0],y));return a}function n(){function a(b,d,e){b!==d&&(void 0===c&&(c={}),c[e]=d);return d}var b,c;B=r();L=a(L,B?s.isBold(B):!1,"isBold");I=a(I,B?s.isItalic(B):!1,"isItalic"); -W=a(W,B?s.hasUnderline(B):!1,"hasUnderline");Q=a(Q,B?s.hasStrikeThrough(B):!1,"hasStrikeThrough");b=B&&p(B,["style:text-properties","fo:font-size"]);z=a(z,b&&parseFloat(b),"fontSize");ja=a(ja,B&&p(B,["style:text-properties","style:font-name"]),"fontName");c&&H.emit(gui.DirectTextStyler.textStylingChanged,c)}function h(a){a.getMemberId()===l&&n()}function d(a){a===l&&n()}function m(a){a.getMemberId()===l&&n()}function c(){n()}function e(a){var b=u.getCursor(l);b&&u.getParagraphElement(b.getNode())=== -a.paragraphElement&&n()}function a(a,b){var c=u.getCursor(l);if(!c)return!1;c=s.getAppliedStyles(c.getSelectedRange());b(!a(c));return!0}function b(a){var b=u.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])):(y=v.mergeObjects(y||{},c),n())}function q(a,c){var d={};d[a]=c;b(d)}function k(a){a=a.spec();y&&a.memberid===l&&"SplitParagraph"!==a.optype&&(y=null,n())}function t(a){q("fo:font-weight", -a?"bold":"normal")}function A(a){q("fo:font-style",a?"italic":"normal")}function w(a){q("style:text-underline-style",a?"solid":"none")}function x(a){q("style:text-line-through-style",a?"solid":"none")}var v=new core.Utils,u=g.getOdtDocument(),s=new gui.StyleHelper(u.getFormatting()),H=new core.EventNotifier([gui.DirectTextStyler.textStylingChanged]),y,B=[],L=!1,I=!1,W=!1,Q=!1,z,ja;this.formatTextSelection=b;this.createCursorStyleOp=function(a,b){var c=null;y&&(c=new ops.OpApplyDirectStyling,c.init({memberid:l, -position:a,length:b,setProperties:y}),y=null,n());return c};this.setBold=t;this.setItalic=A;this.setHasUnderline=w;this.setHasStrikethrough=x;this.setFontSize=function(a){q("fo:font-size",a+"pt")};this.setFontName=function(a){q("style:font-name",a)};this.getAppliedStyles=function(){return B};this.toggleBold=a.bind(this,s.isBold,t);this.toggleItalic=a.bind(this,s.isItalic,A);this.toggleUnderline=a.bind(this,s.hasUnderline,w);this.toggleStrikethrough=a.bind(this,s.hasStrikeThrough,x);this.isBold=function(){return L}; -this.isItalic=function(){return I};this.hasUnderline=function(){return W};this.hasStrikeThrough=function(){return Q};this.fontSize=function(){return z};this.fontName=function(){return ja};this.subscribe=function(a,b){H.subscribe(a,b)};this.unsubscribe=function(a,b){H.unsubscribe(a,b)};this.destroy=function(a){u.unsubscribe(ops.OdtDocument.signalCursorAdded,h);u.unsubscribe(ops.OdtDocument.signalCursorRemoved,d);u.unsubscribe(ops.OdtDocument.signalCursorMoved,m);u.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, -c);u.unsubscribe(ops.OdtDocument.signalParagraphChanged,e);u.unsubscribe(ops.OdtDocument.signalOperationExecuted,k);a()};u.subscribe(ops.OdtDocument.signalCursorAdded,h);u.subscribe(ops.OdtDocument.signalCursorRemoved,d);u.subscribe(ops.OdtDocument.signalCursorMoved,m);u.subscribe(ops.OdtDocument.signalParagraphStyleModified,c);u.subscribe(ops.OdtDocument.signalParagraphChanged,e);u.subscribe(ops.OdtDocument.signalOperationExecuted,k);n()};gui.DirectTextStyler.textStylingChanged="textStyling/changed"; -(function(){return gui.DirectTextStyler})(); -// Input 95 -runtime.loadClass("odf.Namespaces");runtime.loadClass("odf.ObjectNameGenerator"); -gui.ImageManager=function(g,l,f){var p={"image/gif":".gif","image/jpeg":".jpg","image/png":".png"},r=odf.Namespaces.textns,n=g.getOdtDocument(),h=n.getFormatting(),d={};this.insertImage=function(m,c,e,a){var b;runtime.assert(0b.width&&(q=b.width/e);a>b.height&&(k=b.height/a);q=Math.min(q,k);b=e*q;e=a*q;k=n.getOdfCanvas().odfContainer().rootElement.styles;a=m.toLowerCase();var q=p.hasOwnProperty(a)?p[a]:null,t;a=[];runtime.assert(null!==q,"Image type is not supported: "+m);q="Pictures/"+f.generateImageName()+q;t=new ops.OpSetBlob;t.init({memberid:l,filename:q,mimetype:m,content:c});a.push(t);h.getStyleElement("Graphics","graphic",[k])||(m=new ops.OpAddStyle,m.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(m));m=f.generateStyleName();c=new ops.OpAddStyle;c.init({memberid:l,styleName:m,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(c);t=new ops.OpInsertImage;t.init({memberid:l,position:n.getCursorPosition(l),filename:q,frameWidth:b+"cm",frameHeight:e+"cm",frameStyleName:m,frameName:f.generateFrameName()});a.push(t);g.enqueue(a)}}; -// Input 96 +gui.SessionViewOptions=function(){this.caretBlinksOnRangeSelect=this.caretAvatarsInitiallyVisible=this.editInfoMarkersInitiallyVisible=!0}; +(function(){gui.SessionView=function(k,h,b,p,d){function n(a,b,c){function d(b,c,e){c=b+'[editinfo|memberid="'+a+'"]'+e+c;a:{var f=m.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:m.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(".selectionOverlay","{ fill: "+c+"; stroke: "+c+";}","")}function g(a){var b,c;for(c in t)t.hasOwnProperty(c)&&(b=t[c],a?b.show():b.hide())}function q(a){p.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);h===b&&n("","",a.color)}function l(a){var c=a.getMemberId(),e=b.getOdtDocument().getMember(c).getProperties();p.registerCursor(a,z,x);d.registerCursor(a, +!0);if(a=p.getCaret(c))a.setAvatarImageUrl(e.imageUrl),a.setColor(e.color);runtime.log("+++ View here +++ eagerly created an Caret for '"+c+"'! +++")}function f(a){a=a.getMemberId();var b=d.getSelectionView(h),c=d.getSelectionView(gui.ShadowCursor.ShadowCursorMemberId),e=p.getCaret(h);a===h?(c.hide(),b&&b.show(),e&&e.show()):a===gui.ShadowCursor.ShadowCursorMemberId&&(c.show(),b&&b.hide(),e&&e.hide())}function c(a){d.removeSelectionView(a)}function a(a){var c=a.paragraphElement,d=a.memberId;a=a.timeStamp; +var f,g="",h=c.getElementsByTagNameNS(e,"editinfo").item(0);h?(g=h.getAttributeNS(e,"id"),f=t[g]):(g=Math.random().toString(),f=new ops.EditInfo(c,b.getOdtDocument()),f=new gui.EditInfoMarker(f,w),h=c.getElementsByTagNameNS(e,"editinfo").item(0),h.setAttributeNS(e,"id",g),t[g]=f);f.addEdit(d,new Date(a))}var m,e="urn:webodf:names:editinfo",t={},w=void 0!==k.editInfoMarkersInitiallyVisible?Boolean(k.editInfoMarkersInitiallyVisible):!0,z=void 0!==k.caretAvatarsInitiallyVisible?Boolean(k.caretAvatarsInitiallyVisible): +!0,x=void 0!==k.caretBlinksOnRangeSelect?Boolean(k.caretBlinksOnRangeSelect):!0;this.showEditInfoMarkers=function(){w||(w=!0,g(w))};this.hideEditInfoMarkers=function(){w&&(w=!1,g(w))};this.showCaretAvatars=function(){z||(z=!0,q(z))};this.hideCaretAvatars=function(){z&&(z=!1,q(z))};this.getSession=function(){return b};this.getCaret=function(a){return p.getCaret(a)};this.destroy=function(e){var g=b.getOdtDocument(),h=Object.keys(t).map(function(a){return t[a]});g.unsubscribe(ops.Document.signalMemberAdded, +r);g.unsubscribe(ops.Document.signalMemberUpdated,r);g.unsubscribe(ops.Document.signalCursorAdded,l);g.unsubscribe(ops.Document.signalCursorRemoved,c);g.unsubscribe(ops.OdtDocument.signalParagraphChanged,a);g.unsubscribe(ops.Document.signalCursorMoved,f);g.unsubscribe(ops.OdtDocument.signalParagraphChanged,d.rerenderSelectionViews);g.unsubscribe(ops.OdtDocument.signalTableAdded,d.rerenderSelectionViews);g.unsubscribe(ops.OdtDocument.signalParagraphStyleModified,d.rerenderSelectionViews);m.parentNode.removeChild(m); +(function J(a,b){b?e(b):a @@ -2948,45 +3210,13 @@ a.push(c);t=new ops.OpInsertImage;t.init({memberid:l,position:n.getCursorPositio @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("core.PositionFilter"); -gui.TextManipulator=function(g,l,f){function p(d){var c=new ops.OpRemoveText;c.init({memberid:l,position:d.position,length:d.length});return c}function r(d){0>d.length&&(d.position+=d.length,d.length=-d.length);return d}function n(f,c){var e=new core.PositionFilterChain,a=gui.SelectionMover.createPositionIterator(h.getRootElement(f)),b=c?a.nextPosition:a.previousPosition;e.addFilter("BaseFilter",h.getPositionFilter());e.addFilter("RootFilter",h.createRootFilter(l));for(a.setUnfilteredPosition(f,0);b();)if(e.acceptPosition(a)=== -d)return!0;return!1}var h=g.getOdtDocument(),d=core.PositionFilter.FilterResult.FILTER_ACCEPT;this.enqueueParagraphSplittingOps=function(){var d=r(h.getCursorSelection(l)),c,e=[];0 + Copyright (C) 2013 KO GmbH @licstart The JavaScript code in this page is free software: you can redistribute it @@ -3021,15 +3251,88 @@ ua&&N.bind(c.C,b.CtrlAlt,ua.addAnnotation),N.bind(c.Z,b.Ctrl,la),N.bind(c.Z,b.Ct @source: http://www.webodf.org/ @source: https://github.com/kogmbh/WebODF/ */ -runtime.loadClass("gui.Caret"); -gui.CaretManager=function(g){function l(a){return b.hasOwnProperty(a)?b[a]:null}function f(){return Object.keys(b).map(function(a){return b[a]})}function p(a){a===g.getInputMemberId()&&g.getSession().getOdtDocument().getOdfCanvas().getElement().removeAttribute("tabindex");delete b[a]}function r(a){a=a.getMemberId();a===g.getInputMemberId()&&(a=l(a))&&a.refreshCursorBlinking()}function n(){var a=l(g.getInputMemberId());k=!1;a&&a.ensureVisible()}function h(){var a=l(g.getInputMemberId());a&&(a.handleUpdate(), -k||(k=!0,runtime.setTimeout(n,50)))}function d(a){a.memberId===g.getInputMemberId()&&h()}function m(){var a=l(g.getInputMemberId());a&&a.setFocus()}function c(){var a=l(g.getInputMemberId());a&&a.removeFocus()}function e(){var a=l(g.getInputMemberId());a&&a.show()}function a(){var a=l(g.getInputMemberId());a&&a.hide()}var b={},q=runtime.getWindow(),k=!1;this.registerCursor=function(a,c,d){var e=a.getMemberId();c=new gui.Caret(a,c,d);b[e]=c;e===g.getInputMemberId()?(runtime.log("Starting to track input on new cursor of "+ -e),a.handleUpdate=h,g.getSession().getOdtDocument().getOdfCanvas().getElement().setAttribute("tabindex",-1),g.getEventManager().focus()):a.handleUpdate=c.handleUpdate;return c};this.getCaret=l;this.getCarets=f;this.destroy=function(h){var k=g.getSession().getOdtDocument(),l=g.getEventManager(),n=f();k.unsubscribe(ops.OdtDocument.signalParagraphChanged,d);k.unsubscribe(ops.OdtDocument.signalCursorMoved,r);k.unsubscribe(ops.OdtDocument.signalCursorRemoved,p);l.unsubscribe("focus",m);l.unsubscribe("blur", -c);q.removeEventListener("focus",e,!1);q.removeEventListener("blur",a,!1);(function u(a,b){b?h(b):a + 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.OperationTransformMatrix=function(){function k(b){b.position+=b.length;b.length*=-1}function h(b){var a=0>b.length;a&&k(b);return a}function b(b,a){function d(f){b[f]===a&&e.push(f)}var e=[];b&&["style:parent-style-name","style:next-style-name"].forEach(d);return e}function p(b,a){function d(e){b[e]===a&&delete b[e]}b&&["style:parent-style-name","style:next-style-name"].forEach(d)}function d(b){var a={};Object.keys(b).forEach(function(f){a[f]="object"===typeof b[f]?d(b[f]):b[f]});return a}function n(b, +a,d,e){var f,g=!1,h=!1,k,l=[];e&&e.attributes&&(l=e.attributes.split(","));b&&(d||0=a.position+a.length)){e=f?b:a;h=f?a:b;if(b.position!==a.position||b.length!==a.length)p=d(e),q=d(h);a=r(h.setProperties,null,e.setProperties,null,"style:text-properties");if(a.majorChanged||a.minorChanged)k=[],b=[],l=e.position+e.length,n=h.position+h.length,h.positionl?a.minorChanged&&(p=q,p.position=l,p.length=n-l,b.push(p),h.length=l-h.position):l>n&&a.majorChanged&&(p.position=n,p.length=l-n,k.push(p),e.length=n-e.position),e.setProperties&&g(e.setProperties)&&k.push(e),h.setProperties&&g(h.setProperties)&&b.push(h),f?(l=k,k=b):l=b}return{opSpecsA:l,opSpecsB:k}},InsertText:function(b,a){a.position<=b.position?b.position+=a.text.length:a.position<=b.position+b.length&&(b.length+=a.text.length);return{opSpecsA:[b],opSpecsB:[a]}}, +MoveCursor:l,RemoveCursor:l,RemoveStyle:l,RemoveText:function(b,a){var d=b.position+b.length,e=a.position+a.length,f=[b],g=[a];e<=b.position?b.position-=a.length:a.positiona.position?b.position+=a.text.length:d?a.position+=b.text.length:b.position+=a.text.length;return{opSpecsA:[b],opSpecsB:[a]}},MoveCursor:function(b,a){var d=h(a);b.positiona.position)b.position+=1;else return d?a.position+=b.text.length: +b.position+=1,null;return{opSpecsA:[b],opSpecsB:[a]}},UpdateMember:l,UpdateMetadata:l,UpdateParagraphStyle:l},MoveCursor:{MoveCursor:l,RemoveCursor:function(b,a){return{opSpecsA:b.memberid===a.memberid?[]:[b],opSpecsB:[a]}},RemoveMember:l,RemoveStyle:l,RemoveText:function(b,a){var d=h(b),e=b.position+b.length,f=a.position+a.length;f<=b.position?b.position-=a.length:a.positiona.position?b.position+=1:b.position===a.position&&(d?a.position+=1:b.position+=1);return{opSpecsA:[b],opSpecsB:[a]}},UpdateMember:l,UpdateMetadata:l,UpdateParagraphStyle:l},UpdateMember:{UpdateMetadata:l,UpdateParagraphStyle:l},UpdateMetadata:{UpdateMetadata:function(b,a,d){var e,f=[b],h=[a];e=d?b:a;b=d?a:b;n(b.setProperties||null,b.removedProperties||null,e.setProperties||null,e.removedProperties||null);e.setProperties&&g(e.setProperties)||e.removedProperties&& +q(e.removedProperties)||(d?f=[]:h=[]);b.setProperties&&g(b.setProperties)||b.removedProperties&&q(b.removedProperties)||(d?h=[]:f=[]);return{opSpecsA:f,opSpecsB:h}},UpdateParagraphStyle:l},UpdateParagraphStyle:{UpdateParagraphStyle:function(b,a,d){var e,f=[b],h=[a];b.styleName===a.styleName&&(e=d?b:a,b=d?a:b,r(b.setProperties,b.removedProperties,e.setProperties,e.removedProperties,"style:paragraph-properties"),r(b.setProperties,b.removedProperties,e.setProperties,e.removedProperties,"style:text-properties"), +n(b.setProperties||null,b.removedProperties||null,e.setProperties||null,e.removedProperties||null),e.setProperties&&g(e.setProperties)||e.removedProperties&&q(e.removedProperties)||(d?f=[]:h=[]),b.setProperties&&g(b.setProperties)||b.removedProperties&&q(b.removedProperties)||(d?h=[]:f=[]));return{opSpecsA:f,opSpecsB:h}}}};this.passUnchanged=l;this.extendTransformations=function(b){Object.keys(b).forEach(function(a){var d=b[a],e,g=f.hasOwnProperty(a);runtime.log((g?"Extending":"Adding")+" map for optypeA: "+ +a);g||(f[a]={});e=f[a];Object.keys(d).forEach(function(b){var c=e.hasOwnProperty(b);runtime.assert(a<=b,"Wrong order:"+a+", "+b);runtime.log(" "+(c?"Overwriting":"Adding")+" entry for optypeB: "+b);e[b]=d[b]})})};this.transformOpspecVsOpspec=function(b,a){var d=b.optype<=a.optype,e;runtime.log("Crosstransforming:");runtime.log(runtime.toJson(b));runtime.log(runtime.toJson(a));d||(e=b,b=a,a=e);(e=(e=f[b.optype])&&e[a.optype])?(e=e(b,a,!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 108 +/* + + 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.OperationTransformer=function(){function k(d){var h=[];d.forEach(function(d){h.push(b.create(d))});return h}function h(b,k){for(var g,q,r=[],l=[];0 @licstart The JavaScript code in this page is free software: you can redistribute it @@ -3064,15 +3367,6 @@ c);q.removeEventListener("focus",e,!1);q.removeEventListener("blur",a,!1);(funct @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,f,p,r){function n(a,b,c){function d(b,c,e){c=b+'[editinfo|memberid="'+a+'"]'+e+c;a:{var f=t.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:t.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 h(a){var b,c;for(c in w)w.hasOwnProperty(c)&&(b=w[c],a?b.show():b.hide())}function d(a){p.getCarets().forEach(function(b){a?b.showHandle():b.hideHandle()})}function m(a){var b=a.getMemberId();a=a.getProperties();n(b,a.fullName,a.color);l===b&&n("","",a.color)}function c(a){var b=a.getMemberId(),c=f.getOdtDocument().getMember(b).getProperties();p.registerCursor(a,v,u);r.registerCursor(a, -!0);if(a=p.getCaret(b))a.setAvatarImageUrl(c.imageUrl),a.setColor(c.color);runtime.log("+++ View here +++ eagerly created an Caret for '"+b+"'! +++")}function e(a){a=a.getMemberId();var b=r.getSelectionView(l),c=r.getSelectionView(gui.ShadowCursor.ShadowCursorMemberId),d=p.getCaret(l);a===l?(c.hide(),b&&b.show(),d&&d.show()):a===gui.ShadowCursor.ShadowCursorMemberId&&(c.show(),b&&b.hide(),d&&d.hide())}function a(a){r.removeSelectionView(a)}function b(a){var b=a.paragraphElement,c=a.memberId;a=a.timeStamp; -var d,e="",g=b.getElementsByTagNameNS(A,"editinfo")[0];g?(e=g.getAttributeNS(A,"id"),d=w[e]):(e=Math.random().toString(),d=new ops.EditInfo(b,f.getOdtDocument()),d=new gui.EditInfoMarker(d,x),g=b.getElementsByTagNameNS(A,"editinfo")[0],g.setAttributeNS(A,"id",e),w[e]=d);d.addEdit(c,new Date(a))}function q(){H=!0}function k(){s=runtime.getWindow().setInterval(function(){H&&(r.rerenderSelectionViews(),H=!1)},200)}var t,A="urn:webodf:names:editinfo",w={},x=void 0!==g.editInfoMarkersInitiallyVisible? -Boolean(g.editInfoMarkersInitiallyVisible):!0,v=void 0!==g.caretAvatarsInitiallyVisible?Boolean(g.caretAvatarsInitiallyVisible):!0,u=void 0!==g.caretBlinksOnRangeSelect?Boolean(g.caretBlinksOnRangeSelect):!0,s,H=!1;this.showEditInfoMarkers=function(){x||(x=!0,h(x))};this.hideEditInfoMarkers=function(){x&&(x=!1,h(x))};this.showCaretAvatars=function(){v||(v=!0,d(v))};this.hideCaretAvatars=function(){v&&(v=!1,d(v))};this.getSession=function(){return f};this.getCaret=function(a){return p.getCaret(a)}; -this.destroy=function(d){var g=f.getOdtDocument(),h=Object.keys(w).map(function(a){return w[a]});g.unsubscribe(ops.OdtDocument.signalMemberAdded,m);g.unsubscribe(ops.OdtDocument.signalMemberUpdated,m);g.unsubscribe(ops.OdtDocument.signalCursorAdded,c);g.unsubscribe(ops.OdtDocument.signalCursorRemoved,a);g.unsubscribe(ops.OdtDocument.signalParagraphChanged,b);g.unsubscribe(ops.OdtDocument.signalCursorMoved,e);g.unsubscribe(ops.OdtDocument.signalParagraphChanged,q);g.unsubscribe(ops.OdtDocument.signalTableAdded, -q);g.unsubscribe(ops.OdtDocument.signalParagraphStyleModified,q);runtime.getWindow().clearInterval(s);t.parentNode.removeChild(t);(function W(a,b){b?d(b):a 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"; +ops.Server=function(){};ops.Server.prototype.connect=function(k,h){};ops.Server.prototype.networkStatus=function(){};ops.Server.prototype.login=function(k,h,b,p){};ops.Server.prototype.joinSession=function(k,h,b,p){};ops.Server.prototype.leaveSession=function(k,h,b,p){};ops.Server.prototype.getGenesisUrl=function(k){}; +// Input 110 +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@namespace svgns url(http://www.w3.org/2000/svg);\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\noffice|document *::selection {\n background: transparent;\n}\noffice|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}\ndraw|frame {\n /** make sure frames are above the main body. */\n z-index: 1;\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}\noffice|text[webodfhelper|links="inactive"] text|a {\n cursor: text;\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: 0;\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 pointer-events: none;\n}\n\ncursor|cursor > .caret {\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 > .handle {\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 > .handle > img {\n border-radius: 5px;\n}\n\ncursor|cursor > .handle.active {\n opacity: 0.8;\n}\n\ncursor|cursor > .handle: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/** Input Method Editor input pane & behaviours */\n/* not within a cursor */\n#eventTrap {\n height: auto;\n display: block;\n position: absolute;\n width: 1px;\n outline: none;\n opacity: 0;\n color: rgba(255, 255, 255, 0); /* hide the blinking caret by setting the colour to fully transparent */\n overflow: hidden; /* The overflow visibility is used to hide and show characters being entered */\n pointer-events: none;\n}\n\n/* within a cursor */\ncursor|cursor > #composer {\n text-decoration: underline;\n}\n\ncursor|cursor[cursor|composing="true"] > #composer {\n display: inline-block;\n height: auto;\n width: auto;\n}\n\ncursor|cursor[cursor|composing="true"] {\n display: inline-block;\n width: auto;\n height: inherit;\n}\n\ncursor|cursor[cursor|composing="true"] > .caret {\n /* during composition, the caret should be pushed along by the composition text, inline with the text */\n position: static;\n /* as it is now part of an inline-block, it will no longer need correct to top or height values to align properly */\n height: auto !important;\n top: auto !important;\n}\n\neditinfo|editinfo {\n /* Empty or invisible display:inline elements respond very badly to mouse selection.\n Inline blocks are much more reliably selectable in Chrome & friends */\n display: inline-block;\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 color: black;\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 pointer-events: none;\n top: 0;\n left: 0;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 15;\n}\n.selectionOverlay > polygon {\n fill-opacity: 0.3;\n stroke-opacity: 0.8;\n stroke-width: 1;\n fill-rule: evenodd;\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\ndiv.customScrollbars::-webkit-scrollbar\n{\n width: 8px;\n height: 8px;\n background-color: transparent;\n}\n\ndiv.customScrollbars::-webkit-scrollbar-track\n{\n background-color: transparent;\n}\n\ndiv.customScrollbars::-webkit-scrollbar-thumb\n{\n background-color: #444;\n border-radius: 4px;\n}\n';