1 /** 2 * Copyright (C) 2012-2013 KO GmbH <copyright@kogmbh.com> 3 * 4 * @licstart 5 * This file is part of WebODF. 6 * 7 * WebODF is free software: you can redistribute it and/or modify it 8 * under the terms of the GNU Affero General Public License (GNU AGPL) 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * WebODF is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Affero General Public License for more details. 16 * 17 * You should have received a copy of the GNU Affero General Public License 18 * along with WebODF. If not, see <http://www.gnu.org/licenses/>. 19 * @licend 20 * 21 * @source: http://www.webodf.org/ 22 * @source: https://github.com/kogmbh/WebODF/ 23 */ 24 25 /*global ops, runtime, odf, core*/ 26 27 /** 28 * @constructor 29 * @implements ops.Operation 30 */ 31 ops.OpRemoveText = function OpRemoveText() { 32 "use strict"; 33 34 var memberid, timestamp, 35 /**@type {number}*/ 36 position, 37 /**@type {number}*/ 38 length, 39 /**@type{!odf.OdfUtils}*/ 40 odfUtils, 41 /**@type{!core.DomUtils}*/ 42 domUtils; 43 44 /** 45 * @param {!ops.OpRemoveText.InitSpec} data 46 */ 47 this.init = function (data) { 48 runtime.assert(data.length >= 0, "OpRemoveText only supports positive lengths"); 49 memberid = data.memberid; 50 timestamp = data.timestamp; 51 position = parseInt(data.position, 10); 52 length = parseInt(data.length, 10); 53 odfUtils = new odf.OdfUtils(); 54 domUtils = new core.DomUtils(); 55 }; 56 57 this.isEdit = true; 58 this.group = undefined; 59 60 /** 61 * @param {!ops.Document} document 62 */ 63 this.execute = function (document) { 64 var odtDocument = /**@type{ops.OdtDocument}*/(document), 65 range, 66 textNodes, 67 paragraph, 68 cursor = odtDocument.getCursor(memberid), 69 collapseRules = new odf.CollapsingRules(odtDocument.getRootNode()); 70 71 odtDocument.upgradeWhitespacesAtPosition(position); 72 odtDocument.upgradeWhitespacesAtPosition(position + length); 73 74 range = odtDocument.convertCursorToDomRange(position, length); 75 domUtils.splitBoundaries(range); 76 textNodes = odfUtils.getTextElements(range, false, true); 77 paragraph = /**@type{!Element}*/(odfUtils.getParagraphElement(range.startContainer, range.startOffset)); 78 runtime.assert(paragraph !== undefined, "Attempting to remove text outside a paragraph element"); 79 range.detach(); 80 81 // Each character element is fully contained within the range, so will be completely removed 82 textNodes.forEach(function (element) { 83 if (element.parentNode) { 84 // In order to guarantee OT-ability, this rule needs to be enforced, otherwise it's impossible to tell 85 // which paragraph boundaries might be impacted by the remove op. 86 runtime.assert(domUtils.containsNode(paragraph, element), 87 "RemoveText only supports removing elements within the same paragraph"); 88 89 collapseRules.mergeChildrenIntoParent(element); 90 } else { 91 // If this is an empty text node, it might have already been removed from it's container. 92 // Although WebODF specifically avoids empty text nodes at all times, incorrect 3rd party 93 // DOM manipulation (or undiscovered WebODF bugs) may leave these behind. 94 runtime.log("WARN: text element has already been removed from it's container"); 95 } 96 }); 97 98 odtDocument.emit(ops.OdtDocument.signalStepsRemoved, {position: position}); 99 odtDocument.downgradeWhitespacesAtPosition(position); 100 odtDocument.fixCursorPositions(); 101 odtDocument.getOdfCanvas().refreshSize(); 102 odtDocument.emit(ops.OdtDocument.signalParagraphChanged, { 103 paragraphElement: paragraph, 104 memberId: memberid, 105 timeStamp: timestamp 106 }); 107 108 if (cursor) { 109 cursor.resetSelectionType(); 110 odtDocument.emit(ops.Document.signalCursorMoved, cursor); 111 } 112 113 odtDocument.getOdfCanvas().rerenderAnnotations(); 114 return true; 115 }; 116 117 /** 118 * @return {!ops.OpRemoveText.Spec} 119 */ 120 this.spec = function () { 121 return { 122 optype: "RemoveText", 123 memberid: memberid, 124 timestamp: timestamp, 125 position: position, 126 length: length 127 }; 128 }; 129 }; 130 /**@typedef{{ 131 optype:string, 132 memberid:string, 133 timestamp:number, 134 position:number, 135 length:number 136 }}*/ 137 ops.OpRemoveText.Spec; 138 /**@typedef{{ 139 memberid:string, 140 timestamp:(number|undefined), 141 position:number, 142 length:number 143 }}*/ 144 ops.OpRemoveText.InitSpec; 145