1 /** 2 * Copyright (C) 2012 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 core, ops, gui, runtime*/ 26 27 /** 28 * @class 29 * A cursor is a dom node that visually represents a cursor in a DOM tree. 30 * It should stay synchronized with the selection in the document. When 31 * there is only one collapsed selection range, a cursor should be shown at 32 * that point. 33 * 34 * Putting the cursor in the DOM tree modifies the DOM, so care should be taken 35 * to keep the selection consistent. If e.g. a selection is drawn over the 36 * cursor, and the cursor is updated to the selection, the cursor is removed 37 * from the DOM because the selection is not collapsed. This means that the 38 * offsets of the selection may have to be changed. 39 * 40 * When the selection is collapsed, the cursor is placed after the point of the 41 * selection and the selection will stay valid. However, if the cursor was 42 * placed in the DOM tree and was counted in the offset, the offset in the 43 * selection should be decreased by one. 44 * 45 * Even when the selection allows for a cursor, it might be desireable to hide 46 * the cursor by not letting it be part of the DOM. 47 * 48 * @constructor 49 * @param {!string} memberId The memberid this cursor is assigned to 50 * @param {!ops.Document} document The document in which the cursor is placed 51 */ 52 ops.OdtCursor = function OdtCursor(memberId, document) { 53 "use strict"; 54 var self = this, 55 validSelectionTypes = {}, 56 selectionType, 57 /**@type{!gui.SelectionMover}*/ 58 selectionMover, 59 /**@type{!core.Cursor}*/ 60 cursor, 61 events = new core.EventNotifier([ops.OdtCursor.signalCursorUpdated]); 62 63 /** 64 * Remove the cursor from the document 65 * @return {undefined} 66 */ 67 this.removeFromDocument = function () { 68 // TODO: find out if nodeAfterCursor, textNodeIncrease need to be dealt with in any way 69 cursor.remove(); 70 }; 71 72 /** 73 * Subscribe to cursor update events. 74 * 75 * The update event called whenever the cursor is moved around manually. 76 * @param {!string} eventid 77 * @param {!Function} cb 78 */ 79 this.subscribe = function (eventid, cb) { 80 events.subscribe(eventid, cb); 81 }; 82 83 /** 84 * Unsubscribe from cursor events 85 * @param {!string} eventid 86 * @param {!Function} cb 87 */ 88 this.unsubscribe = function (eventid, cb) { 89 events.unsubscribe(eventid, cb); 90 }; 91 92 /** 93 * @return {!gui.StepCounter} 94 */ 95 this.getStepCounter = function () { 96 return selectionMover.getStepCounter(); 97 }; 98 /** 99 * Obtain the memberid the cursor is assigned to. 100 * @return {string} 101 */ 102 this.getMemberId = function () { 103 return memberId; 104 }; 105 /** 106 * Obtain the node representing the cursor. 107 * @return {!Element} 108 */ 109 this.getNode = function () { 110 return cursor.getNode(); 111 }; 112 /** 113 * Obtain the node representing the selection start point. 114 * If a 0-length range is selected (e.g., by clicking without 115 * dragging),, this will return the exact same node as getNode 116 * @return {!Element} 117 */ 118 this.getAnchorNode = function () { 119 return cursor.getAnchorNode(); 120 }; 121 /** 122 * Obtain the currently selected range to which the cursor corresponds. 123 * @return {!Range} 124 */ 125 this.getSelectedRange = function () { 126 return cursor.getSelectedRange(); 127 }; 128 /** Set the given range as the selected range for this cursor 129 * @param {!Range} range, 130 * @param {boolean} isForwardSelection 131 * @return {undefined} 132 */ 133 this.setSelectedRange = function (range, isForwardSelection) { 134 cursor.setSelectedRange(range, isForwardSelection); 135 events.emit(ops.OdtCursor.signalCursorUpdated, self); 136 }; 137 /** 138 * Returns if the selection of this cursor has the 139 * same direction as the direction of the range 140 * @return {boolean} 141 */ 142 this.hasForwardSelection = function () { 143 return cursor.hasForwardSelection(); 144 }; 145 /** 146 * Obtain the document to which the cursor corresponds. 147 * @return {!ops.Document} 148 */ 149 this.getDocument = function () { 150 return document; 151 }; 152 153 /** 154 * Gets the current selection type. 155 * @return {!string} 156 */ 157 this.getSelectionType = function () { 158 return selectionType; 159 }; 160 161 /** 162 * Sets the current selection type to the given value. 163 * @param {!string} value 164 * @return {undefined} 165 */ 166 this.setSelectionType = function (value) { 167 if (validSelectionTypes.hasOwnProperty(value)) { 168 selectionType = value; 169 } else { 170 runtime.log("Invalid selection type: " + value); 171 } 172 }; 173 174 /** 175 * Reset selection type to default. 176 * @return {undefined} 177 */ 178 this.resetSelectionType = function () { 179 self.setSelectionType(ops.OdtCursor.RangeSelection); 180 }; 181 182 function init() { 183 cursor = new core.Cursor(document.getDOMDocument(), memberId); 184 selectionMover = new gui.SelectionMover(cursor, document.getRootNode()); 185 186 validSelectionTypes[ops.OdtCursor.RangeSelection] = true; 187 validSelectionTypes[ops.OdtCursor.RegionSelection] = true; 188 self.resetSelectionType(); 189 } 190 191 init(); 192 }; 193 194 /**@const 195 @type {!string} */ 196 ops.OdtCursor.RangeSelection = 'Range'; 197 /**@const 198 @type {!string} */ 199 ops.OdtCursor.RegionSelection = 'Region'; 200 /**@const 201 @type {!string} */ 202 ops.OdtCursor.signalCursorUpdated = "cursorUpdated"; 203