mediawiki-extensions-Visual.../modules/ve/dm/ve.dm.MetaItem.js

254 lines
8.2 KiB
JavaScript
Raw Normal View History

/*!
* VisualEditor DataModel MetaItem class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* DataModel meta item.
*
* @class
* @abstract
* @extends ve.EventEmitter
* @constructor
* @param {Object} element Reference to element in meta-linmod
*/
ve.dm.MetaItem = function VeDmMetaItem( element ) {
// Parent constructor
ve.EventEmitter.call( this );
// Properties
this.element = element;
this.list = null;
this.offset = null;
this.index = null;
};
/* Inheritance */
ve.inheritClass( ve.dm.MetaItem, ve.EventEmitter );
/* Static members */
// TODO these static properties should really be in a base class or mixin, e.g. "matchable"
/**
* Symbolic name for the meta item class. Must be set to a unique string by every subclass. Must not
* conflict with names of other nodes, annotations, or meta items.
* @static
* @property {string} [static.name=null]
* @inheritable
*/
ve.dm.MetaItem.static.name = null;
/**
* Symbolic name for the group this meta item type will be grouped in in ve.dm.MetaList.
*
* @static
* @property {String} [static.group='misc']
* @inheritable
*/
ve.dm.MetaItem.static.group = 'misc';
/**
* Array of HTML tag names that this meta item should be a match candidate for.
* Empty array means none, null means any.
* For more information about element matching, see ve.dm.ModelRegistry.
* @static
* @property {string[]} static.matchTagNames
* @inheritable
*/
ve.dm.MetaItem.static.matchTagNames = null;
/**
* Array of RDFa types that this meta item should be a match candidate for.
* Empty array means none, null means any.
* For more information about element matching, see ve.dm.ModelRegistry.
* @static
* @property {Array} static.matchRdfaType Array of strings or regular expressions
* @inheritable
*/
ve.dm.MetaItem.static.matchRdfaTypes = null;
/**
* Optional function to determine whether this meta item should match a given element.
* Takes an HTMLElement and returns true or false.
* This function is only called if this meta item has a chance of "winning"; see
* ve.dm.ModelRegistry for more information about element matching.
* If set to null, this property is ignored. Setting this to null is not the same as unconditionally
* returning true, because the presence or absence of a matchFunction affects the node's
* specificity.
*
* NOTE: This function is NOT a method, within this function "this" will not refer to an instance
* of this class (or to anything reasonable, for that matter).
* @static
* @property {Function} static.matchFunction
* @inheritable
*/
ve.dm.MetaItem.static.matchFunction = null;
/**
* Static function to convert a DOM element or set of sibling DOM elements to a meta-linmod
* element for this item type.
*
* This function is only called if this item "won" the matching for the first DOM element, so
* domElements[0] will match this item's matching rule. There is usually only one node in
* domElements[]. Multiple nodes will only be passed if this item supports about groups.
* If there are multiple nodes, the nodes are all adjacent siblings in the same about group
* (i.e. they are grouped together because they have the same value for the about attribute).
*
* Meta-elements can occur anywhere, including places where only content is allowed, so meta items
* generally won't need to worry about the context variables related to content vs. non-content
* and wrapping as long as they return meta-elements from toDataElement().
*
* Note that if this function returns null, the DOM node will be alienated as an alien *node*,
* not an alien *meta item*.
*
* @static
* @method
* @param {HTMLElement[]} domElements DOM elements to convert. Usually only one element
* @param {Object} context Object describing the current state of the converter
* @param {boolean} context.expectingContent Whether this function is expected to return a content element
* @param {boolean} context.inWrapper Whether this element is in a wrapper paragraph generated by the converter;
* can only be true if context.expectingContent is also true
* @param {boolean} context.canCloseWrapper Whether the current wrapper paragraph can be closed;
* can only be true if context.inWrapper is also true
* @returns {Object|null} Meta-linmod element, or null to alienate
*/
ve.dm.MetaItem.static.toDataElement = function ( /*domElements, context*/ ) {
throw new Error( 've.dm.MetaItem subclass must implement toDataElement' );
};
/**
* Static function to convert a meta-linmod element for this item type back to one or more
* DOM elements.
*
* @static
* @method
* @param {Object} Meta-linmod element with a type property and optionally an attributes property
* @returns {HTMLElement[]} DOM elements
*/
ve.dm.MetaItem.static.toDomElements = function ( /*dataElement*/ ) {
throw new Error( 've.dm.MetaItem subclass must implement toDomElements' );
};
/**
* Whether this item supports about grouping. When a DOM element matches an item type that has
* about grouping enabled, the converter will look for adjacent siblings with the same value for
* the about attribute, and ask toDataElement() to produce a single data element for all of those
* DOM nodes combined.
*
* @static
* @property {boolean} static.enableAboutGrouping
* @inheritable
*/
ve.dm.MetaItem.static.enableAboutGrouping = false;
/**
* Whether HTML attributes should be preserved for this item type. If true, the HTML attributes
* of the DOM elements will be stored as attributes in the meta-linmod. The attribute names will be
* html/i/attrName, where i is the index of the DOM element in the domElements array, and attrName
* is the name of the attribute.
*
* This should generally be enabled, except for item types that store their entire HTML in an
* attribute.
*
* @static
* @property {boolean} static.storeHtmlAttributes
* @inheritable
*/
ve.dm.MetaItem.static.storeHtmlAttributes = true;
/* Methods */
/**
* Get the group this meta item belongs to.
* @see ve.dm.MetaItem#static.group
* @returns {string} Group
*/
ve.dm.MetaItem.prototype.getGroup = function () {
return this.constructor.static.group;
};
/**
* Get the metadata element this item represents.
* @returns {Object} Metadata element (by reference)
*/
ve.dm.MetaItem.prototype.getElement = function () {
return this.element;
};
/**
* Get the MetaList this item is attached to.
* @returns {ve.dm.MetaList|null} Reference to the parent list, or null if not attached
*/
ve.dm.MetaItem.prototype.getParentList = function () {
return this.list;
};
/**
* Get this item's offset in the linear model.
*
* This is only known if the item is attached to a MetaList.
*
* @returns {number|null} Offset, or null if not attached
*/
ve.dm.MetaItem.prototype.getOffset = function () {
return this.offset;
};
/**
* Get this item's index in the metadata array at the offset.
*
* This is only known if the item is attached to a MetaList.
*
* @returns {number|null} Index, or null if not attached
*/
ve.dm.MetaItem.prototype.getIndex = function () {
return this.index;
};
/**
* Set the offset. This is used by the parent list to synchronize the item with the document state.
* @param {number} offset New offset
*/
ve.dm.MetaItem.prototype.setOffset = function ( offset ) {
this.offset = offset;
};
/**
* Set the index. This is used by the parent list to synchronize the item with the document state.
* @param {number} index New index
*/
ve.dm.MetaItem.prototype.setIndex = function ( index ) {
this.index = index;
};
/**
* Attach this item to a MetaList.
* @param {ve.dm.MetaList} list Parent list to attach to
* @param {number} offset Offset of this item in the parent list's document
* @param {number} index Index of this item in the metadata array at the offset
*/
ve.dm.MetaItem.prototype.attach = function ( list, offset, index ) {
this.list = list;
this.offset = offset;
this.index = index;
};
/**
* Detach this item from its parent list.
*
* This clears the stored offset and index, unless the item has already been attached to another list.
*
* @param {ve.dm.MetaList} list List to detach from
*/
ve.dm.MetaItem.prototype.detach = function ( list ) {
if ( this.list === list ) {
this.list = null;
this.offset = null;
this.index = null;
}
};