text: /*!
   * VisualEditor ContentEditable Node class.
   *
   * @copyright 2011-2020 VisualEditor Team and others; see http://ve.mit-license.org
   */
  
  /**
   * Generic ContentEditable node.
   *
   * @abstract
   * @extends ve.ce.View
   * @mixins ve.Node
   *
   * @constructor
   * @param {ve.dm.Node} model Model to observe
   * @param {Object} [config] Configuration options
   */
  ve.ce.Node = function VeCeNode() {
  	// Parent constructor
  	ve.ce.Node.super.apply( this, arguments );
  
  	// Mixin constructor
  	ve.Node.call( this );
  };
  
  /* Inheritance */
  
  OO.inheritClass( ve.ce.Node, ve.ce.View );
  
  OO.mixinClass( ve.ce.Node, ve.Node );
  
  /* Static Members */
  
  /**
   * Whether Enter splits this node type.
   *
   * When the user presses Enter, we split the node they're in (if splittable), then split its parent
   * if splittable, and continue traversing up the tree and stop at the first non-splittable node.
   *
   * @static
   * @property {boolean}
   * @inheritable
   */
  ve.ce.Node.static.splitOnEnter = false;
  
  /**
   * Whether Enter removes the empty last child of this node.
   *
   * Set true on the parent of a splitOnEnter node (e.g. a ListNode) to ensure that the last splittable
   * child (e.g a ListItemNode) is removed when empty and enter is pressed.
   *
   * @static
   * @property {boolean}
   * @inheritable
   */
  ve.ce.Node.static.removeEmptyLastChildOnEnter = false;
  
  /**
   * Whether a node supports multiline input at all.
   *
   * If set to false, pressing Enter will not perform any splitting at all. If set to null, traverse
   * up the tree until a boolean value is found.
   *
   * @static
   * @property {boolean|null}
   * @inheritable
   */
  ve.ce.Node.static.isMultiline = null;
  
  /**
   * Whether a node traps the cursor when active, e.g. in table cells
   *
   * @static
   * @property {boolean}
   * @inheritable
   */
  ve.ce.Node.static.trapsCursor = false;
  
  /**
   * Command to execute when Enter is pressed while this node is selected, or when the node is double-clicked.
   *
   * @static
   * @property {string|null}
   * @inheritable
   */
  ve.ce.Node.static.primaryCommandName = null;
  
  /* Static Methods */
  
  /**
   * Get a plain text description.
   *
   * @static
   * @inheritable
   * @param {ve.dm.Node} node Node model
   * @return {string} Description of node
   */
  ve.ce.Node.static.getDescription = function () {
  	return '';
  };
  
  /* Methods */
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.getChildNodeTypes = function () {
  	return this.model.getChildNodeTypes();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.getParentNodeTypes = function () {
  	return this.model.getParentNodeTypes();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.getSuggestedParentNodeTypes = function () {
  	return this.model.getSuggestedParentNodeTypes();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.canHaveChildren = function () {
  	return this.model.canHaveChildren();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.canHaveChildrenNotContent = function () {
  	return this.model.canHaveChildrenNotContent();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.isInternal = function () {
  	return this.model.isInternal();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.isMetaData = function () {
  	return this.model.isMetaData();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.isWrapped = function () {
  	return this.model.isWrapped();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.isUnwrappable = function () {
  	return this.model.isUnwrappable();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.canContainContent = function () {
  	return this.model.canContainContent();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.isContent = function () {
  	return this.model.isContent();
  };
  
  /**
   * @inheritdoc ve.Node
   *
   * If this is set to true it should implement:
   *
   *     setFocused( boolean val )
   *     boolean isFocused()
   */
  ve.ce.Node.prototype.isFocusable = function () {
  	return this.model.isFocusable();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.isAlignable = function () {
  	return this.model.isAlignable();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.isCellable = function () {
  	return this.model.isCellable();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.isCellEditable = function () {
  	return this.model.isCellEditable();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.hasSignificantWhitespace = function () {
  	return this.model.hasSignificantWhitespace();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.handlesOwnChildren = function () {
  	return this.model.handlesOwnChildren();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.shouldIgnoreChildren = function () {
  	return this.model.shouldIgnoreChildren();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.getLength = function () {
  	return this.model.getLength();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.getOuterLength = function () {
  	return this.model.getOuterLength();
  };
  
  /**
   * @inheritdoc ve.Node
   */
  ve.ce.Node.prototype.getOffset = function () {
  	return this.model.getOffset();
  };
  
  /**
   * Check if the node can be split.
   *
   * @return {boolean} Node can be split
   */
  ve.ce.Node.prototype.splitOnEnter = function () {
  	return this.constructor.static.splitOnEnter;
  };
  
  /**
   * Check if the node removes its empty last child on 'enter'.
   *
   * @return {boolean} Node removes empty last child on 'enter'
   */
  ve.ce.Node.prototype.removeEmptyLastChildOnEnter = function () {
  	return this.constructor.static.removeEmptyLastChildOnEnter;
  };
  
  /**
   * Check if the node is supports multiline input.
   *
   * Traverses upstream until a boolean value is found. If no value
   * is found, reads the default from the surface.
   *
   * @return {boolean} Node supports multiline input
   */
  ve.ce.Node.prototype.isMultiline = function () {
  	var booleanNode = this.traverseUpstream( function ( node ) {
  		return node.constructor.static.isMultiline === null;
  	} );
  	if ( booleanNode ) {
  		return booleanNode.constructor.static.isMultiline;
  	} else {
  		return !this.root || this.getRoot().getSurface().getSurface().isMultiline();
  	}
  };
  
  /**
   * Check if the node traps cursor when active
   *
   * @return {boolean} Node traps cursor
   */
  ve.ce.Node.prototype.trapsCursor = function () {
  	return this.constructor.static.trapsCursor;
  };
  
  /**
   * Release all memory.
   */
  ve.ce.Node.prototype.destroy = function () {
  	this.parent = null;
  	this.root = null;
  	this.doc = null;
  
  	// Parent method
  	ve.ce.Node.super.prototype.destroy.call( this );
  };
  
  /**
   * Get the model's HTML document
   *
   * @return {HTMLDocument} Model document
   */
  ve.ce.Node.prototype.getModelHtmlDocument = function () {
  	return this.model.getDocument() && this.model.getDocument().getHtmlDocument();
  };
  