| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- /**
- * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
- */
- /**
- * @module widget/highlightstack
- */
- import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
- import mix from '@ckeditor/ckeditor5-utils/src/mix';
- /**
- * Class used to handle correct order of highlights on elements.
- *
- * When different highlights are applied to same element correct order should be preserved:
- *
- * * highlight with highest priority should be applied,
- * * if two highlights have same priority - sort by CSS class provided in
- * {@link module:engine/conversion/downcasthelpers~HighlightDescriptor}.
- *
- * This way, highlight will be applied with the same rules it is applied on texts.
- */
- export default class HighlightStack {
- /**
- * Creates class instance.
- */
- constructor() {
- this._stack = [];
- }
- /**
- * Adds highlight descriptor to the stack.
- *
- * @fires change:top
- * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} descriptor
- * @param {module:engine/view/downcastwriter~DowncastWriter} writer
- */
- add( descriptor, writer ) {
- const stack = this._stack;
- // Save top descriptor and insert new one. If top is changed - fire event.
- const oldTop = stack[ 0 ];
- this._insertDescriptor( descriptor );
- const newTop = stack[ 0 ];
- // When new object is at the top and stores different information.
- if ( oldTop !== newTop && !compareDescriptors( oldTop, newTop ) ) {
- this.fire( 'change:top', {
- oldDescriptor: oldTop,
- newDescriptor: newTop,
- writer
- } );
- }
- }
- /**
- * Removes highlight descriptor from the stack.
- *
- * @fires change:top
- * @param {String} id Id of the descriptor to remove.
- * @param {module:engine/view/downcastwriter~DowncastWriter} writer
- */
- remove( id, writer ) {
- const stack = this._stack;
- const oldTop = stack[ 0 ];
- this._removeDescriptor( id );
- const newTop = stack[ 0 ];
- // When new object is at the top and stores different information.
- if ( oldTop !== newTop && !compareDescriptors( oldTop, newTop ) ) {
- this.fire( 'change:top', {
- oldDescriptor: oldTop,
- newDescriptor: newTop,
- writer
- } );
- }
- }
- /**
- * Inserts given descriptor in correct place in the stack. It also takes care about updating information when
- * descriptor with same id is already present.
- *
- * @private
- * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} descriptor
- */
- _insertDescriptor( descriptor ) {
- const stack = this._stack;
- const index = stack.findIndex( item => item.id === descriptor.id );
- // Inserting exact same descriptor - do nothing.
- if ( compareDescriptors( descriptor, stack[ index ] ) ) {
- return;
- }
- // If descriptor with same id but with different information is on the stack - remove it.
- if ( index > -1 ) {
- stack.splice( index, 1 );
- }
- // Find correct place to insert descriptor in the stack.
- // It have different information (for example priority) so it must be re-inserted in correct place.
- let i = 0;
- while ( stack[ i ] && shouldABeBeforeB( stack[ i ], descriptor ) ) {
- i++;
- }
- stack.splice( i, 0, descriptor );
- }
- /**
- * Removes descriptor with given id from the stack.
- *
- * @private
- * @param {String} id Descriptor's id.
- */
- _removeDescriptor( id ) {
- const stack = this._stack;
- const index = stack.findIndex( item => item.id === id );
- // If descriptor with same id is on the list - remove it.
- if ( index > -1 ) {
- stack.splice( index, 1 );
- }
- }
- }
- mix( HighlightStack, EmitterMixin );
- // Compares two descriptors by checking their priority and class list.
- //
- // @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} a
- // @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} b
- // @returns {Boolean} Returns true if both descriptors are defined and have same priority and classes.
- function compareDescriptors( a, b ) {
- return a && b && a.priority == b.priority && classesToString( a.classes ) == classesToString( b.classes );
- }
- // Checks whenever first descriptor should be placed in the stack before second one.
- //
- // @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} a
- // @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} b
- // @returns {Boolean}
- function shouldABeBeforeB( a, b ) {
- if ( a.priority > b.priority ) {
- return true;
- } else if ( a.priority < b.priority ) {
- return false;
- }
- // When priorities are equal and names are different - use classes to compare.
- return classesToString( a.classes ) > classesToString( b.classes );
- }
- // Converts CSS classes passed with {@link module:engine/conversion/downcasthelpers~HighlightDescriptor} to
- // sorted string.
- //
- // @param {String|Array<String>} descriptor
- // @returns {String}
- function classesToString( classes ) {
- return Array.isArray( classes ) ? classes.sort().join( ',' ) : classes;
- }
- /**
- * Fired when top element on {@link module:widget/highlightstack~HighlightStack} has been changed
- *
- * @event change:top
- * @param {Object} data Additional information about the change.
- * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} [data.newDescriptor] New highlight
- * descriptor. It will be `undefined` when last descriptor is removed from the stack.
- * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} [data.oldDescriptor] Old highlight
- * descriptor. It will be `undefined` when first descriptor is added to the stack.
- * @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that can be used to modify element.
- */
|