All files / src/ui ve.ui.SequenceRegistry.js

96.87% Statements 31/32
96.15% Branches 25/26
100% Functions 3/3
96.87% Lines 31/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121                          1x   1x         1x               1x   6x           6x                                         1x                           1607x 1607x 9107x 1219x   9107x 1219x   9107x 1219x     1607x 1607x   1607x 9642x 9642x 378x   9264x 240x   9024x 9024x 3x           1607x         1x       1x     1x     1x      
/*!
 * VisualEditor UserInterface SequenceRegistry class.
 *
 * @copyright See AUTHORS.txt
 */
 
/**
 * Sequence registry.
 *
 * @class
 * @extends OO.Registry
 * @constructor
 */
ve.ui.SequenceRegistry = function VeUiSequenceRegistry() {
	// Parent constructor
	ve.ui.SequenceRegistry.super.call( this );
};
 
/* Inheritance */
 
OO.inheritClass( ve.ui.SequenceRegistry, OO.Registry );
 
/**
 * Register a sequence with the factory.
 *
 * @param {ve.ui.Sequence} sequence
 * @throws {Error} If sequence is not an instance of ve.ui.Sequence
 */
ve.ui.SequenceRegistry.prototype.register = function ( sequence ) {
	// Validate arguments
	Iif ( !( sequence instanceof ve.ui.Sequence ) ) {
		throw new Error(
			'sequence must be an instance of ve.ui.Sequence, cannot be a ' + typeof sequence
		);
	}
 
	ve.ui.SequenceRegistry.super.prototype.register.call( this, sequence.getName(), sequence );
};
 
/**
 * Matching sequence and corresponding range
 *
 * @typedef {Object} Match
 * @memberof ve.ui.SequenceRegistry
 * @property {ve.ui.Sequence} sequence
 * @property {ve.Range} range
 */
 
/**
 * Find sequence matches a given offset in the data
 *
 * @param {ve.dm.ElementLinearData} data
 * @param {number} offset
 * @param {boolean} [isPaste] Whether this in the context of a paste
 * @param {boolean} [isDelete] Whether this is after content being deleted
 * @return {ve.ui.SequenceRegistry.Match[]} Array of matching sequences, and the corresponding range of the match for each.
 */
ve.ui.SequenceRegistry.prototype.findMatching = function ( data, offset, isPaste, isDelete ) {
	// To avoid blowup when matching RegExp sequences, we're going to grab
	// all the plaintext to the left (until the nearest node) *once* and pass
	// it to each sequence matcher.  We're also going to hard-limit that
	// plaintext to 256 characters to ensure we don't run into O(N^2)
	// slowdown when inserting N characters of plain text.
 
	// First skip over open elements, then close elements, to ensure that
	// pressing enter after a (possibly nested) list item or inside a
	// paragraph works properly.  Typing "foo\n" inside a paragraph creates
	// "foo</p><p>" in the content model, and typing "foo\n" inside a list
	// creates "foo</p></li><li><p>" -- we want to give the matcher a
	// chance to match "foo\n+" in these cases.
	let textStart;
	let state = 0;
	for ( textStart = offset - 1; textStart >= 0 && ( offset - textStart ) <= 256; textStart-- ) {
		if ( state === 0 && !data.isOpenElementData( textStart ) ) {
			state++;
		}
		if ( state === 1 && !data.isCloseElementData( textStart ) ) {
			state++;
		}
		if ( state === 2 && data.isElementData( textStart ) ) {
			break;
		}
	}
	const sequences = [];
	const plaintext = data.getText( true, new ve.Range( textStart + 1, offset ) );
	// Now search through the registry.
	for ( const name in this.registry ) {
		const sequence = this.registry[ name ];
		if ( isPaste && !sequence.checkOnPaste ) {
			continue;
		}
		if ( isDelete && !sequence.checkOnDelete ) {
			continue;
		}
		const range = sequence.match( data, offset, plaintext );
		if ( range !== null ) {
			sequences.push( {
				sequence: sequence,
				range: range
			} );
		}
	}
	return sequences;
};
 
/* Initialization */
 
ve.ui.sequenceRegistry = new ve.ui.SequenceRegistry();
 
/* Registrations */
 
ve.ui.sequenceRegistry.register(
	new ve.ui.Sequence( 'bulletStar', 'bulletWrapOnce', [ { type: 'paragraph' }, '*', ' ' ], 2 )
);
ve.ui.sequenceRegistry.register(
	new ve.ui.Sequence( 'numberDot', 'numberWrapOnce', [ { type: 'paragraph' }, '1', '.', ' ' ], 3 )
);
ve.ui.sequenceRegistry.register(
	new ve.ui.Sequence( 'horizontalRule', 'insertHorizontalRule', [ { type: 'paragraph' }, '-', '-', '-', '-' ], 4 )
);