All files / src/ce/nodes ve.ce.ActiveNode.js

90.9% Statements 30/33
75% Branches 15/20
80% Functions 4/5
90.9% Lines 30/33

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                                1x   53x     53x 53x     53x           53x         1x             1x   48x       48x     48x   48x           1x 37x       37x     37x   37x 3x     37x               1x 88x 88x   88x     7x       7x   7x   81x 3x   81x 81x        
/*!
 * VisualEditor ContentEditable ActiveNode class.
 *
 * @copyright See AUTHORS.txt
 */
 
/**
 * Active nodes are editable sections that are nested inside
 * uneditable sections.
 *
 * @class
 * @mixes ve.ce.ContentEditableNode
 * @abstract
 *
 * @constructor
 */
ve.ce.ActiveNode = function VeCeActiveNode() {
	// Mixin constructor
	ve.ce.ContentEditableNode.call( this );
 
	// Properties
	this.activeNodeSurface = null;
	this.isActiveNodeSetup = false;
 
	// Events
	this.connect( this, {
		setup: 'onActiveNodeSetup',
		teardown: 'onActiveNodeTeardown'
	} );
 
	// DOM changes
	this.$element.addClass( 've-ce-activeNode' );
};
 
/* Inheritance */
 
OO.mixinClass( ve.ce.ActiveNode, ve.ce.ContentEditableNode );
 
/* Methods */
 
/**
 * Handle node setup
 */
ve.ce.ActiveNode.prototype.onActiveNodeSetup = function () {
	// Exit if already setup or not attached
	Iif ( this.isActiveNodeSetup || !this.root ) {
		return;
	}
 
	this.activeNodeSurface = this.getRoot().getSurface();
 
	// Events
	this.activeNodeSurface.getModel().connect( this, { select: 'onActiveNodeSurfaceModelSelect' } );
 
	this.isActiveNodeSetup = true;
};
 
/**
 * Handle node teardown
 */
ve.ce.ActiveNode.prototype.onActiveNodeTeardown = function () {
	Iif ( !this.isActiveNodeSetup ) {
		return;
	}
 
	const surface = this.activeNodeSurface;
 
	// Events
	surface.getModel().disconnect( this );
 
	if ( surface.getActiveNode() === this ) {
		surface.setActiveNode( null );
	}
 
	this.isActiveNodeSetup = false;
};
 
/**
 * Handle select events from the surface model.
 *
 * @param {ve.dm.Selection} selection
 */
ve.ce.ActiveNode.prototype.onActiveNodeSurfaceModelSelect = function ( selection ) {
	const coveringRange = selection.getCoveringRange(),
		surface = this.activeNodeSurface;
 
	if ( coveringRange && this.model.getRange().containsRange( new ve.Range( coveringRange.from ) ) ) {
		// Only set this as the active node if active node is empty, or not a
		// descendant of this node.
		Eif (
			!surface.getActiveNode() ||
			!surface.getActiveNode().traverseUpstream( ( node ) => node !== this )
		) {
			surface.setActiveNode( this );
		}
		this.$element.addClass( 've-ce-activeNode-active' );
	} else {
		if ( surface.getActiveNode() === this ) {
			surface.setActiveNode( null );
		}
		Eif ( !selection.isNull() ) {
			this.$element.removeClass( 've-ce-activeNode-active' );
		}
	}
};