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   51x     51x 51x     51x           51x         1x             1x   46x       46x     46x   46x           1x 35x       35x     35x   35x 3x     35x               1x 86x 86x   86x     7x       7x   7x   79x 3x   79x 79x        
/*!
 * 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' );
		}
	}
};