|
@@ -29,17 +29,19 @@ const attrOpTypes = new Set(
|
|
|
* {@link module:engine/model/document~Document document} and has it ranges updated accordingly. Internal implementation of this
|
|
* {@link module:engine/model/document~Document document} and has it ranges updated accordingly. Internal implementation of this
|
|
|
* mechanism bases on {@link module:engine/model/liverange~LiveRange live ranges}.
|
|
* mechanism bases on {@link module:engine/model/liverange~LiveRange live ranges}.
|
|
|
*
|
|
*
|
|
|
- * Differences between {@link module:engine/model/selection~Selection} and `LiveSelection` are two:
|
|
|
|
|
|
|
+ * Differences between {@link module:engine/model/selection~Selection} and `LiveSelection` are:
|
|
|
* * there is always a range in `LiveSelection` - even if no ranges were added there is a
|
|
* * there is always a range in `LiveSelection` - even if no ranges were added there is a
|
|
|
* {@link module:engine/model/liveselection~LiveSelection#_getDefaultRange "default range"} present in the selection,
|
|
* {@link module:engine/model/liveselection~LiveSelection#_getDefaultRange "default range"} present in the selection,
|
|
|
- * * ranges added to this selection updates automatically when the document changes.
|
|
|
|
|
|
|
+ * * ranges added to this selection updates automatically when the document changes,
|
|
|
|
|
+ * * attributes of `LiveSelection` are updated automatically according to selection ranges,
|
|
|
|
|
+ * * markers containing `LiveSelection` are updated automatically according to selection ranges.
|
|
|
*
|
|
*
|
|
|
* Since `LiveSelection` uses {@link module:engine/model/liverange~LiveRange live ranges}
|
|
* Since `LiveSelection` uses {@link module:engine/model/liverange~LiveRange live ranges}
|
|
|
* and is updated when {@link module:engine/model/document~Document document}
|
|
* and is updated when {@link module:engine/model/document~Document document}
|
|
|
* changes, it cannot be set on {@link module:engine/model/node~Node nodes}
|
|
* changes, it cannot be set on {@link module:engine/model/node~Node nodes}
|
|
|
* that are inside {@link module:engine/model/documentfragment~DocumentFragment document fragment}.
|
|
* that are inside {@link module:engine/model/documentfragment~DocumentFragment document fragment}.
|
|
|
* If you need to represent a selection in document fragment,
|
|
* If you need to represent a selection in document fragment,
|
|
|
- * use {@link module:engine/model/selection~Selection "normal" selection} instead.
|
|
|
|
|
|
|
+ * use {@link module:engine/model/selection~Selection Selection class} instead.
|
|
|
*
|
|
*
|
|
|
* @extends module:engine/model/selection~Selection
|
|
* @extends module:engine/model/selection~Selection
|
|
|
*/
|
|
*/
|
|
@@ -72,12 +74,36 @@ export default class LiveSelection extends Selection {
|
|
|
*/
|
|
*/
|
|
|
this._attributePriority = new Map();
|
|
this._attributePriority = new Map();
|
|
|
|
|
|
|
|
- // Whenever attribute operation is performed on document, update attributes. This is not the most efficient
|
|
|
|
|
- // way to update selection attributes, but should be okay for now. `_updateAttributes` will be fired too often,
|
|
|
|
|
- // but it won't change attributes or fire `change:attribute` event if not needed.
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Keeps names of markers in which selection is placed.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @private
|
|
|
|
|
+ * @member {Set} module:engine/model/liveselection~LiveSelection#_markers
|
|
|
|
|
+ */
|
|
|
|
|
+ this._markers = new Set();
|
|
|
|
|
+
|
|
|
|
|
+ // Whenever attribute operation is performed on document, update attributes and markers that contains this selection.
|
|
|
|
|
+ // This is not the most efficient way to update selection attributes, but should be okay for now.
|
|
|
this.listenTo( this._document, 'change', ( evt, type ) => {
|
|
this.listenTo( this._document, 'change', ( evt, type ) => {
|
|
|
if ( attrOpTypes.has( type ) ) {
|
|
if ( attrOpTypes.has( type ) ) {
|
|
|
this._updateAttributes( false );
|
|
this._updateAttributes( false );
|
|
|
|
|
+ } else if ( type == 'move' ) {
|
|
|
|
|
+ // The only case when `LiveSelection` range may end up in marker after document change
|
|
|
|
|
+ // is when the document fragment containing selection is moved into (or outside of) a marker.
|
|
|
|
|
+ this._updateMarkers();
|
|
|
|
|
+ }
|
|
|
|
|
+ } );
|
|
|
|
|
+
|
|
|
|
|
+ // Whenever marker is added or removed, update list of markers that contains this selection.
|
|
|
|
|
+ this.listenTo( this._document.markers, 'add', ( evt, marker ) => {
|
|
|
|
|
+ if ( marker.getRange().containsPosition( this.getFirstPosition() ) ) {
|
|
|
|
|
+ this.addMarker( marker.name );
|
|
|
|
|
+ }
|
|
|
|
|
+ } );
|
|
|
|
|
+
|
|
|
|
|
+ this.listenTo( this._document.markers, 'remove', ( evt, marker ) => {
|
|
|
|
|
+ if ( marker.getRange().containsPosition( this.getFirstPosition() ) ) {
|
|
|
|
|
+ this.removeMarker( marker.name );
|
|
|
}
|
|
}
|
|
|
} );
|
|
} );
|
|
|
}
|
|
}
|
|
@@ -153,7 +179,7 @@ export default class LiveSelection extends Selection {
|
|
|
*/
|
|
*/
|
|
|
addRange( range, isBackward = false ) {
|
|
addRange( range, isBackward = false ) {
|
|
|
super.addRange( range, isBackward );
|
|
super.addRange( range, isBackward );
|
|
|
- this.refreshAttributes();
|
|
|
|
|
|
|
+ this.refresh();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -161,7 +187,7 @@ export default class LiveSelection extends Selection {
|
|
|
*/
|
|
*/
|
|
|
removeAllRanges() {
|
|
removeAllRanges() {
|
|
|
super.removeAllRanges();
|
|
super.removeAllRanges();
|
|
|
- this.refreshAttributes();
|
|
|
|
|
|
|
+ this.refresh();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -169,7 +195,7 @@ export default class LiveSelection extends Selection {
|
|
|
*/
|
|
*/
|
|
|
setRanges( newRanges, isLastBackward = false ) {
|
|
setRanges( newRanges, isLastBackward = false ) {
|
|
|
super.setRanges( newRanges, isLastBackward );
|
|
super.setRanges( newRanges, isLastBackward );
|
|
|
- this.refreshAttributes();
|
|
|
|
|
|
|
+ this.refresh();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -231,10 +257,12 @@ export default class LiveSelection extends Selection {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Removes all attributes from the selection and sets attributes according to the surrounding nodes.
|
|
|
|
|
|
|
+ * Removes all attributes from the selection and sets attributes according to the surrounding nodes. Refreshes list
|
|
|
|
|
+ * of names of markers containing this selection.
|
|
|
*/
|
|
*/
|
|
|
- refreshAttributes() {
|
|
|
|
|
|
|
+ refresh() {
|
|
|
this._updateAttributes( true );
|
|
this._updateAttributes( true );
|
|
|
|
|
+ this._updateMarkers();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -364,6 +392,26 @@ export default class LiveSelection extends Selection {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Updates list of markers which contains this selection.
|
|
|
|
|
+ *
|
|
|
|
|
+ * A marker contains this selection if {@link module:engine/model/selection~Selection#getFirstPosition first position}
|
|
|
|
|
+ * is inside that marker range.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @protected
|
|
|
|
|
+ */
|
|
|
|
|
+ _updateMarkers() {
|
|
|
|
|
+ this.clearMarkers();
|
|
|
|
|
+
|
|
|
|
|
+ const pos = this.getFirstPosition();
|
|
|
|
|
+
|
|
|
|
|
+ for ( let marker of this._document.markers ) {
|
|
|
|
|
+ if ( marker.getRange().containsPosition( pos ) ) {
|
|
|
|
|
+ this.addMarker( marker.name );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Generates and returns an attribute key for selection attributes store, basing on original attribute key.
|
|
* Generates and returns an attribute key for selection attributes store, basing on original attribute key.
|
|
|
*
|
|
*
|
|
@@ -638,9 +686,9 @@ export default class LiveSelection extends Selection {
|
|
|
|
|
|
|
|
const newRange = this._prepareRange( selectionRange );
|
|
const newRange = this._prepareRange( selectionRange );
|
|
|
const index = this._ranges.indexOf( gyRange );
|
|
const index = this._ranges.indexOf( gyRange );
|
|
|
- this._ranges.splice( index, 1, newRange );
|
|
|
|
|
-
|
|
|
|
|
gyRange.detach();
|
|
gyRange.detach();
|
|
|
|
|
+
|
|
|
|
|
+ this._ranges.splice( index, 1, newRange );
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|