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   52x     52x 52x     52x           52x         1x             1x   47x       47x     47x   47x           1x 35x       35x     35x   35x 3x     35x               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' );
		}
	}
};