mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-29 08:34:54 +00:00
54a232a92b
For data->DOM, this is easy: .toDataElements() can optionally return an array instead of an object, and that will be treated as the data to insert. If this happens, the converter won't descend. The node handler can recursively invoke the converter if it needs to (although I suspect the current implementation is broken when converting block content in an inline context). For DOM->data, this is a bit more complex. The node sets .static.handlesOwnChildren = true; , which triggers the converter to pass a data slice rather than a single data element, and not to descend. The node handler can invoke the converter to recursively convert DOM subtrees to data. ve.dm.Converter (data->DOM): * Renamed createDataElement() to createDataElements() ** .toDataElement() may return element or array, handle this * Renamed childDataElement to childDataElements, is now an array * Actually alienate if .toDataElement() returns null ** Shockingly, this claimed to be supported before but wasn't * Rather than pushing to data, concat to it ** Add closing if needed * Don't descend if .toDataElement() returned an array of length >1, or if the node has .handlesOwnChildren = true ve.dm.Converter (DOM->data): * Split getDomSubtreeFromData() and getDomFromData() * When converting a node that handles its own children, pass in a data slice and skip over that data Change-Id: I196cb4c0895cbf0b428a189adb61b56565573ab3
222 lines
6.1 KiB
JavaScript
222 lines
6.1 KiB
JavaScript
/*!
|
|
* VisualEditor DataModel NodeFactory class.
|
|
*
|
|
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* DataModel node factory.
|
|
*
|
|
* @class
|
|
* @extends ve.NamedClassFactory
|
|
* @constructor
|
|
*/
|
|
ve.dm.NodeFactory = function VeDmNodeFactory() {
|
|
// Parent constructor
|
|
ve.NamedClassFactory.call( this );
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
ve.inheritClass( ve.dm.NodeFactory, ve.NamedClassFactory );
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Get a document data element.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @param {Object} attributes Node attributes, defaults will be used where needed
|
|
* @returns {Object} Data element
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.getDataElement = function ( type, attributes ) {
|
|
var element = { 'type': type };
|
|
if ( type in this.registry ) {
|
|
attributes = ve.extendObject( {}, this.registry[type].static.defaultAttributes, attributes );
|
|
if ( !ve.isEmptyObject( attributes ) ) {
|
|
element.attributes = ve.copyObject( attributes );
|
|
}
|
|
return element;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Get allowed child node types for a node.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {string[]|null} List of node types allowed as children or null if any type is allowed
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.getChildNodeTypes = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
return this.registry[type].static.childNodeTypes;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Get allowed parent node types for a node.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {string[]|null} List of node types allowed as parents or null if any type is allowed
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.getParentNodeTypes = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
return this.registry[type].static.parentNodeTypes;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Get suggested parent node types for a node.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {string[]|null} List of node types suggested as parents or null if any type is suggested
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.getSuggestedParentNodeTypes = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
return this.registry[type].static.suggestedParentNodeTypes;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Check if a node can have children.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {boolean} The node can have children
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.canNodeHaveChildren = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
// If childNodeTypes is null any child is allowed, if it's an array of at least one element
|
|
// than at least one kind of node is allowed
|
|
var types = this.registry[type].static.childNodeTypes;
|
|
return types === null || ( ve.isArray( types ) && types.length > 0 );
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Check if a node can have children but not content nor be content.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {boolean} The node can have children but not content nor be content
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.canNodeHaveChildrenNotContent = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
return this.canNodeHaveChildren( type ) &&
|
|
!this.registry[type].static.canContainContent &&
|
|
!this.registry[type].static.isContent;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Check if a node has a wrapped element in the document data.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {boolean} Whether the node has a wrapping element
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.isNodeWrapped = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
return this.registry[type].static.isWrapped;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Check if a node can contain content.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {boolean} The node contains content
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.canNodeContainContent = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
return this.registry[type].static.canContainContent;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Check if a node is content.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {boolean} The node is content
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.isNodeContent = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
return this.registry[type].static.isContent;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Check if the node has significant whitespace.
|
|
*
|
|
* Can only be true if canContainContent is also true.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {boolean} The node has significant whitespace
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.doesNodeHaveSignificantWhitespace = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
return this.registry[type].static.hasSignificantWhitespace;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Check if the node stores HTML attributes in the linear model.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {boolean} Whether the node stores HTML attributes
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.doesNodeStoreHtmlAttributes = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
return this.registry[type].static.storeHtmlAttributes;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/**
|
|
* Check if the node handles its own children.
|
|
*
|
|
* @method
|
|
* @param {string} type Node type
|
|
* @returns {boolean} Whether the node handles its own children
|
|
* @throws {Error} Unknown node type
|
|
*/
|
|
ve.dm.NodeFactory.prototype.doesNodeHandleOwnChildren = function ( type ) {
|
|
if ( type in this.registry ) {
|
|
return this.registry[type].static.handlesOwnChildren;
|
|
}
|
|
throw new Error( 'Unknown node type: ' + type );
|
|
};
|
|
|
|
/* Initialization */
|
|
|
|
ve.dm.nodeFactory = new ve.dm.NodeFactory();
|