mediawiki-extensions-Visual.../modules/ve/dm/ve.dm.Annotation.js
Catrope a835c03bc1 Change MetaNodes to MetaItems
Rather than meta-things being special kinds of nodes, they are now a
separate class of things (MetaItems) along with Nodes and Annotations.

* Created a generic ve.dm.MetaItem that meta items inherit from.
  There will be actual instances of this class as well in the upcoming
  meta group code.
* Renamed MetaNode to AlienMetaItem, MWMetaNode to MWMetaItem,
  'metaBlock'/'metaInline' to 'alienMeta'
* Created a MetaItemFactory, handle meta items in the ModelRegistry
* Kill ve.dm.Node.static.isMeta, now obsolete

ve.dm.Converter:
* Pass in the MetaItemFactory
* Look up data element types in the ModelRegistry rather than the
  NodeFactory, because they can be either nodes or meta items
* Document createDataElement() and make explicit that modelClass can be
  either a node or a meta item
* Handle meta items in getDataFromDom()
* In getDomFromData(), check the MetaItemFactory as well as the NodeFactory

Change-Id: I893709c6f3aa00f85c1b905b70f9f4e597bdeada
2013-03-14 23:35:50 -07:00

179 lines
5.6 KiB
JavaScript

/*!
* VisualEditor DataModel Annotation class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Generic DataModel annotation.
*
* This is an abstract class, annotations should extend this and call this constructor from their
* constructor. You should not instantiate this class directly.
*
* Annotations in the linear model are instances of subclasses of this class. Subclasses are
* required to have a constructor with the same signature.
*
* this.htmlTagName and this.htmlAttributes are private to the base class, subclasses must not
* use them. Any information from the HTML element that is needed later should be extracted into
* this.data by overriding getAnnotationData(). Subclasses can read from this.data but must not
* write to it directly.
*
* @class
* @constructor
* @param {HTMLElement|Object} [element] HTML element the annotation was converted from, if any, or
* an object to copy into the annotation's data property
*/
ve.dm.Annotation = function VeDmAnnotation( element ) {
this.name = this.constructor.static.name; // Needed for proper hashing
this.data = {};
if ( ve.isPlainObject( element ) ) {
this.data = ve.copyObject( element );
} else if ( element && element.nodeType === Node.ELEMENT_NODE ) {
this.htmlTagName = element.nodeName.toLowerCase();
this.htmlAttributes = ve.getDomAttributes( element );
this.data = this.getAnnotationData( element );
}
};
/* Static properties */
/**
* Object containing static properties.
*
* ve#inheritClass contains special logic to make sure these properties are inherited by subclasses.
* @static
* @property
* @inheritable
*/
ve.dm.Annotation.static = {};
/**
* Symbolic name for the annotation 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.Annotation.static.name = null;
/**
* Array of HTML tag names that the annotation should be a match candidate for.
*
* Empty array means none, null means any.
*
* @see ve.dm.ModelRegistry
*
* @static
* @property {string[]} static.matchTagNames
* @inheritable
*/
ve.dm.Annotation.static.matchTagNames = null;
/**
* Array of RDFa types that the annotation should be a match candidate for.
*
* Empty array means none, null means any.
*
* @see ve.dm.ModelRegistry
*
* @static
* @property {Array} static.matchRdfaType Array of strings or regular expressions
* @inheritable
*/
ve.dm.Annotation.static.matchRdfaTypes = null;
/**
* Optional function to determine whether the annotation should match a given element.
*
* Takes an HTMLElement and returns true or false.
*
* This function is only called if this annotation 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 annotation'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.Annotation.static.matchFunction = null;
/* Methods */
/**
* Get annotation data for the linear model.
*
* Called when building a new annotation from an HTML element.
*
* This annotation data object is completely free-form. It's stored in the linear model, it can be
* manipulated by UI widgets, and you access it as this.data in toHTML() on the way out and in
* renderHTML() for rendering. It is also the ONLY data you can reliably use in those contexts, so
* any information from the HTML element that you'll need later should be extracted into the data
* object here.
*
* @method
* @param {HTMLElement} element HTML element the annotation will represent
* @returns {Object} Annotation data
*/
ve.dm.Annotation.prototype.getAnnotationData = function () {
return {};
};
/**
* Convert the annotation back to HTML for output purposes.
*
* You should only use this.data here, you cannot reliably use any of the other properties.
* The default action is to restore the original HTML element's tag name and attributes (if this
* annotation was created based on an element). If a subclass wants to do this too (this is common),
* it should call its parent's implementation first, then manipulate the return value.
*
* @method
* @returns {Object} Object with 'tag' (tag name) and 'attributes' (object with attribute key/values)
*/
ve.dm.Annotation.prototype.toHTML = function () {
return {
'tag': this.htmlTagName || '',
'attributes': this.htmlAttributes || {}
};
};
/**
* Convert the annotation to HTML for rendering purposes.
*
* By default, this just calls #toHTML, but it may be customized if the rendering should be
* different from the output.
*
* @see #toHTML
*
* @method
* @returns {Object} Object with 'tag' (tag name) and 'attributes' (object with attribute key/values)
*/
ve.dm.Annotation.prototype.renderHTML = function () {
return this.toHTML();
};
/**
* Get a hash of the annotation.
*
* This is a custom hash function for ve#getHash and should not be overridden by subclasses.
*
* @method
* @returns {string} Hash string
*/
ve.dm.Annotation.prototype.getHash = function () {
var keys = [ 'name', 'data' ], obj = {}, i;
for ( i = 0; i < keys.length; i++ ) {
if ( this[keys[i]] !== undefined ) {
obj[keys[i]] = this[keys[i]];
}
}
return ve.getHash( obj );
};