@@ -19,10 +19,6 @@ import toMap from '@ckeditor/ckeditor5-utils/src/tomap';
1919import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror' ;
2020import log from '@ckeditor/ckeditor5-utils/src/log' ;
2121
22- const attrOpTypes = new Set (
23- [ 'addAttribute' , 'removeAttribute' , 'changeAttribute' , 'addRootAttribute' , 'removeRootAttribute' , 'changeRootAttribute' ]
24- ) ;
25-
2622const storePrefix = 'selection:' ;
2723
2824/**
@@ -533,29 +529,13 @@ class LiveSelection extends Selection {
533529 }
534530 } ) ;
535531
536- this . listenTo ( this . _model , 'applyOperation' , ( evt , args ) => {
537- const operation = args [ 0 ] ;
538-
539- if ( ! operation . isDocumentOperation ) {
540- return ;
541- }
542-
543- // Whenever attribute operation is performed on document, update selection attributes.
544- // This is not the most efficient way to update selection attributes, but should be okay for now.
545- if ( attrOpTypes . has ( operation . type ) ) {
546- this . _updateAttributes ( false ) ;
547- }
548-
549- const batch = operation . delta . batch ;
532+ this . listenTo ( this . _document , 'change' , ( evt , batch ) => {
533+ // Update selection's attributes.
534+ this . _updateAttributes ( false ) ;
550535
551- // Batch may not be passed to the document#change event in some tests.
552- // See https://github.com/ckeditor/ckeditor5-engine/issues/1001#issuecomment-314202352
553- if ( batch ) {
554- // Whenever element which had selection's attributes stored in it stops being empty,
555- // the attributes need to be removed.
556- clearAttributesStoredInElement ( operation , this . _model , batch ) ;
557- }
558- } , { priority : 'low' } ) ;
536+ // Clear selection attributes from element if no longer empty.
537+ clearAttributesStoredInElement ( this . _model , batch ) ;
538+ } ) ;
559539
560540 this . listenTo ( this . _model , 'applyOperation' , ( ) => {
561541 while ( this . _fixGraveyardRangesData . length ) {
@@ -823,6 +803,9 @@ class LiveSelection extends Selection {
823803 // Internal method for removing `LiveSelection` attribute. Supports attribute priorities (through `directChange`
824804 // parameter).
825805 //
806+ // NOTE: Even if attribute is not present in the selection but is provided to this method, it's priority will
807+ // be changed according to `directChange` parameter.
808+ //
826809 // @private
827810 // @param {String } key Attribute key.
828811 // @param {Boolean } [directChange=true] `true` if the change is caused by `Selection` API, `false` if change
@@ -837,16 +820,16 @@ class LiveSelection extends Selection {
837820 return false ;
838821 }
839822
823+ // Update priorities map.
824+ this . _attributePriority . set ( key , priority ) ;
825+
840826 // Don't do anything if value has not changed.
841827 if ( ! super . hasAttribute ( key ) ) {
842828 return false ;
843829 }
844830
845831 this . _attrs . delete ( key ) ;
846832
847- // Update priorities map.
848- this . _attributePriority . set ( key , priority ) ;
849-
850833 return true ;
851834 }
852835
@@ -1021,24 +1004,30 @@ function getAttrsIfCharacter( node ) {
10211004}
10221005
10231006// Removes selection attributes from element which is not empty anymore.
1024- function clearAttributesStoredInElement ( operation , model , batch ) {
1025- let changeParent = null ;
1026-
1027- if ( operation . type == 'insert' ) {
1028- changeParent = operation . position . parent ;
1029- } else if ( operation . type == 'move' || operation . type == 'reinsert' || operation . type == 'remove' ) {
1030- changeParent = operation . getMovedRangeStart ( ) . parent ;
1031- }
1007+ //
1008+ // @private
1009+ // @param {module:engine/model/model~Model } model
1010+ // @param {module:engine/model/batch~Batch } batch
1011+ function clearAttributesStoredInElement ( model , batch ) {
1012+ const differ = model . document . differ ;
1013+
1014+ for ( const entry of differ . getChanges ( ) ) {
1015+ if ( entry . type != 'insert' ) {
1016+ continue ;
1017+ }
10321018
1033- if ( ! changeParent || changeParent . isEmpty ) {
1034- return ;
1035- }
1019+ const changeParent = entry . position . parent ;
1020+ const isNoLongerEmpty = entry . length === changeParent . maxOffset ;
10361021
1037- model . enqueueChange ( batch , writer => {
1038- const storedAttributes = Array . from ( changeParent . getAttributeKeys ( ) ) . filter ( key => key . startsWith ( storePrefix ) ) ;
1022+ if ( isNoLongerEmpty ) {
1023+ model . enqueueChange ( batch , writer => {
1024+ const storedAttributes = Array . from ( changeParent . getAttributeKeys ( ) )
1025+ . filter ( key => key . startsWith ( storePrefix ) ) ;
10391026
1040- for ( const key of storedAttributes ) {
1041- writer . removeAttribute ( key , changeParent ) ;
1027+ for ( const key of storedAttributes ) {
1028+ writer . removeAttribute ( key , changeParent ) ;
1029+ }
1030+ } ) ;
10421031 }
1043- } ) ;
1032+ }
10441033}
0 commit comments