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

91.17% Statements 31/34
75% Branches 15/20
80% Functions 4/5
91.17% Lines 31/34

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                                1x   47x     47x 47x     47x           47x         1x             1x   42x       42x     42x   42x           1x 31x       31x     31x   31x 3x     31x               1x 74x 74x 74x   74x     7x           7x   7x   67x 3x   67x 67x        
/*!
 * VisualEditor ContentEditable ActiveNode class.
 *
 * @copyright See AUTHORS.txt
 */
 
/**
 * Active nodes are editable sections that are nested inside
 * uneditable sections.
 *
 * @class
 * @mixins 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;
	}
 
	var 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 ) {
	var coveringRange = selection.getCoveringRange(),
		surface = this.activeNodeSurface,
		activeNode = this;
 
	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( function ( node ) {
				return node !== activeNode;
			} )
		) {
			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' );
		}
	}
};