UNPKG

ckeditor5-image-upload-base64

Version:

The development environment of CKEditor 5 – the best browser-based rich text editor.

177 lines (153 loc) 5.66 kB
/** * @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. */