Forráskód Böngészése

Added Selection class.

Szymon Cofalik 10 éve
szülő
commit
db97f224ba
1 módosított fájl, 178 hozzáadás és 0 törlés
  1. 178 0
      packages/ckeditor5-engine/src/treemodel/selection.js

+ 178 - 0
packages/ckeditor5-engine/src/treemodel/selection.js

@@ -0,0 +1,178 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [ 'treemodel/liverange', 'emittermixin', 'utils' ], ( LiveRange, EmitterMixin, utils ) => {
+	/**
+	 * Represents a selection that is made on nodes in {@link treeModel.Document}. Selection instance is
+	 * created by {@link treeModel.Document}. In most scenarios you should not need to create an instance of Selection.
+	 *
+	 * @class treeModel.Selection
+	 */
+	class Selection {
+		constructor() {
+			/**
+			 * Stores all ranges that are selected.
+			 *
+			 * @private
+			 * @property {Array.<LiveRange>}
+			 */
+			this._ranges = [];
+
+			/**
+			 * Specifies whether the last added range was added as a backward or forward range.
+			 *
+			 * @private
+			 * @property {Boolean}
+			 */
+			this._lastRangeBackward = false;
+		}
+
+		/**
+		 * Selection anchor. Anchor may be described as a position where the selection starts.
+		 * Together with {@link #focus} they define the direction of selection, which is important
+		 * when expanding/shrinking selection. When there are no ranges in selection anchor is null.
+		 * Anchor is always a start or end of the most recent added range. It may be a bit unintuitive when
+		 * there are multiple ranges in selection.
+		 *
+		 * @property {treeModel.LivePosition|null}
+		 */
+		get anchor() {
+			if ( this._ranges.length > 0 ) {
+				let range = this._ranges[ this._ranges.length - 1 ];
+
+				return this._lastRangeBackward ? range.end : range.start;
+			}
+
+			return null;
+		}
+
+		/**
+		 * Selection focus. Focus is a position where the selection ends. When there are no ranges in selection,
+		 * focus is null.
+		 *
+		 * @see {#anchor}
+		 * @property {treeModel.LivePosition|null}
+		 */
+		get focus() {
+			if ( this._ranges.length > 0 ) {
+				let range = this._ranges[ this._ranges.length - 1 ];
+
+				return this._lastRangeBackward ? range.start : range.end;
+			}
+
+			return null;
+		}
+
+		/**
+		 * Returns whether the selection is collapsed. Selection is collapsed when all it's ranges are collapsed.
+		 *
+		 * @property {Boolean}
+		 */
+		get isCollapsed() {
+			for ( let i = 0; i < this._ranges.length; i++ ) {
+				if ( !this._ranges[ i ].isCollapsed ) {
+					return false;
+				}
+			}
+
+			return true;
+		}
+
+		/**
+		 * Adds a range to the selection. Added range is copied and converted to {@link treeModel.LiveRange}. This means
+		 * that passed range is not saved in the Selection instance and you can safely operate on it. Accepts a flag
+		 * describing in which way the selection is made - passed range might be selected from {@link treeModel.Range#start}
+		 * to {@link treeModel.Range#end} or from {@link treeModel.Range#start} to {@link treeModel.Range#end}. The flag
+		 * is used to set {@link #anchor} and {@link #focus} properties.
+		 *
+		 * @param {treeModel.Range} range Range to add.
+		 * @param {Boolean} [isBackward] Flag describing if added range was selected forward - from start to end (`false`)
+		 * or backward - from end to start (`true`). Defaults to `false`.
+		 */
+		addRange( range, isBackward ) {
+			pushRange.call( this, range );
+			this._lastRangeBackward = !!isBackward;
+
+			this.fire( 'update' );
+		}
+
+		/**
+		 * Unbinds all events previously bound by this selection, including events bound by created {@link treeModel.LiveRange}s.
+		 */
+		detach() {
+			detachRanges.call( this );
+		}
+
+		/**
+		 * Returns an array of ranges added to the selection. The method returns a copy of internal array, so
+		 * it will not change when ranges get added or removed from selection.
+		 *
+		 * @returns {Array.<LiveRange>}
+		 */
+		getRanges() {
+			return this._ranges.slice();
+		}
+
+		/**
+		 * Removes all ranges that were added to the selection. Fires update event.
+		 */
+		removeAllRanges() {
+			detachRanges.call( this );
+			this._ranges = [];
+
+			this.fire( 'update' );
+		}
+
+		/**
+		 * Replaces all ranges that were added to the selection with given array of ranges. Last range of the array
+		 * is treated like the last added range and is used to set {@link #anchor} and {@link #focus}. Accepts a flag
+		 * describing in which way the selection is made (see {@link #addRange}).
+		 *
+		 * @param {Array.<treeModel.Range>} newRanges Array of ranges to set.
+		 * @param {Boolean} [isLastBackward] Flag describing if last added range was selected forward - from start to end (`false`)
+		 * or backward - from end to start (`true`). Defaults to `false`.
+		 */
+		setRanges( newRanges, isLastBackward ) {
+			detachRanges.call( this );
+			this._ranges = [];
+
+			for ( let i = 0; i < newRanges.length; i++ ) {
+				pushRange.call( this, newRanges[ i ] );
+			}
+
+			this._lastRangeBackward = !!isLastBackward;
+			this.fire( 'update' );
+		}
+	}
+
+	/**
+	 * Unbinds all events bound by created {@link treeModel.LiveRange}s.
+	 */
+	function detachRanges() {
+		/*jshint validthis: true */
+
+		for ( let i = 0; i < this._ranges.length; i++ ) {
+			this._ranges[ i ].detach();
+		}
+	}
+
+	/**
+	 * Converts given range to {@link treeModel.LiveRange} and adds it to internal ranges array.
+	 *
+	 * @param {treeModel.Range} range Range to add.
+	 */
+	function pushRange( range ) {
+		/*jshint validthis: true */
+
+		let liveRange = new LiveRange( range.start.clone(), range.end.clone() );
+		this._ranges.push( liveRange );
+	}
+
+	utils.extend( Selection.prototype, EmitterMixin );
+
+	return Selection;
+} );