%PDF- %PDF-
| Direktori : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/tests/dm/ |
| Current File : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/tests/dm/ve.dm.example.js |
/*!
* VisualEditor DataModel example data sets.
*
* @copyright 2011-2016 VisualEditor Team and others; see http://ve.mit-license.org
*/
/**
* @class
* @singleton
* @ignore
*/
ve.dm.example = {};
/* Methods */
/**
* Convert arrays of shorthand annotations in a data fragment to AnnotationSets with real
* annotation objects, and wraps the result in a ve.dm.ElementLinearData object.
*
* Shorthand notation for annotations is:
* [ 'a', [ { type: 'link', attributes: { href: '...' } ] ]
*
* The actual storage format has an instance of ve.dm.LinkAnnotation instead of the plain object,
* and an instance of ve.dm.AnnotationSet instead of the array.
*
* @method
* @param {Array} data Linear model data
* @param {ve.dm.IndexValueStore} [store] Index-value store to use, creates one if undefined
* @return {ve.dm.FlatLinearData} Linear data store
* @throws {Error} Example data passed to preprocessAnnotations by reference
*/
ve.dm.example.preprocessAnnotations = function ( data, store ) {
var i, key;
// Sanity check to make sure ve.dm.example data has not been passed in
// by reference. Always use ve#copy.
for ( i in ve.dm.example ) {
if ( data === ve.dm.example[ i ] ) {
throw new Error( 'Example data passed to preprocessAnnotations by reference' );
}
}
function preprocessOriginalDomElements( el ) {
var originalDomElements = el.originalDomElements;
if ( originalDomElements ) {
el.originalDomElementsIndex = store.index( originalDomElements, originalDomElements.map( ve.getNodeHtml ).join( '' ) );
delete el.originalDomElements;
}
}
store = store || new ve.dm.IndexValueStore();
for ( i = 0; i < data.length; i++ ) {
key = data[ i ].annotations ? 'annotations' : 1;
// check for shorthand annotation objects in array
if ( Array.isArray( data[ i ][ key ] ) && data[ i ][ key ][ 0 ].type ) {
data[ i ][ key ].forEach( preprocessOriginalDomElements );
data[ i ][ key ] = ve.dm.example.createAnnotationSet( store, data[ i ][ key ] ).getIndexes();
}
preprocessOriginalDomElements( data[ i ] );
}
return new ve.dm.FlatLinearData( store, data );
};
/**
* Convert real data back to shorthand notation. See #preprocessAnnotations.
*
* Any annotation that has originalDomElements will be shallow-cloned and have
* originalDomElements removed.
*
* @param {Array} data Linear model data. Will be modified.
* @param {ve.dm.IndexValueStore} store Index-value store to resolve annotations in
* @param {boolean} [preserveDomElements] Preserve original DOM elements
* @return {Array} The given `data` parameter.
*/
ve.dm.example.postprocessAnnotations = function ( data, store, preserveDomElements ) {
var i, j, key;
for ( i = 0; i < data.length; i++ ) {
key = data[ i ].annotations ? 'annotations' : 1;
if ( Array.isArray( data[ i ][ key ] ) ) {
data[ i ] = $.extend( Array.isArray( data[ i ] ) ? [] : {}, data[ i ] );
data[ i ][ key ] = new ve.dm.AnnotationSet( store, data[ i ][ key ] ).get();
for ( j = 0; j < data[ i ][ key ].length; j++ ) {
data[ i ][ key ][ j ] = data[ i ][ key ][ j ].element;
if ( !preserveDomElements && data[ i ][ key ][ j ].originalDomElementsIndex !== undefined ) {
// Make a shallow clone and remove originalDomElements from it
data[ i ][ key ][ j ] = $.extend( {}, data[ i ][ key ][ j ] );
delete data[ i ][ key ][ j ].originalDomElementsIndex;
}
}
}
}
return data;
};
/**
* Remove originalDomElements from linear model data.
*
* @param {Array} data Linear model data. Will be modified.
* @return {Array} data parameter
*/
ve.dm.example.removeOriginalDomElements = function ( data ) {
var i, len;
for ( i = 0, len = data.length; i < len; i++ ) {
if ( data[ i ].originalDomElementsIndex !== undefined ) {
delete data[ i ].originalDomElementsIndex;
}
}
return data;
};
/**
* Create an annotation object from shorthand notation.
*
* @method
* @param {Object} annotation Plain object with type and attributes properties
* @param {ve.dm.IndexValueStore} [store] Index value store
* @return {ve.dm.Annotation} Instance of the right ve.dm.Annotation subclass
*/
ve.dm.example.createAnnotation = function ( annotation, store ) {
return ve.dm.annotationFactory.createFromElement( annotation, store );
};
/**
* Create an AnnotationSet from an array of shorthand annotations.
*
* This calls ve.dm.example.createAnnotation() for each element and puts the result in an
* AnnotationSet.
*
* @method
* @param {ve.dm.IndexValueStore} store Index-value store
* @param {Array} annotations Array of annotations in shorthand format
* @return {ve.dm.AnnotationSet}
*/
ve.dm.example.createAnnotationSet = function ( store, annotations ) {
var i;
for ( i = 0; i < annotations.length; i++ ) {
annotations[ i ] = ve.dm.example.createAnnotation( annotations[ i ], store );
}
return new ve.dm.AnnotationSet( store, store.indexes( annotations ) );
};
/* Some common annotations in shorthand format */
ve.dm.example.bold = { type: 'textStyle/bold', attributes: { nodeName: 'b' } };
ve.dm.example.italic = { type: 'textStyle/italic', attributes: { nodeName: 'i' } };
ve.dm.example.underline = { type: 'textStyle/underline', attributes: { nodeName: 'u' } };
ve.dm.example.span = { type: 'textStyle/span', attributes: { nodeName: 'span' } };
ve.dm.example.big = { type: 'textStyle/big', attributes: { nodeName: 'big' } };
ve.dm.example.code = { type: 'textStyle/code', attributes: { nodeName: 'code' } };
ve.dm.example.tt = { type: 'textStyle/code', attributes: { nodeName: 'tt' } };
ve.dm.example.strong = { type: 'textStyle/bold', attributes: { nodeName: 'strong' } };
ve.dm.example.link = function ( href ) {
return { type: 'link', attributes: { href: href } };
};
ve.dm.example.language = function ( lang, dir ) {
return { type: 'meta/language', attributes: { lang: lang, dir: dir } };
};
ve.dm.example.annIndex = function ( tagName, text ) {
var ann = ve.copy( {
b: ve.dm.example.bold,
i: ve.dm.example.italic,
u: ve.dm.example.underline
}[ tagName ] );
ann.originalDomElementsIndex = ve.dm.IndexValueStore.prototype.indexOfValue( null, '<' + tagName + '>' + text + '</' + tagName + '>' );
return ve.dm.IndexValueStore.prototype.indexOfValue( ann );
};
// index = store.indexOfValue( ve.dm.example.bold )
ve.dm.example.boldIndex = 'h49981eab0f8056ff';
ve.dm.example.italicIndex = 'hefd27ef3bf2041dd';
ve.dm.example.underlineIndex = 'hf214c680fbc361da';
ve.dm.example.strongIndex = 'ha5aaf526d1c3af54';
ve.dm.example.domBoldIndex = 'ha17878c4224059d6';
ve.dm.example.domItalicIndex = 'h818fb55eaa1f5676';
ve.dm.example.domUnderlineIndex = 'h6d4db1ae2f34b4b7';
ve.dm.example.inlineSlug = '<span class="ve-ce-branchNode-slug ve-ce-branchNode-inlineSlug"></span>';
ve.dm.example.blockSlug = '<div class="ve-ce-branchNode-slug ve-ce-branchNode-blockSlug"></div>';
/**
* Creates a document from example data.
*
* Defaults to ve.dm.example.data if no name is supplied.
*
* @param {string} [name='data'] Named element of ve.dm.example
* @param {ve.dm.IndexValueStore} [store] A specific index-value store to use, optionally.
* @return {ve.dm.Document} Document
* @throws {Error} Example data not found
*/
ve.dm.example.createExampleDocument = function ( name, store ) {
return ve.dm.example.createExampleDocumentFromObject( name, store, ve.dm.example );
};
/**
* Helper function for ve.dm.createExampleDocument.
*
* @param {string} [name='data'] Named element of ve.dm.example
* @param {ve.dm.IndexValueStore} [store] A specific index-value store to use, optionally.
* @param {Object} object Collection of test documents, keyed by name
* @return {ve.dm.Document} Document
* @throws {Error} Example data not found
*/
ve.dm.example.createExampleDocumentFromObject = function ( name, store, object ) {
name = name || 'data';
if ( object[ name ] === undefined ) {
throw new Error( 'Example data \'' + name + '\' not found' );
}
return ve.dm.example.createExampleDocumentFromData( object[ name ], store );
};
ve.dm.example.createExampleDocumentFromData = function ( data, store ) {
var doc, i;
store = store || new ve.dm.IndexValueStore();
doc = new ve.dm.Document(
ve.dm.example.preprocessAnnotations( ve.copy( data ), store )
);
// HACK internalList isn't populated when creating a document from data
if ( data.internalItems ) {
for ( i = 0; i < data.internalItems.length; i++ ) {
doc.internalList.queueItemHtml(
data.internalItems[ i ].group,
data.internalItems[ i ].key,
data.internalItems[ i ].body
);
}
}
if ( data.internalListNextUniqueNumber ) {
doc.internalList.nextUniqueNumber = data.internalListNextUniqueNumber;
}
doc.buildNodeTree();
return doc;
};
/**
* Looks up a value in a node tree.
*
* @method
* @param {ve.Node} root Root node to lookup from
* @param {...number} [paths] Index path
* @return {ve.Node} Node at given path
*/
ve.dm.example.lookupNode = function ( root ) {
var i,
node = root;
for ( i = 1; i < arguments.length; i++ ) {
node = node.children[ arguments[ i ] ];
}
return node;
};
ve.dm.example.createDomElement = function ( type, attributes ) {
var key,
element = document.createElement( type );
for ( key in attributes ) {
element.setAttribute( key, attributes[ key ] );
}
return element;
};
ve.dm.example.testDir = window.VE_TESTDIR || '.';
ve.dm.example.imgSrc = 'https://upload.wikimedia.org/wikipedia/commons/b/b3/Wikipedia-logo-v2-en.svg';
ve.dm.example.baseUri = 'http://example.org';
ve.dm.example.base = ( function () {
var doc = ve.createDocumentFromHtml( '' ),
node = doc.createElement( 'base' );
node.setAttribute( 'href', ve.dm.example.baseUri );
doc.head.appendChild( node );
return doc;
}() );
ve.dm.example.fullImgSrc = ve.resolveUrl( ve.dm.example.imgSrc, ve.dm.example.base );
ve.dm.example.image = {
html: '<img src="' + ve.dm.example.imgSrc + '" alt="Example" width="100" height="50">',
data: {
type: 'inlineImage',
attributes: {
src: ve.dm.example.imgSrc,
alt: 'Example',
width: 100,
height: 50
}
}
};
ve.dm.example.blockImage = {
html: '<figure class="ve-align-right"><img src="' + ve.dm.example.imgSrc + '" alt="Example" width="100" height="50"><figcaption>caption</figcaption></figure>',
data: [
{
type: 'blockImage',
attributes: {
src: ve.dm.example.imgSrc,
alt: 'Example',
width: 100,
height: 50,
originalClasses: 've-align-right',
unrecognizedClasses: [],
align: 'right'
}
},
{ type: 'imageCaption' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'c', 'a', 'p', 't', 'i', 'o', 'n',
{ type: '/paragraph' },
{ type: '/imageCaption' },
{ type: '/blockImage' }
]
};
/**
* Serialized HTML.
*
* This is what the parser will emit.
* TODO remove some of the <p>s here to test automatic wrapping
*/
ve.dm.example.html =
'<h1>a<b>b</b><i>c</i></h1>' +
'<table>' +
// implicit <tbody>
'<tr>' +
'<td>' +
'<p>d</p>' +
'<ul>' +
'<li>' +
'<p>e</p>' +
'<ul>' +
'<li>' +
'<p>f</p>' +
'</li>' +
'</ul>' +
'</li>' +
'</ul>' +
'<ol>' +
'<li>' +
'<p>g</p>' +
'</li>' +
'</ol>' +
'</td>' +
'</tr>' +
'</table>' +
'<pre>h' + ve.dm.example.image.html + 'i</pre>' +
'<dl>' +
'<dt>' +
'<p>j</p>' +
'</dt>' +
'<dd>' +
'<p>k</p>' +
'</dd>' +
'</dl>' +
'<p>l</p>' +
'<p>m</p>';
/**
* The offset path of the result of getNodeAndOffset for each offset
*
* @see ve.getOffsetPath
*/
ve.dm.example.offsetPaths = [
[ 0, 0, 0 ],
[ 0, 0, 0 ],
[ 0, 0, 1 ],
[ 0, 1, 0, 1 ],
[ 0, 2, 0, 1 ],
[ 0, 2, 0, 1 ],
[ 0, 2, 0, 1 ],
[ 0, 2, 0, 1 ],
[ 0, 2, 0, 1 ],
[ 0, 2, 0, 1 ],
// 10
[ 1, 0, 0, 0, 0, 0, 0 ],
[ 1, 0, 0, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 0, 0, 0 ],
[ 1, 0, 0, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 0, 0, 1 ],
// 20
[ 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0 ],
[ 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 2, 0, 0, 0, 0 ],
// 30
[ 1, 0, 0, 0, 2, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 2, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 2, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 2, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 2, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 2, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 2, 0, 0, 0, 1 ],
[ 1, 0, 0, 0, 2, 0, 0, 0, 1 ],
[ 2, 0, 0 ],
[ 2, 0, 1 ],
// 40
null,
[ 2, 2, 0 ],
[ 2, 2, 1 ],
[ 2, 2, 1 ],
[ 2, 2, 1 ],
[ 2, 2, 1 ],
[ 3, 0, 0, 0, 0 ],
[ 3, 0, 0, 0, 1 ],
[ 3, 0, 0, 0, 1 ],
[ 3, 0, 0, 0, 1 ],
// 50
[ 3, 0, 0, 0, 1 ],
[ 3, 1, 0, 0, 0 ],
[ 3, 1, 0, 0, 1 ],
[ 3, 1, 0, 0, 1 ],
[ 3, 1, 0, 0, 1 ],
[ 3, 1, 0, 0, 1 ],
[ 4, 0, 0 ],
[ 4, 0, 1 ],
[ 4, 0, 1 ],
[ 5, 0, 0 ],
// 60
[ 5, 0, 1 ]
];
/*
* Linear data.
*
* This is what we convert serialized HTML from the parser into so we can work with it more easily.
*
* There are three types of components in content data:
*
* {string} Plain text character
*
* {Array} Annotated character
* 0: {string} Character
* 1: {Object} List of references to immutable annotation objects, keyed by JSON
* serializations of their values (hashes)
*
* {Object} Opening or closing structural element
* type: {string} Symbolic node type name, if closing element first character will be "/"
* [attributes]: {Object} List of symbolic attribute name and literal value pairs
*/
ve.dm.example.data = [
// 0 - Beginning of heading
{ type: 'heading', attributes: { level: 1 } },
// 1 - Plain "a"
'a',
// 2 - Bold "b"
[ 'b', [ ve.dm.example.bold ] ],
// 3 - Italic "c"
[ 'c', [ ve.dm.example.italic ] ],
// 4 - End of heading
{ type: '/heading' },
// 5 - Beginning of table
{ type: 'table' },
// 6 - Beginning of body
{ type: 'tableSection', attributes: { style: 'body' } },
// 7 - Beginning of row
{ type: 'tableRow' },
// 8 - Beginning of cell
{ type: 'tableCell', attributes: { style: 'data' } },
// 9 - Beginning of paragraph
{ type: 'paragraph' },
// 10 - Plain "d"
'd',
// 11 - End of paragraph
{ type: '/paragraph' },
// 12 - Beginning of bullet list
{ type: 'list', attributes: { style: 'bullet' } },
// 13 - Beginning of list item
{ type: 'listItem' },
// 14 - Beginning of paragraph
{ type: 'paragraph' },
// 15 - Plain "e"
'e',
// 16 - End of paragraph
{ type: '/paragraph' },
// 17 - Beginning of nested bullet list
{ type: 'list', attributes: { style: 'bullet' } },
// 18 - Beginning of nested bullet list item
{ type: 'listItem' },
// 19 - Beginning of paragraph
{ type: 'paragraph' },
// 20 - Plain "f"
'f',
// 21 - End of paragraph
{ type: '/paragraph' },
// 22 - End of nested bullet list item
{ type: '/listItem' },
// 23 - End of nested bullet list
{ type: '/list' },
// 24 - End of bullet list item
{ type: '/listItem' },
// 25 - End of bullet list
{ type: '/list' },
// 26 - Beginning of numbered list
{ type: 'list', attributes: { style: 'number' } },
// 27 - Beginning of numbered list item
{ type: 'listItem' },
// 28 - Beginning of paragraph
{ type: 'paragraph' },
// 29 - Plain "g"
'g',
// 30 - End of paragraph
{ type: '/paragraph' },
// 31 - End of item
{ type: '/listItem' },
// 32 - End of list
{ type: '/list' },
// 33 - End of cell
{ type: '/tableCell' },
// 34 - End of row
{ type: '/tableRow' },
// 35 - End of body
{ type: '/tableSection' },
// 36 - End of table
{ type: '/table' },
// 37 - Beginning of preformatted
{ type: 'preformatted' },
// 38 - Plain "h"
'h',
// 39 - Beginning of inline image
ve.dm.example.image.data,
// 40 - End of inline image
{ type: '/inlineImage' },
// 41 - Plain "i"
'i',
// 42 - End of preformatted
{ type: '/preformatted' },
// 43 - Beginning of definition list
{ type: 'definitionList' },
// 44 - Beginning of definition list term item
{ type: 'definitionListItem', attributes: { style: 'term' } },
// 45 - Beginning of paragraph
{ type: 'paragraph' },
// 46 - Plain "j"
'j',
// 47 - End of paragraph
{ type: '/paragraph' },
// 48 - End of definition list term item
{ type: '/definitionListItem' },
// 49 - Beginning of definition list definition item
{ type: 'definitionListItem', attributes: { style: 'definition' } },
// 50 - Beginning of paragraph
{ type: 'paragraph' },
// 51 - Plain "k"
'k',
// 52 - End of paragraph
{ type: '/paragraph' },
// 53 - End of definition list definition item
{ type: '/definitionListItem' },
// 54 - End of definition list
{ type: '/definitionList' },
// 55 - Beginning of paragraph
{ type: 'paragraph' },
// 56 - Plain "l"
'l',
// 57 - End of paragraph
{ type: '/paragraph' },
// 58 - Beginning of paragraph
{ type: 'paragraph' },
// 59 - Plain "m"
'm',
// 60 - End of paragraph
{ type: '/paragraph' },
// 61 - Beginning of internalList
{ type: 'internalList' },
// 62 - End of internalList
{ type: '/internalList' }
// 63 - End of document
];
ve.dm.example.alienData = [
// 0
{ type: 'alienBlock', originalDomElements: $( '<foobar />' ).toArray() },
{ type: '/alienBlock' },
// 2
{ type: 'paragraph' },
'a',
// 4
{ type: 'alienInline', originalDomElements: $( '<foobar />' ).toArray() },
{ type: '/alienInline' },
// 6
'b',
{ type: '/paragraph' },
// 8
{ type: 'alienBlock', originalDomElements: $( '<foobar />' ).toArray() },
{ type: '/alienBlock' },
// 10
{ type: 'internalList' },
{ type: '/internalList' }
];
ve.dm.example.alienWithEmptyData = [
// 0
{ type: 'paragraph' },
{ type: '/paragraph' },
// 2
{ type: 'paragraph' },
'a',
// 4
{ type: 'alienInline', originalDomElements: $( '<foobar />' ).toArray() },
{ type: '/alienInline' },
// 6
{ type: '/paragraph' },
// 7
{ type: 'internalList' },
{ type: '/internalList' }
];
ve.dm.example.internalData = [
// 0
{ type: 'paragraph' },
'F', 'o', 'o',
{ type: '/paragraph' },
// 5
{ type: 'internalList' },
// 6
{ type: 'internalItem' },
// 7
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'B', 'a', 'r',
{ type: '/paragraph' },
// 12
{ type: '/internalItem' },
// 13
{ type: 'internalItem' },
// 14
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'B', 'a', 'z',
{ type: '/paragraph' },
// 19
{ type: '/internalItem' },
// 20
{ type: '/internalList' },
// 21
{ type: 'paragraph' },
'Q', 'u', 'u', 'x',
{ type: '/paragraph' }
// 27
];
ve.dm.example.internalData.internalItems = [
{ group: 'test', key: 'bar', body: 'Bar' },
{ group: 'test', key: 'baz', body: 'Baz' }
];
ve.dm.example.withMeta = [
{
type: 'alienMeta',
originalDomElements: $( '<!-- No content conversion -->' ).toArray()
},
{ type: '/alienMeta' },
{
type: 'alienMeta',
originalDomElements: $( '<meta property="foo" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: 'paragraph' },
'F',
'o',
'o',
{
type: 'alienMeta',
originalDomElements: $( '<link rel="bar" href="baz" />' ).toArray()
},
{ type: '/alienMeta' },
'B',
'a',
'r',
{
type: 'alienMeta',
originalDomElements: $( '<meta property="foo" content="bar" />' ).toArray()
},
{ type: '/alienMeta' },
'B',
'a',
{
type: 'alienMeta',
originalDomElements: $( '<!-- inline -->' ).toArray()
},
{ type: '/alienMeta' },
'z',
{ type: '/paragraph' },
{
type: 'alienMeta',
originalDomElements: $( '<meta property="bar" content="baz" />' ).toArray()
},
{ type: '/alienMeta' },
{
type: 'alienMeta',
originalDomElements: $( '<!--barbaz-->' ).toArray()
},
{ type: '/alienMeta' },
{
type: 'alienMeta',
originalDomElements: $( '<link rel="foofoo" href="barbar" />' ).toArray()
},
{ type: '/alienMeta' },
{
type: 'alienMeta',
originalDomElements: $( '<meta typeof=bazquux" data-foo="foobar" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: 'internalList' },
{ type: '/internalList' }
];
ve.dm.example.withMetaPlainData = [
{ type: 'paragraph' },
'F',
'o',
'o',
'B',
'a',
'r',
'B',
'a',
'z',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
];
ve.dm.example.withMetaMetaData = [
[
{
type: 'alienMeta',
originalDomElements: $( '<!-- No content conversion -->' ).toArray()
},
{
type: 'alienMeta',
originalDomElements: $( '<meta property="foo" />' ).toArray()
}
],
undefined,
undefined,
undefined,
[
{
type: 'alienMeta',
originalDomElements: $( '<link rel="bar" href="baz" />' ).toArray()
}
],
undefined,
undefined,
[
{
type: 'alienMeta',
originalDomElements: $( '<meta property="foo" content="bar" />' ).toArray()
}
],
undefined,
[
{
type: 'alienMeta',
originalDomElements: $( '<!-- inline -->' ).toArray()
}
],
undefined,
[
{
type: 'alienMeta',
originalDomElements: $( '<meta property="bar" content="baz" />' ).toArray()
},
{
type: 'alienMeta',
originalDomElements: $( '<!--barbaz-->' ).toArray()
},
{
type: 'alienMeta',
originalDomElements: $( '<link rel="foofoo" href="barbar" />' ).toArray()
},
{
type: 'alienMeta',
originalDomElements: $( '<meta typeof=bazquux" data-foo="foobar" />' ).toArray()
}
],
undefined,
undefined
];
ve.dm.example.listWithMeta = [
// 0 - Beginning of list
{
type: 'alienMeta',
originalDomElements: $( '<meta property="one" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: 'list' },
// 1 - Beginning of first list item
{
type: 'alienMeta',
originalDomElements: $( '<meta property="two" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: 'listItem', attributes: { styles: [ 'bullet' ] } },
// 2 - Beginning of paragraph
{
type: 'alienMeta',
originalDomElements: $( '<meta property="three" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: 'paragraph' },
// 3 - Plain "a"
{
type: 'alienMeta',
originalDomElements: $( '<meta property="four" />' ).toArray()
},
{ type: '/alienMeta' },
'a',
// 4 - End of paragraph
{
type: 'alienMeta',
originalDomElements: $( '<meta property="five" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: '/paragraph' },
// 5 - End of first list item
{
type: 'alienMeta',
originalDomElements: $( '<meta property="six" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: '/listItem' },
// 6 - Beginning of second list item
{
type: 'alienMeta',
originalDomElements: $( '<meta property="seven" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: 'listItem', attributes: { styles: [ 'bullet' ] } },
// 7 - Beginning of paragraph
{
type: 'alienMeta',
originalDomElements: $( '<meta property="eight" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: 'paragraph' },
// 8 - Plain "b"
{
type: 'alienMeta',
originalDomElements: $( '<meta property="nine" />' ).toArray()
},
{ type: '/alienMeta' },
'b',
// 9 - End of paragraph
{
type: 'alienMeta',
originalDomElements: $( '<meta property="ten" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: '/paragraph' },
// 10 - End of second list item
{
type: 'alienMeta',
originalDomElements: $( '<meta property="eleven" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: '/listItem' },
// 11 - End of list
{
type: 'alienMeta',
originalDomElements: $( '<meta property="twelve" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: '/list' },
// 12 - Trailing metadata
{
type: 'alienMeta',
originalDomElements: $( '<meta property="thirteen" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: 'internalList' },
{ type: '/internalList' }
];
ve.dm.example.mergedCellsHtml =
'<table>' +
'<tr>' +
'<td>1</td><td>2</td><td>3</td><td rowspan="3">4</td><td>5</td><td>6</td>' +
'</tr>' +
'<tr>' +
'<td>7</td><td colspan="2">8</td><td rowspan="4">9</td><td>10</td>' +
'</tr>' +
'<tr>' +
'<td>11</td><td>12</td><td>13</td><td>14</td>' +
'</tr>' +
'<tr>' +
'<td>15</td><td rowspan="3" colspan="3">16</td><td>17</td>' +
'</tr>' +
'<tr>' +
'<td>18</td><td>19</td>' +
'</tr>' +
'<tr>' +
'<td>20</td><td colspan="2">21</td>' +
'</tr>' +
'<tr>' +
'<td>22</td><td>23</td><td>24</td><td>25</td><td>26</td><td>27</td>' +
'</tr>' +
'</table>';
ve.dm.example.mergedCells = [
{ type: 'table' },
{ type: 'tableSection', attributes: { style: 'body' } },
{ type: 'tableRow' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'2',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'3',
{ type: '/paragraph' },
{ type: '/tableCell' },
{
type: 'tableCell',
attributes: {
style: 'data',
originalRowspan: '3',
rowspan: 3
}
},
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'4',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'5',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'6',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: 'tableRow' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'7',
{ type: '/paragraph' },
{ type: '/tableCell' },
{
type: 'tableCell',
attributes: {
style: 'data',
colspan: 2,
originalColspan: '2'
}
},
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'8',
{ type: '/paragraph' },
{ type: '/tableCell' },
{
type: 'tableCell',
attributes: {
style: 'data',
originalRowspan: '4',
rowspan: 4
}
},
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'9',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1', '0',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: 'tableRow' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1', '1',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1', '2',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1', '3',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1', '4',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: 'tableRow' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1', '5',
{ type: '/paragraph' },
{ type: '/tableCell' },
{
type: 'tableCell',
attributes: {
style: 'data',
colspan: 3,
originalColspan: '3',
originalRowspan: '3',
rowspan: 3
}
},
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1', '6',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1', '7',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: 'tableRow' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1', '8',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1', '9',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: 'tableRow' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'2', '0',
{ type: '/paragraph' },
{ type: '/tableCell' },
{
type: 'tableCell',
attributes: {
style: 'data',
colspan: 2,
originalColspan: '2'
}
},
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'2', '1',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: 'tableRow' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'2', '2',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'2', '3',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'2', '4',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'2', '5',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'2', '6',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'2', '7',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: '/tableSection' },
{ type: '/table' },
{ type: 'internalList' },
{ type: '/internalList' }
];
ve.dm.example.complexTableHtml = '<table><caption>Foo</caption><thead><tr><th rowspan="">Bar</th></tr></thead>' +
'<tfoot><tr><td colspan=2>Baz</td></tr></tfoot><tbody><tr><td rowspan="02">Quux</td><td colspan="2 garbage">Whee</td></tr></tbody></table>';
ve.dm.example.complexTable = [
{ type: 'table' },
{ type: 'tableCaption' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'F',
'o',
'o',
{ type: '/paragraph' },
{ type: '/tableCaption' },
{ type: 'tableSection', attributes: { style: 'header' } },
{ type: 'tableRow' },
{
type: 'tableCell',
attributes: {
style: 'header',
originalRowspan: ''
}
},
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'B',
'a',
'r',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: '/tableSection' },
{ type: 'tableSection', attributes: { style: 'footer' } },
{ type: 'tableRow' },
{
type: 'tableCell',
attributes: {
style: 'data',
colspan: 2,
originalColspan: '2'
}
},
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'B',
'a',
'z',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: '/tableSection' },
{ type: 'tableSection', attributes: { style: 'body' } },
{ type: 'tableRow' },
{
type: 'tableCell',
attributes: {
style: 'data',
rowspan: 2,
originalRowspan: '02'
}
},
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'Q',
'u',
'u',
'x',
{ type: '/paragraph' },
{ type: '/tableCell' },
{
type: 'tableCell',
attributes: {
style: 'data',
originalColspan: '2 garbage'
}
},
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'W',
'h',
'e',
'e',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: '/tableSection' },
{ type: '/table' },
{ type: 'internalList' },
{ type: '/internalList' }
];
ve.dm.example.inlineAtEdges = [
// 0
{ type: 'paragraph' },
// 1
ve.dm.example.image.data,
// 2
{ type: '/inlineImage' },
// 3
'F', 'o', 'o',
// 6
{ type: 'alienInline', originalDomElements: $( '<foobar />' ).toArray() },
// 7
{ type: '/alienInline' },
// 8
{ type: '/paragraph' },
// 9
{ type: 'internalList' },
{ type: '/internalList' }
];
ve.dm.example.annotatedTableHtml =
'<table>' +
'<tr><td><b>Foo</b></td><td><strong>Bar</strong></td><td><i>Baz</i></td></tr>' +
'<tr><td><b><i>Quux</i></b></td><td><strong>Whee</strong></td><td><u>Yay</u></td></tr>' +
'</table>';
ve.dm.example.annotatedTable = [
{ type: 'table' },
{
type: 'tableSection',
attributes: { style: 'body' }
},
{ type: 'tableRow' },
{
type: 'tableCell',
attributes: { style: 'data' }
},
{
type: 'paragraph',
internal: { generated: 'wrapper' }
},
[ 'F', [ ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.bold ] ],
{ type: '/paragraph' },
{ type: '/tableCell' },
{
type: 'tableCell',
attributes: { style: 'data' }
},
{
type: 'paragraph',
internal: { generated: 'wrapper' }
},
[ 'B', [ ve.dm.example.strong ] ],
[ 'a', [ ve.dm.example.strong ] ],
[ 'r', [ ve.dm.example.strong ] ],
{ type: '/paragraph' },
{ type: '/tableCell' },
{
type: 'tableCell',
attributes: { style: 'data' }
},
{
type: 'paragraph',
internal: { generated: 'wrapper' }
},
[ 'B', [ ve.dm.example.italic ] ],
[ 'a', [ ve.dm.example.italic ] ],
[ 'z', [ ve.dm.example.italic ] ],
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: 'tableRow' },
{
type: 'tableCell',
attributes: { style: 'data' }
},
{
type: 'paragraph',
internal: { generated: 'wrapper' }
},
[ 'Q', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ 'u', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ 'u', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ 'x', [ ve.dm.example.bold, ve.dm.example.italic ] ],
{ type: '/paragraph' },
{ type: '/tableCell' },
{
type: 'tableCell',
attributes: { style: 'data' }
},
{
type: 'paragraph',
internal: { generated: 'wrapper' }
},
[ 'W', [ ve.dm.example.strong ] ],
[ 'h', [ ve.dm.example.strong ] ],
[ 'e', [ ve.dm.example.strong ] ],
[ 'e', [ ve.dm.example.strong ] ],
{ type: '/paragraph' },
{ type: '/tableCell' },
{
type: 'tableCell',
attributes: { style: 'data' }
},
{
type: 'paragraph',
internal: { generated: 'wrapper' }
},
[ 'Y', [ ve.dm.example.underline ] ],
[ 'a', [ ve.dm.example.underline ] ],
[ 'y', [ ve.dm.example.underline ] ],
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: '/tableSection' },
{ type: '/table' },
{ type: 'internalList' },
{ type: '/internalList' }
];
ve.dm.example.emptyBranch = [
{ type: 'table' },
{ type: '/table' },
{ type: 'internalList' },
{ type: '/internalList' }
];
/**
* Sample content data index.
*
* This is part of what a ve.dm.DocumentFragment generates when given linear data.
*
* (21) branch nodes
* (01) document node
* (01) heading node
* (01) table node
* (01) tableRow node
* (01) tableCell node
* (06) paragraph nodes
* (03) list nodes
* (03) listItem nodes
* (01) preformatted node
* (01) definitionList node
* (02) definitionListItem nodes
* (10) leaf nodes
* (09) text nodes
* (01) image node
*/
ve.dm.example.tree = new ve.dm.DocumentNode( [
// Heading with "abc"
new ve.dm.HeadingNode( ve.dm.example.data[ 0 ], [ new ve.dm.TextNode( 3 ) ] ),
new ve.dm.TableNode( ve.dm.example.data[ 5 ], [
new ve.dm.TableSectionNode( ve.dm.example.data[ 6 ], [
new ve.dm.TableRowNode( ve.dm.example.data[ 7 ], [
new ve.dm.TableCellNode( ve.dm.example.data[ 8 ], [
// Paragraph with "d"
new ve.dm.ParagraphNode( ve.dm.example.data[ 9 ], [ new ve.dm.TextNode( 1 ) ] ),
new ve.dm.ListNode( ve.dm.example.data[ 12 ], [
// 1st level bullet list item with "e"
new ve.dm.ListItemNode( ve.dm.example.data[ 13 ], [
new ve.dm.ParagraphNode(
ve.dm.example.data[ 14 ],
[ new ve.dm.TextNode( 1 ) ]
),
new ve.dm.ListNode( ve.dm.example.data[ 17 ], [
// 2nd level bullet list item with "f"
new ve.dm.ListItemNode( ve.dm.example.data[ 18 ], [
new ve.dm.ParagraphNode(
ve.dm.example.data[ 19 ],
[ new ve.dm.TextNode( 1 ) ]
)
] )
] )
] )
] ),
new ve.dm.ListNode( ve.dm.example.data[ 26 ], [
// Numbered list item with "g"
new ve.dm.ListItemNode( ve.dm.example.data[ 27 ], [
new ve.dm.ParagraphNode(
ve.dm.example.data[ 28 ],
[ new ve.dm.TextNode( 1 ) ]
)
] )
] )
] )
] )
] )
] ),
// Preformatted with "h[example.png]i"
new ve.dm.PreformattedNode( ve.dm.example.data[ 37 ], [
new ve.dm.TextNode( 1 ),
new ve.dm.InlineImageNode( ve.dm.example.data[ 39 ] ),
new ve.dm.TextNode( 1 )
] ),
new ve.dm.DefinitionListNode( ve.dm.example.data[ 43 ], [
// Definition list term item with "j"
new ve.dm.DefinitionListItemNode( ve.dm.example.data[ 44 ], [
new ve.dm.ParagraphNode( ve.dm.example.data[ 45 ], [ new ve.dm.TextNode( 1 ) ] )
] ),
// Definition list definition item with "k"
new ve.dm.DefinitionListItemNode( ve.dm.example.data[ 49 ], [
new ve.dm.ParagraphNode( ve.dm.example.data[ 50 ], [ new ve.dm.TextNode( 1 ) ] )
] )
] ),
new ve.dm.ParagraphNode( ve.dm.example.data[ 55 ], [ new ve.dm.TextNode( 1 ) ] ),
new ve.dm.ParagraphNode( ve.dm.example.data[ 58 ], [ new ve.dm.TextNode( 1 ) ] ),
new ve.dm.InternalListNode( ve.dm.example.data[ 61 ] )
] );
ve.dm.example.domToDataCases = {
'paragraph with plain text': {
body: '<p>abc</p>',
data: [
{ type: 'paragraph' },
'a',
'b',
'c',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'annotated text with bold, italic, underline formatting': {
body: '<p><b>a</b><i>b</i><u>c</u></p>',
data: [
{ type: 'paragraph' },
[ 'a', [ ve.dm.example.bold ] ],
[ 'b', [ ve.dm.example.italic ] ],
[ 'c', [ ve.dm.example.underline ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
ceHtml: '<p class="ve-ce-branchNode ve-ce-contentBranchNode ve-ce-paragraphNode">' +
'<b class="ve-ce-textStyleAnnotation ve-ce-boldAnnotation">a</b>' +
'<i class="ve-ce-textStyleAnnotation ve-ce-italicAnnotation">b</i>' +
'<u class="ve-ce-textStyleAnnotation ve-ce-underlineAnnotation">c</u>' +
'</p>'
},
'annotation from data': {
data: [
{ type: 'paragraph' },
// Annotation without nodeName
[ 'a', [ { type: 'textStyle/bold' } ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<p><b>a</b></p>'
},
'equivalent annotations': {
body: '<p><code>a</code>b<tt>c</tt>d<code>e</code><tt>f</tt></p>',
data: [
{ type: 'paragraph' },
[ 'a', [ ve.dm.example.code ] ],
'b',
[ 'c', [ ve.dm.example.tt ] ],
'd',
[ 'e', [ ve.dm.example.code ] ],
[ 'f', [ ve.dm.example.tt ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<p><code>a</code>b<tt>c</tt>d<code>ef</code></p>'
},
'additive annotations': {
body: '<p><big>a<big>b</big>c</big><b>d<b>e</b>f</b></p>',
data: [
{ type: 'paragraph' },
[ 'a', [ ve.dm.example.big ] ],
[ 'b', [ ve.dm.example.big, ve.dm.example.big ] ],
[ 'c', [ ve.dm.example.big ] ],
[ 'd', [ ve.dm.example.bold ] ],
[ 'e', [ ve.dm.example.bold, ve.dm.example.bold ] ],
[ 'f', [ ve.dm.example.bold ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'additive annotations overlapping other annotations': {
body: '<p><i><big>a<big><b>b</b></big><b>c</b></big></i></p>',
data: [
{ type: 'paragraph' },
[ 'a', [ ve.dm.example.italic, ve.dm.example.big ] ],
[ 'b', [ ve.dm.example.italic, ve.dm.example.big, ve.dm.example.big, ve.dm.example.bold ] ],
[ 'c', [ ve.dm.example.italic, ve.dm.example.big, ve.dm.example.bold ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'annotations normalised on import': {
body: '<p><em>Foo</em><strong>bar</strong></p>',
fromClipboard: true,
data: [
{ type: 'paragraph' },
[ 'F', [ ve.dm.example.italic ] ],
[ 'o', [ ve.dm.example.italic ] ],
[ 'o', [ ve.dm.example.italic ] ],
[ 'b', [ ve.dm.example.bold ] ],
[ 'a', [ ve.dm.example.bold ] ],
[ 'r', [ ve.dm.example.bold ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
normalizedBody: '<p><i>Foo</i><b>bar</b></p>'
},
'language annotation': {
body: '<p>' +
'<span lang="en">ten</span>' +
'<span lang="fr" dir="ltr">dix</span>' +
'<span lang="cy" dir="ltr">deg</span>' +
'<span dir="rtl">12</span>' +
'<span dir="RtL">34</span>' +
'</p>',
data: [
{ type: 'paragraph' },
[ 't', [ ve.dm.example.language( 'en', null ) ] ],
[ 'e', [ ve.dm.example.language( 'en', null ) ] ],
[ 'n', [ ve.dm.example.language( 'en', null ) ] ],
[ 'd', [ ve.dm.example.language( 'fr', 'ltr' ) ] ],
[ 'i', [ ve.dm.example.language( 'fr', 'ltr' ) ] ],
[ 'x', [ ve.dm.example.language( 'fr', 'ltr' ) ] ],
[ 'd', [ ve.dm.example.language( 'cy', 'ltr' ) ] ],
[ 'e', [ ve.dm.example.language( 'cy', 'ltr' ) ] ],
[ 'g', [ ve.dm.example.language( 'cy', 'ltr' ) ] ],
[ '1', [ ve.dm.example.language( null, 'rtl' ) ] ],
[ '2', [ ve.dm.example.language( null, 'rtl' ) ] ],
[ '3', [ ve.dm.example.language( null, 'RtL' ) ] ],
[ '4', [ ve.dm.example.language( null, 'RtL' ) ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
ceHtml: '<p class="ve-ce-branchNode ve-ce-contentBranchNode ve-ce-paragraphNode">' +
'<span class="ve-ce-languageAnnotation ve-ce-bidi-isolate" lang="en" title="visualeditor-languageannotation-description">ten</span>' +
'<span class="ve-ce-languageAnnotation ve-ce-bidi-isolate" lang="fr" dir="ltr" title="visualeditor-languageannotation-description">dix</span>' +
'<span class="ve-ce-languageAnnotation ve-ce-bidi-isolate" lang="cy" dir="ltr" title="visualeditor-languageannotation-description">deg</span>' +
'<span class="ve-ce-languageAnnotation ve-ce-bidi-isolate" dir="rtl" title="visualeditor-languageannotation-description-with-dir">12</span>' +
'<span class="ve-ce-languageAnnotation ve-ce-bidi-isolate" dir="RtL" title="visualeditor-languageannotation-description-with-dir">34</span>' +
'</p>'
},
'other textStyle annotations': {
body: '<p>' +
'<abbr>a</abbr>' +
'<var>b</var>' +
'<kbd>c</kbd>' +
'<q>d</q>' +
'<samp>e</samp>' +
'<time>f</time>' +
'<dfn>g</dfn>' +
'<mark>h</mark>' +
'<font>i</font>' +
'</p>',
data: [
{ type: 'paragraph' },
[ 'a', [ { type: 'textStyle/abbreviation', attributes: { nodeName: 'abbr' } } ] ],
[ 'b', [ { type: 'textStyle/variable', attributes: { nodeName: 'var' } } ] ],
[ 'c', [ { type: 'textStyle/userInput', attributes: { nodeName: 'kbd' } } ] ],
[ 'd', [ { type: 'textStyle/quotation', attributes: { nodeName: 'q' } } ] ],
[ 'e', [ { type: 'textStyle/codeSample', attributes: { nodeName: 'samp' } } ] ],
[ 'f', [ { type: 'textStyle/datetime', attributes: { nodeName: 'time' } } ] ],
[ 'g', [ { type: 'textStyle/definition', attributes: { nodeName: 'dfn' } } ] ],
[ 'h', [ { type: 'textStyle/highlight', attributes: { nodeName: 'mark' } } ] ],
[ 'i', [ { type: 'textStyle/font', attributes: { nodeName: 'font' } } ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
ceHtml: '<p class="ve-ce-branchNode ve-ce-contentBranchNode ve-ce-paragraphNode">' +
'<abbr class="ve-ce-textStyleAnnotation ve-ce-abbreviationAnnotation">a</abbr>' +
'<var class="ve-ce-textStyleAnnotation ve-ce-variableAnnotation">b</var>' +
'<kbd class="ve-ce-textStyleAnnotation ve-ce-userInputAnnotation">c</kbd>' +
'<q class="ve-ce-textStyleAnnotation ve-ce-quotationAnnotation">d</q>' +
'<samp class="ve-ce-textStyleAnnotation ve-ce-codeSampleAnnotation">e</samp>' +
'<time class="ve-ce-textStyleAnnotation ve-ce-datetimeAnnotation">f</time>' +
'<dfn class="ve-ce-textStyleAnnotation ve-ce-definitionAnnotation">g</dfn>' +
'<mark class="ve-ce-textStyleAnnotation ve-ce-highlightAnnotation">h</mark>' +
'<font class="ve-ce-textStyleAnnotation ve-ce-fontAnnotation">i</font>' +
'</p>'
},
'strip leading whitespace in non-whitespace preserving nodes': {
// T53462/T142132
data: [
{ type: 'paragraph' },
' ', 'f', 'o', 'o',
{ type: '/paragraph' },
{ type: 'paragraph' },
' ', '\t', ' ', '\t', 'b', 'a', 'r',
{ type: '/paragraph' },
{ type: 'heading', attributes: { level: 2 } },
' ', ' ', 'b', 'a', 'z',
{ type: '/heading' },
{ type: 'preformatted' },
' ', '\t', 'q', 'u', 'u', 'x',
{ type: '/preformatted' },
{ type: 'internalList' },
{ type: '/internalList' }
],
normalizedBody: '<p>foo</p><p>bar</p><h2>baz</h2><pre> \tquux</pre>',
clipboardBody: '<p> foo</p><p> \t \tbar</p><h2> baz</h2><pre> \tquux</pre>'
},
image: {
body: ve.dm.example.image.html,
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
ve.dm.example.image.data,
{ type: '/inlineImage' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
ceHtml: '<p class="ve-ce-branchNode ve-ce-contentBranchNode ve-ce-paragraphNode ve-ce-generated-wrapper">' +
ve.dm.example.inlineSlug +
'<img class="ve-ce-leafNode ve-ce-focusableNode ve-ce-imageNode ve-ce-inlineImageNode" contenteditable="false" alt="Example"' +
' src="https://upload.wikimedia.org/wikipedia/commons/b/b3/Wikipedia-logo-v2-en.svg" style="width: 100px; height: 50px;">' +
ve.dm.example.inlineSlug +
'</p>'
},
'block image': {
body: ve.dm.example.blockImage.html,
data: ve.dm.example.blockImage.data.concat( [
{ type: 'internalList' },
{ type: '/internalList' }
] ),
ceHtml: ve.dm.example.blockSlug +
'<figure class="ve-ce-branchNode ve-ce-focusableNode ve-ce-imageNode ve-ce-blockImageNode" contenteditable="false">' +
'<img src="https://upload.wikimedia.org/wikipedia/commons/b/b3/Wikipedia-logo-v2-en.svg" alt="Example" style="width: 100px; height: 50px;">' +
'<figcaption class="ve-ce-branchNode">' +
'<p class="ve-ce-branchNode ve-ce-contentBranchNode ve-ce-paragraphNode ve-ce-generated-wrapper">caption</p>' +
'</figcaption>' +
'</figure>' +
ve.dm.example.blockSlug
},
'paragraph with alienInline inside': {
body: '<p>a<foobar class="foo">b</foobar>c</p>',
data: [
{ type: 'paragraph' },
'a',
{
type: 'alienInline',
originalDomElements: $( '<foobar class="foo">b</foobar>' ).toArray()
},
{ type: '/alienInline' },
'c',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'paragraphs with an alienBlock between them': {
body: '<p>abc</p><div rel="ve:Alien">abc</div><p>def</p>',
data: [
{ type: 'paragraph' },
'a',
'b',
'c',
{ type: '/paragraph' },
{ type: 'alienBlock', originalDomElements: $( '<div rel="ve:Alien">abc</div>' ).toArray() },
{ type: '/alienBlock' },
{ type: 'paragraph' },
'd',
'e',
'f',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'annotated inline nodes': {
body: '<p>a<b><foobar class="foo">b</foobar><i><foobar class="bar">c</foobar></i></b>' +
'<i><br/>d</i>e</p>',
data: [
{ type: 'paragraph' },
'a',
{
type: 'alienInline',
originalDomElements: $( '<foobar class="foo">b</foobar>' ).toArray(),
annotations: [ ve.dm.example.bold ]
},
{ type: '/alienInline' },
{
type: 'alienInline',
originalDomElements: $( '<foobar class="bar">c</foobar>' ).toArray(),
annotations: [ ve.dm.example.bold, ve.dm.example.italic ]
},
{ type: '/alienInline' },
{
type: 'break',
annotations: [ ve.dm.example.italic ]
},
{ type: '/break' },
[ 'd', [ ve.dm.example.italic ] ],
'e',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'annotated comments': {
body: '<p><b><!--foo-->bar<!--baz--></b></p>',
data: [
{ type: 'paragraph' },
{
type: 'comment',
annotations: [ ve.dm.example.bold ],
attributes: {
text: 'foo'
}
},
{ type: '/comment' },
[ 'b', [ ve.dm.example.bold ] ],
[ 'a', [ ve.dm.example.bold ] ],
[ 'r', [ ve.dm.example.bold ] ],
{
type: 'comment',
annotations: [ ve.dm.example.bold ],
attributes: {
text: 'baz'
}
},
{ type: '/comment' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
clipboardBody: '<p><b><span rel="ve:Comment" data-ve-comment="foo"> </span>bar<span rel="ve:Comment" data-ve-comment="baz"> </span></b></p>',
ceHtml: '<p class="ve-ce-branchNode ve-ce-contentBranchNode ve-ce-paragraphNode">' +
'<b class="ve-ce-textStyleAnnotation ve-ce-boldAnnotation">' +
ve.dm.example.inlineSlug +
'<span class="ve-ce-leafNode ve-ce-focusableNode ve-ce-commentNode" contenteditable="false"></span>' +
'bar' +
'<span class="ve-ce-leafNode ve-ce-focusableNode ve-ce-commentNode" contenteditable="false"></span>' +
'</b>' +
ve.dm.example.inlineSlug +
'</p>'
},
'annotated metadata': {
body: '<p><b><meta />bar<meta /></b></p>',
data: [
{ type: 'paragraph' },
{
type: 'alienMeta',
annotations: [ ve.dm.example.bold ],
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
[ 'b', [ ve.dm.example.bold ] ],
[ 'a', [ ve.dm.example.bold ] ],
[ 'r', [ ve.dm.example.bold ] ],
{
type: 'alienMeta',
annotations: [ ve.dm.example.bold ],
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'annotated metadata in a wrapper': {
body: '<b><meta />bar<meta />quux<meta /></b>',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
{
type: 'alienMeta',
annotations: [ ve.dm.example.bold ],
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
[ 'b', [ ve.dm.example.bold ] ],
[ 'a', [ ve.dm.example.bold ] ],
[ 'r', [ ve.dm.example.bold ] ],
{
type: 'alienMeta',
annotations: [ ve.dm.example.bold ],
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
[ 'q', [ ve.dm.example.bold ] ],
[ 'u', [ ve.dm.example.bold ] ],
[ 'u', [ ve.dm.example.bold ] ],
[ 'x', [ ve.dm.example.bold ] ],
{
type: 'alienMeta',
annotations: [ ve.dm.example.bold ],
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'annotated element metadata in a wrapper with content': {
body: '<b><link />foo<link /></b>',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
{
type: 'alienMeta',
annotations: [ ve.dm.example.bold ],
originalDomElements: $( '<link />' ).toArray()
},
{ type: '/alienMeta' },
[ 'f', [ ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.bold ] ],
{
type: 'alienMeta',
annotations: [ ve.dm.example.bold ],
originalDomElements: $( '<link />' ).toArray()
},
{ type: '/alienMeta' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'metadata in a wrapper followed by annotated text': {
body: 'Foo<meta /><b>Baz</b>',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'F',
'o',
'o',
{
type: 'alienMeta',
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
[ 'B', [ ve.dm.example.bold ] ],
[ 'a', [ ve.dm.example.bold ] ],
[ 'z', [ ve.dm.example.bold ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping of bare content': {
body: 'abc',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'a',
'b',
'c',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping of bare content with inline node': {
body: '1<br/>2',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1',
{ type: 'break' },
{ type: '/break' },
'2',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping of bare content starting with inline node': {
body: ve.dm.example.image.html + '12',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
ve.dm.example.image.data,
{ type: '/inlineImage' },
'1',
'2',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping of bare content with inline alien': {
body: '1<foobar class="bar">baz</foobar>2',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1',
{
type: 'alienInline',
originalDomElements: $( '<foobar class="bar">baz</foobar>' ).toArray()
},
{ type: '/alienInline' },
'2',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping of bare content with block alien': {
body: '1<div rel="ve:Alien" class="bar">baz</div>2',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1',
{ type: '/paragraph' },
{
type: 'alienBlock',
originalDomElements: $( '<div rel="ve:Alien" class="bar">baz</div>' ).toArray()
},
{ type: '/alienBlock' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'2',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping of bare content starting with inline alien': {
body: '<foobar class="bar">Foo</foobar>Bar',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
{
type: 'alienInline',
originalDomElements: $( '<foobar class="bar">Foo</foobar>' ).toArray()
},
{ type: '/alienInline' },
'B',
'a',
'r',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping of bare content ending with inline alien': {
body: 'Foo<foobar class="bar">Bar</foobar>',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'F',
'o',
'o',
{
type: 'alienInline',
originalDomElements: $( '<foobar class="bar">Bar</foobar>' ).toArray()
},
{ type: '/alienInline' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping of bare content with about group': {
body: '1<foobar about="#vet1">foo</foobar><foobar about="#vet1">bar</foobar>2',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'1',
{
type: 'alienInline',
originalDomElements: $( '<foobar about="#vet1">foo</foobar><foobar about="#vet1">bar</foobar>' ).toArray()
},
{ type: '/alienInline' },
'2',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping of bare content between structural nodes': {
body: '<table></table>abc<table></table>',
data: [
{ type: 'table' },
{ type: '/table' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'a',
'b',
'c',
{ type: '/paragraph' },
{ type: 'table' },
{ type: '/table' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping of bare content between paragraphs': {
body: '<p>abc</p>def<p></p>',
data: [
{ type: 'paragraph' },
'a',
'b',
'c',
{ type: '/paragraph' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'd',
'e',
'f',
{ type: '/paragraph' },
{ type: 'paragraph' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'wrapping prevents empty list items': {
body: '<ul><li></li></ul>',
data: [
{ type: 'list', attributes: { style: 'bullet' } },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'empty' } },
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'empty document': {
body: '',
data: [
{ type: 'paragraph', internal: { generated: 'empty' } },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'empty document with meta': {
body: '<meta />',
data: [
{
type: 'alienMeta',
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
{ type: 'paragraph', internal: { generated: 'empty' } },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'empty document with comment': {
body: '<!-- comment -->',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
{
type: 'comment',
attributes: {
text: ' comment '
}
},
{ type: '/comment' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
clipboardBody: '<span rel="ve:Comment" data-ve-comment=" comment "> </span>'
},
'empty document with content added by the editor': {
data: [
{ type: 'paragraph', internal: { generated: 'empty' } },
'F',
'o',
'o',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
normalizedBody: '<p>Foo</p>'
},
'empty list item with content added by the editor': {
data: [
{ type: 'list', attributes: { style: 'bullet' } },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'empty' } },
'F',
'o',
'o',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
],
normalizedBody: '<ul><li><p>Foo</p></li></ul>'
},
'slug paragraph added between two nodes that had whitespace': {
data: [
{ type: 'table', internal: { whitespace: [ undefined, undefined, undefined, '\n' ] } },
{ type: '/table' },
{ type: 'paragraph', internal: { generated: 'slug' } },
{ type: '/paragraph' },
{ type: 'table', internal: { whitespace: [ '\n' ] } },
{ type: '/table' },
{ type: 'internalList' },
{ type: '/internalList' }
],
normalizedBody: '<table></table>\n<table></table>'
},
'example document': {
body: ve.dm.example.html,
data: ve.dm.example.data
},
'empty annotation': {
body: '<p>Foo<span id="anchorTarget"></span>Bar</p>',
data: [
{ type: 'paragraph' },
'F', 'o', 'o',
{
type: 'alienMeta',
originalDomElements: $( '<span id="anchorTarget"></span>' ).toArray()
},
{ type: '/alienMeta' },
'B', 'a', 'r',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'empty annotation in wrapper paragraph': {
body: 'Foo<span id="anchorTarget"></span>Bar',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'F', 'o', 'o',
{
type: 'alienMeta',
originalDomElements: $( '<span id="anchorTarget"></span>' ).toArray()
},
{ type: '/alienMeta' },
'B', 'a', 'r',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'nested empty annotation': {
body: '<p>Foo<i><b><u></u></b></i>Bar</p>',
data: [
{ type: 'paragraph' },
'F', 'o', 'o',
{
type: 'alienMeta',
originalDomElements: $( '<i><b><u></u></b></i>' ).toArray()
},
{ type: '/alienMeta' },
'B', 'a', 'r',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'empty annotation inside nonempty annotation': {
body: '<p><i>Foo<b></b></i></p>',
data: [
{ type: 'paragraph' },
[ 'F', [ ve.dm.example.italic ] ],
[ 'o', [ ve.dm.example.italic ] ],
[ 'o', [ ve.dm.example.italic ] ],
{
type: 'alienMeta',
originalDomElements: $( '<b></b>' ).toArray(),
annotations: [ ve.dm.example.italic ]
},
{ type: '/alienMeta' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'empty annotation with comment': {
body: '<p>Foo<b><!-- Bar --></b>Baz</p>',
data: [
{ type: 'paragraph' },
'F', 'o', 'o',
{
type: 'comment',
annotations: [ ve.dm.example.bold ],
attributes: {
text: ' Bar '
}
},
{ type: '/comment' },
'B', 'a', 'z',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
clipboardBody: '<p>Foo<b><span rel="ve:Comment" data-ve-comment=" Bar "> </span></b>Baz</p>'
},
'empty annotation with metadata': {
body: '<p>Foo<b><meta /></b>Baz</p>',
data: [
{ type: 'paragraph' },
'F', 'o', 'o',
{
type: 'alienMeta',
originalDomElements: $( '<b><meta /></b>' ).toArray()
},
{ type: '/alienMeta' },
'B', 'a', 'z',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'adjacent identical annotations': {
body:
'<p><b>Foo</b><b>bar</b><strong>baz</strong></p>' +
'<p><a href="quux">Foo</a><a href="quux">bar</a><a href="whee">baz</a></p>',
data: [
{ type: 'paragraph' },
[ 'F', [ ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.bold ] ],
[ 'b', [ ve.dm.example.bold ] ],
[ 'a', [ ve.dm.example.bold ] ],
[ 'r', [ ve.dm.example.bold ] ],
[ 'b', [ ve.dm.example.strong ] ],
[ 'a', [ ve.dm.example.strong ] ],
[ 'z', [ ve.dm.example.strong ] ],
{ type: '/paragraph' },
{ type: 'paragraph' },
[ 'F', [ ve.dm.example.link( 'quux' ) ] ],
[ 'o', [ ve.dm.example.link( 'quux' ) ] ],
[ 'o', [ ve.dm.example.link( 'quux' ) ] ],
[ 'b', [ ve.dm.example.link( 'quux' ) ] ],
[ 'a', [ ve.dm.example.link( 'quux' ) ] ],
[ 'r', [ ve.dm.example.link( 'quux' ) ] ],
[ 'b', [ ve.dm.example.link( 'whee' ) ] ],
[ 'a', [ ve.dm.example.link( 'whee' ) ] ],
[ 'z', [ ve.dm.example.link( 'whee' ) ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody:
'<p><b>Foobarbaz</b></p>' +
'<p><a href="quux">Foobar</a><a href="whee">baz</a></p>'
},
'list item with space followed by link': {
body: '<ul><li><p> <a href="Foobar">bar</a></p></li></ul>',
head: '<base href="http://example.com/Foo" />',
data: [
{ type: 'list', attributes: { style: 'bullet' } },
{ type: 'listItem' },
{ type: 'paragraph', internal: { whitespace: [ undefined, ' ' ] } },
[ 'b', [ ve.dm.example.link( 'Foobar' ) ] ],
[ 'a', [ ve.dm.example.link( 'Foobar' ) ] ],
[ 'r', [ ve.dm.example.link( 'Foobar' ) ] ],
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace between unwrapped inline nodes': {
body: '<foobar>c</foobar> <foobar>d</foobar>\n<foobar>e</foobar>',
data: [
{
type: 'paragraph',
internal: {
generated: 'wrapper'
}
},
{
type: 'alienInline',
originalDomElements: $( '<foobar>c</foobar>' ).toArray()
},
{ type: '/alienInline' },
' ',
{
type: 'alienInline',
originalDomElements: $( '<foobar>d</foobar>' ).toArray()
},
{ type: '/alienInline' },
'\n',
{
type: 'alienInline',
originalDomElements: $( '<foobar>e</foobar>' ).toArray()
},
{ type: '/alienInline' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation in headings': {
body: '<h2>Foo</h2><h2> Bar</h2><h2>Baz </h2><h2> Quux </h2>',
data: [
{ type: 'heading', attributes: { level: 2 } },
'F',
'o',
'o',
{ type: '/heading' },
{
type: 'heading',
attributes: { level: 2 },
internal: { whitespace: [ undefined, ' ' ] }
},
'B',
'a',
'r',
{ type: '/heading' },
{
type: 'heading',
attributes: { level: 2 },
internal: { whitespace: [ undefined, undefined, ' ' ] }
},
'B',
'a',
'z',
{ type: '/heading' },
{
type: 'heading',
attributes: { level: 2 },
internal: { whitespace: [ undefined, ' ', ' ' ] }
},
'Q',
'u',
'u',
'x',
{ type: '/heading' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation in list items': {
body: '<ul><li>Foo</li><li> Bar</li><li>Baz </li><li> Quux </li></ul>',
data: [
{ type: 'list', attributes: { style: 'bullet' } },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'F',
'o',
'o',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: 'listItem', internal: { whitespace: [ undefined, ' ' ] } },
{ type: 'paragraph', internal: { whitespace: [ ' ' ], generated: 'wrapper' } },
'B',
'a',
'r',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: 'listItem', internal: { whitespace: [ undefined, undefined, ' ' ] } },
{ type: 'paragraph', internal: { whitespace: [ undefined, undefined, undefined, ' ' ], generated: 'wrapper' } },
'B',
'a',
'z',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: 'listItem', internal: { whitespace: [ undefined, ' ', ' ' ] } },
{
type: 'paragraph',
internal: { whitespace: [ ' ', undefined, undefined, ' ' ], generated: 'wrapper' }
},
'Q',
'u',
'u',
'x',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation with annotations': {
body: '<p> <i> Foo </i> </p>',
data: [
{
type: 'paragraph',
internal: { whitespace: [ undefined, ' ', ' ' ] }
},
[ ' ', [ ve.dm.example.italic ] ],
[ ' ', [ ve.dm.example.italic ] ],
[ 'F', [ ve.dm.example.italic ] ],
[ 'o', [ ve.dm.example.italic ] ],
[ 'o', [ ve.dm.example.italic ] ],
[ ' ', [ ve.dm.example.italic ] ],
[ ' ', [ ve.dm.example.italic ] ],
[ ' ', [ ve.dm.example.italic ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<p> <i>Foo</i> </p>'
},
'outer whitespace preservation in a list with bare text and a wrapper paragraph': {
body: '\n<ul>\n\n<li>\n\n\nBa re\n\n\n\n</li>\n\n\n\n\n<li>\t<p>\t\tP\t\t\t</p>\t\t\t\t</li>\t\n</ul>\t\n\t\n',
data: [
{ type: 'list', attributes: { style: 'bullet' }, internal: { whitespace: [ '\n', '\n\n', '\t\n', '\t\n\t\n' ] } },
{ type: 'listItem', internal: { whitespace: [ '\n\n', '\n\n\n', '\n\n\n\n', '\n\n\n\n\n' ] } },
{ type: 'paragraph', internal: { generated: 'wrapper', whitespace: [ '\n\n\n', undefined, undefined, '\n\n\n\n' ] } },
'B',
'a',
' ',
'r',
'e',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: 'listItem', internal: { whitespace: [ '\n\n\n\n\n', '\t', '\t\t\t\t', '\t\n' ] } },
{ type: 'paragraph', internal: { whitespace: [ '\t', '\t\t', '\t\t\t', '\t\t\t\t' ] } },
'P',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
],
innerWhitespace: [ '\n', '\t\n\t\n' ]
},
'outer whitespace preservation in a list with bare text and a sublist': {
body: '<ul>\n<li>\n\nBa re\n\n\n<ul>\n\n\n\n<li> <p> P </p> </li>\t</ul>\t\t</li>\t\t\t</ul>',
data: [
{ type: 'list', attributes: { style: 'bullet' }, internal: { whitespace: [ undefined, '\n', '\t\t\t' ] } },
{ type: 'listItem', internal: { whitespace: [ '\n', '\n\n', '\t\t', '\t\t\t' ] } },
{ type: 'paragraph', internal: { generated: 'wrapper', whitespace: [ '\n\n', undefined, undefined, '\n\n\n' ] } },
'B',
'a',
' ',
'r',
'e',
{ type: '/paragraph' },
{ type: 'list', attributes: { style: 'bullet' }, internal: { whitespace: [ '\n\n\n', '\n\n\n\n', '\t', '\t\t' ] } },
{ type: 'listItem', internal: { whitespace: [ '\n\n\n\n', ' ', ' ', '\t' ] } },
{ type: 'paragraph', internal: { whitespace: [ ' ', ' ', ' ', ' ' ] } },
'P',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation leaves non-edge content whitespace alone': {
body: '<p> A B <b> C\t</b>\t\tD\t\t\t</p>\nE\n\nF\n\n\n<b>\n\n\n\nG </b> H ',
data: [
{ type: 'paragraph', internal: { whitespace: [ undefined, ' ', '\t\t\t', '\n' ] } },
'A',
' ',
' ',
'B',
' ',
' ',
' ',
[ ' ', [ ve.dm.example.bold ] ],
[ ' ', [ ve.dm.example.bold ] ],
[ ' ', [ ve.dm.example.bold ] ],
[ ' ', [ ve.dm.example.bold ] ],
[ 'C', [ ve.dm.example.bold ] ],
[ '\t', [ ve.dm.example.bold ] ],
'\t',
'\t',
'D',
{ type: '/paragraph' },
{ type: 'paragraph', internal: { generated: 'wrapper', whitespace: [ '\n', undefined, undefined, ' ' ] } },
'E',
'\n',
'\n',
'F',
'\n',
'\n',
'\n',
[ '\n', [ ve.dm.example.bold ] ],
[ '\n', [ ve.dm.example.bold ] ],
[ '\n', [ ve.dm.example.bold ] ],
[ '\n', [ ve.dm.example.bold ] ],
[ 'G', [ ve.dm.example.bold ] ],
[ ' ', [ ve.dm.example.bold ] ],
' ',
' ',
'H',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
innerWhitespace: [ undefined, ' ' ],
fromDataBody: '<p> A B <b>C</b>\t\t\tD\t\t\t</p>\nE\n\nF\n\n\n\n\n\n\n<b>G</b> H '
},
'whitespace preservation with non-edge content whitespace with nested annotations': {
body: '<p> A B <b> C\t<i>\t\tD\t\t\t</i>\t\t\t\tE\n</b>\n\nF\n\n\n</p>',
data: [
{ type: 'paragraph', internal: { whitespace: [ undefined, ' ', '\n\n\n' ] } },
'A',
' ',
' ',
'B',
' ',
' ',
' ',
[ ' ', [ ve.dm.example.bold ] ],
[ ' ', [ ve.dm.example.bold ] ],
[ ' ', [ ve.dm.example.bold ] ],
[ ' ', [ ve.dm.example.bold ] ],
[ 'C', [ ve.dm.example.bold ] ],
[ '\t', [ ve.dm.example.bold ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ 'D', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold ] ],
[ '\t', [ ve.dm.example.bold ] ],
[ '\t', [ ve.dm.example.bold ] ],
[ '\t', [ ve.dm.example.bold ] ],
[ 'E', [ ve.dm.example.bold ] ],
[ '\n', [ ve.dm.example.bold ] ],
'\n',
'\n',
'F',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<p> A B <b>C\t\t\t<i>D</i>\t\t\t\t\t\t\tE</b>\n\n\nF\n\n\n</p>'
},
'whitespace preservation with tightly nested annotations': {
body: '<p> A B <b><i>\t\tC\t\t\t</i></b>\n\nD\n\n\n</p>',
data: [
{ type: 'paragraph', internal: { whitespace: [ undefined, ' ', '\n\n\n' ] } },
'A',
' ',
' ',
'B',
' ',
' ',
' ',
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ 'C', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
'\n',
'\n',
'D',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<p> A B \t\t<b><i>C</i></b>\t\t\t\n\nD\n\n\n</p>'
},
'whitespace preservation with nested annotations with whitespace on the left side': {
body: '<p> A B <b>\n\t<i>\t\tC\t\t\t</i></b>\n\nD\n\n\n</p>',
data: [
{ type: 'paragraph', internal: { whitespace: [ undefined, ' ', '\n\n\n' ] } },
'A',
' ',
' ',
'B',
' ',
' ',
' ',
[ '\n', [ ve.dm.example.bold ] ],
[ '\t', [ ve.dm.example.bold ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ 'C', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
'\n',
'\n',
'D',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<p> A B \n\t\t\t<b><i>C</i></b>\t\t\t\n\nD\n\n\n</p>'
},
'whitespace preservation with nested annotations with whitespace on the right side': {
body: '<p> A B <b><i>\t\tC\t\t\t</i>\n\t</b>\n\nD\n\n\n</p>',
data: [
{ type: 'paragraph', internal: { whitespace: [ undefined, ' ', '\n\n\n' ] } },
'A',
' ',
' ',
'B',
' ',
' ',
' ',
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ 'C', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\n', [ ve.dm.example.bold ] ],
[ '\t', [ ve.dm.example.bold ] ],
'\n',
'\n',
'D',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<p> A B \t\t<b><i>C</i></b>\t\t\t\n\t\n\nD\n\n\n</p>'
},
'whitespace preservation with aliens': {
body: ' <div rel="ve:Alien"> <br> </div> <p>\tFoo\t\t<foobar>\t\t\tBar\t\t\t\t</foobar>\nBaz\n\n<foobar>\n\n\nQuux\n\n\n\n</foobar> \tWhee \n</p>\t\n<div rel="ve:Alien">\n\tYay \t </div> \n ',
data: [
{
type: 'alienBlock',
originalDomElements: $( '<div rel="ve:Alien"> <br> </div>' ).toArray(),
internal: {
whitespace: [ ' ', undefined, undefined, ' ' ]
}
},
{ type: '/alienBlock' },
{ type: 'paragraph', internal: { whitespace: [ ' ', '\t', ' \n', '\t\n' ] } },
'F',
'o',
'o',
'\t',
'\t',
{ type: 'alienInline', originalDomElements: $( '<foobar>\t\t\tBar\t\t\t\t</foobar>' ).toArray() },
{ type: '/alienInline' },
'\n',
'B',
'a',
'z',
'\n',
'\n',
{
type: 'alienInline',
originalDomElements: $( '<foobar>\n\n\nQuux\n\n\n\n</foobar>' ).toArray()
},
{ type: '/alienInline' },
' ',
'\t',
'W',
'h',
'e',
'e',
{ type: '/paragraph' },
{
type: 'alienBlock',
originalDomElements: $( '<div rel="ve:Alien">\n\tYay \t </div>' ).toArray(),
internal: {
whitespace: [ '\t\n', undefined, undefined, ' \n ' ]
}
},
{ type: '/alienBlock' },
{ type: 'internalList' },
{ type: '/internalList' }
],
innerWhitespace: [ ' ', ' \n ' ]
},
'whitespace preservation not triggered inside <pre>': {
body: '\n<pre>\n\n\nFoo\n\n\nBar\n\n\n\n</pre>\n\n\n\n\n',
data: [
{ type: 'preformatted', internal: { whitespace: [ '\n', undefined, undefined, '\n\n\n\n\n' ] } },
'\n',
'\n',
'F',
'o',
'o',
'\n',
'\n',
'\n',
'B',
'a',
'r',
'\n',
'\n',
'\n',
'\n',
{ type: '/preformatted' },
{ type: 'internalList' },
{ type: '/internalList' }
],
innerWhitespace: [ '\n', '\n\n\n\n\n' ]
},
'whitespace preservation in table cell starting with text and ending with annotation': {
body: '<table><tbody><tr><td>Foo <b>Bar</b></td></tr></tbody></table>',
data: [
{ type: 'table' },
{ type: 'tableSection', attributes: { style: 'body' } },
{ type: 'tableRow' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'F',
'o',
'o',
' ',
[ 'B', [ ve.dm.example.bold ] ],
[ 'a', [ ve.dm.example.bold ] ],
[ 'r', [ ve.dm.example.bold ] ],
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: '/tableSection' },
{ type: '/table' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation with wrapped text and metas': {
body: '<meta /> <meta />\nFoo',
data: [
{
type: 'alienMeta',
internal: {
whitespace: [
undefined,
undefined,
undefined,
' '
]
},
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
{
type: 'alienMeta',
internal: {
whitespace: [
' ',
undefined,
undefined,
'\n'
]
},
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
{
type: 'paragraph',
internal: {
whitespace: [
'\n'
],
generated: 'wrapper'
}
},
'F',
'o',
'o',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation with wrapped text and comments': {
body: '<!-- Foo --> <!-- Bar -->\nFoo',
data: [
{
type: 'paragraph',
internal: {
generated: 'wrapper'
}
},
{
type: 'comment',
attributes: {
text: ' Foo '
}
},
{ type: '/comment' },
' ',
{
type: 'comment',
attributes: {
text: ' Bar '
}
},
{ type: '/comment' },
'\n',
'F',
'o',
'o',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
clipboardBody: '<span rel="ve:Comment" data-ve-comment=" Foo "> </span> <span rel="ve:Comment" data-ve-comment=" Bar "> </span>\nFoo'
},
'whitespace preservation with comments at end of wrapper paragraph': {
body: '<ul><li> bar<!-- baz -->quux </li></ul>',
data: [
{ type: 'list', attributes: { style: 'bullet' } },
{
type: 'listItem',
internal: {
whitespace: [
undefined,
' ',
' '
]
}
},
{
type: 'paragraph',
internal: {
generated: 'wrapper',
whitespace: [
' ',
undefined,
undefined,
' '
]
}
},
'b', 'a', 'r',
{
type: 'comment',
attributes: {
text: ' baz '
}
},
{ type: '/comment' },
'q', 'u', 'u', 'x',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
],
clipboardBody: '<ul><li> bar<span rel="ve:Comment" data-ve-comment=" baz "> </span>quux </li></ul>'
},
'whitespace preservation with metadata and space at end of wrapper paragraph': {
body: '<ul><li> bar<meta />quux </li></ul>',
data: [
{ type: 'list', attributes: { style: 'bullet' } },
{
type: 'listItem',
internal: {
whitespace: [
undefined,
' ',
' '
]
}
},
{
type: 'paragraph',
internal: {
generated: 'wrapper',
whitespace: [
' ',
undefined,
undefined,
' '
]
}
},
'b', 'a', 'r',
{
type: 'alienMeta',
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
'q', 'u', 'u', 'x',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation with meta at end of wrapper paragraph': {
body: '<ul><li> bar<meta /> </li></ul>',
data: [
{ type: 'list', attributes: { style: 'bullet' } },
{
type: 'listItem',
internal: {
whitespace: [
undefined,
' ',
' '
]
}
},
{
type: 'paragraph',
internal: {
generated: 'wrapper',
whitespace: [
' '
]
}
},
'b', 'a', 'r',
{ type: '/paragraph' },
{
type: 'alienMeta',
originalDomElements: $( '<meta />' ).toArray(),
internal: {
whitespace: [
undefined,
undefined,
undefined,
' '
]
}
},
{ type: '/alienMeta' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation with multiple metas at end of wrapper paragraph': {
body: '<ul><li> foo <meta /> <meta /> </li></ul>',
data: [
{ type: 'list', attributes: { style: 'bullet' } },
{
type: 'listItem',
internal: {
whitespace: [
undefined,
' ',
' '
]
}
},
{
type: 'paragraph',
internal: {
generated: 'wrapper',
whitespace: [
' ',
undefined,
undefined,
' '
]
}
},
'f', 'o', 'o',
{ type: '/paragraph' },
{
type: 'alienMeta',
originalDomElements: $( '<meta />' ).toArray(),
internal: {
whitespace: [
' ',
undefined,
undefined,
' '
]
}
},
{ type: '/alienMeta' },
{
type: 'alienMeta',
originalDomElements: $( '<meta />' ).toArray(),
internal: {
whitespace: [
' ',
undefined,
undefined,
' '
]
}
},
{ type: '/alienMeta' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation with comment at start or end of element': {
body: '<p> <!-- foo -->bar<!-- baz --> </p>',
data: [
{
type: 'paragraph',
internal: {
whitespace: [
undefined,
' ',
' '
]
}
},
{
type: 'comment',
attributes: {
text: ' foo '
}
},
{ type: '/comment' },
'b', 'a', 'r',
{
type: 'comment',
attributes: {
text: ' baz '
}
},
{ type: '/comment' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
clipboardBody: '<p> <span rel="ve:Comment" data-ve-comment=" foo "> </span>bar<span rel="ve:Comment" data-ve-comment=" baz "> </span> </p>'
},
'whitespace surrounding metadata in a wrapper': {
body: '<b>Foo</b> <meta />\n<i>Bar</i>',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
[ 'F', [ ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.bold ] ],
' ',
{
type: 'alienMeta',
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
'\n',
[ 'B', [ ve.dm.example.italic ] ],
[ 'a', [ ve.dm.example.italic ] ],
[ 'r', [ ve.dm.example.italic ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace surrounding metadata in a wrapper followed by inline node': {
body: '<b>Foo</b> <meta />\n<span rel="ve:Alien"></span>',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
[ 'F', [ ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.bold ] ],
' ',
{
type: 'alienMeta',
originalDomElements: $( '<meta />' ).toArray()
},
{ type: '/alienMeta' },
'\n',
{
type: 'alienInline',
originalDomElements: $( '<span rel="ve:Alien"></span>' ).toArray()
},
{ type: '/alienInline' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation in empty branch node': {
body: '<table>\n\n</table>',
data: [
{ type: 'table', internal: { whitespace: [ undefined, '\n\n' ] } },
{ type: '/table' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation in empty list item': {
body: '<ul><li>\n\t</li></ul>',
data: [
{ type: 'list', attributes: { style: 'bullet' } },
{ type: 'listItem', internal: { whitespace: [ undefined, '\n\t' ] } },
{ type: 'paragraph', internal: { generated: 'empty', whitespace: [ '\n\t' ] } },
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation in body with only plain text': {
body: ' Hello\n\t',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper', whitespace: [ ' ', undefined, undefined, '\n\t' ] } },
'H', 'e', 'l', 'l', 'o',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
innerWhitespace: [ ' ', '\n\t' ]
},
'whitespace preservation in empty body': {
body: '\n\t',
data: [
{ type: 'paragraph', internal: { generated: 'empty', whitespace: [ '\n\t' ] } },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
innerWhitespace: [ '\n\t', undefined ]
},
'mismatching whitespace data is ignored': {
data: [
{ type: 'list', attributes: { style: 'bullet' }, internal: { whitespace: [ ' ', ' ', ' ', ' ' ] } },
{ type: 'listItem', internal: { whitespace: [ ' ', ' ', ' ', ' ' ] } },
{ type: 'paragraph', internal: { whitespace: [ ' ', '\t', '\n', ' ' ] } },
'A',
{ type: '/paragraph' },
{ type: 'paragraph', internal: { whitespace: [ ' ' ] } },
'B',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: 'listItem', internal: { whitespace: [ undefined, ' ', '\n' ] } },
{ type: 'paragraph', internal: { generated: 'empty' } },
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
],
innerWhitespace: [ '\t', '\n' ],
normalizedBody: '<ul><li><p>\tA\n</p> <p>B</p></li><li></li></ul>'
},
'whitespace is trimmed from the edges of annotations when serializing': {
body: '<p>A <b> B <i> C\t</i> </b><u>\nD\t</u></p>',
data: [
{ type: 'paragraph' },
'A',
' ',
[ ' ', [ ve.dm.example.bold ] ],
[ 'B', [ ve.dm.example.bold ] ],
[ ' ', [ ve.dm.example.bold ] ],
[ ' ', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ 'C', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ '\t', [ ve.dm.example.bold, ve.dm.example.italic ] ],
[ ' ', [ ve.dm.example.bold ] ],
[ '\n', [ ve.dm.example.underline ] ],
[ 'D', [ ve.dm.example.underline ] ],
[ '\t', [ ve.dm.example.underline ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<p>A <b>B <i>C</i></b>\t \n<u>D</u>\t</p>'
},
'annotation whitespace trimming does not create empty annotations': {
body: '<p>A<b> </b> <b>B</b></p>',
data: [
{ type: 'paragraph' },
'A',
[ ' ', [ ve.dm.example.bold ] ],
' ',
[ 'B', [ ve.dm.example.bold ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<p>A <b>B</b></p>'
},
'order of nested annotations is preserved': {
body: '<p><b><u><i>Foo</i></u></b></p>',
data: [
{ type: 'paragraph' },
[
'F',
[
ve.dm.example.bold,
ve.dm.example.underline,
ve.dm.example.italic
]
],
[
'o',
[
ve.dm.example.bold,
ve.dm.example.underline,
ve.dm.example.italic
]
],
[
'o',
[
ve.dm.example.bold,
ve.dm.example.underline,
ve.dm.example.italic
]
],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'nested annotations are closed and reopened in the correct order': {
body: '<p><a href="Foo">F<b>o<i>o</i></b><i>b</i></a><i>a<b>r</b>b<u>a</u>z</i></p>',
head: '<base href="http://example.com/Bar/Baz" />',
data: [
{ type: 'paragraph' },
[ 'F', [ ve.dm.example.link( 'Foo' ) ] ],
[ 'o', [ ve.dm.example.link( 'Foo' ), ve.dm.example.bold ] ],
[ 'o', [ ve.dm.example.link( 'Foo' ), ve.dm.example.bold, ve.dm.example.italic ] ],
[ 'b', [ ve.dm.example.link( 'Foo' ), ve.dm.example.italic ] ],
[ 'a', [ ve.dm.example.italic ] ],
[ 'r', [ ve.dm.example.italic, ve.dm.example.bold ] ],
[ 'b', [ ve.dm.example.italic ] ],
[ 'a', [ ve.dm.example.italic, ve.dm.example.underline ] ],
[ 'z', [ ve.dm.example.italic ] ],
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'about grouping': {
body: '<div rel="ve:Alien" about="#vet1">Foo</div>' +
'<div rel="ve:Alien" about="#vet1">Bar</div>' +
'<div rel="ve:Alien" about="#vet2">Baz</div>' +
'<foobar about="#vet2">Quux</foobar>' +
'<p>Whee</p>' +
'<foobar about="#vet2">Yay</foobar>' +
'<div rel="ve:Alien" about="#vet2">Blah</div>' +
'<foobar about="#vet3">Meh</foobar>',
data: [
{
type: 'alienBlock',
originalDomElements: $( '<div rel="ve:Alien" about="#vet1">Foo</div>' +
'<div rel="ve:Alien" about="#vet1">Bar</div>' ).toArray()
},
{ type: '/alienBlock' },
{
type: 'alienBlock',
originalDomElements: $( '<div rel="ve:Alien" about="#vet2">Baz</div>' +
'<foobar about="#vet2">Quux</foobar>' ).toArray()
},
{ type: '/alienBlock' },
{ type: 'paragraph' },
'W',
'h',
'e',
'e',
{ type: '/paragraph' },
{
type: 'alienBlock',
originalDomElements: $( '<foobar about="#vet2">Yay</foobar>' +
'<div rel="ve:Alien" about="#vet2">Blah</div>' ).toArray()
},
{ type: '/alienBlock' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
{
type: 'alienInline',
originalDomElements: $( '<foobar about="#vet3">Meh</foobar>' ).toArray()
},
{ type: '/alienInline' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'about group separated by whitespace is split': {
body: '<div rel="ve:Alien" about="#vet1">Foo</div>\t<div rel="ve:Alien" about="#vet1">Bar</div>',
data: [
{
type: 'alienBlock',
originalDomElements: $( '<div rel="ve:Alien" about="#vet1">Foo</div>' ).toArray(),
internal: {
whitespace: [ undefined, undefined, undefined, '\t' ]
}
},
{ type: '/alienBlock' },
{
type: 'alienBlock',
originalDomElements: $( '<div rel="ve:Alien" about="#vet1">Bar</div>' ).toArray(),
internal: {
whitespace: [ '\t' ]
}
},
{ type: '/alienBlock' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'about group separated by text is split': {
body: '<p><span rel="ve:Alien" about="#vet1">Foo</span>X<span rel="ve:Alien" about="#vet1">Bar</span></p>',
data: [
{ type: 'paragraph' },
{
type: 'alienInline',
originalDomElements: $( '<span rel="ve:Alien" about="#vet1">Foo</span>' ).toArray()
},
{ type: '/alienInline' },
'X',
{
type: 'alienInline',
originalDomElements: $( '<span rel="ve:Alien" about="#vet1">Bar</span>' ).toArray()
},
{ type: '/alienInline' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'whitespace preservation with an about group': {
body: ' <div rel="ve:Alien" about="#vet1">\tFoo\t\t</div>' +
'<div rel="ve:Alien" about="#vet1"> Bar </div> ',
data: [
{
type: 'alienBlock',
originalDomElements: $( '<div rel="ve:Alien" about="#vet1">\tFoo\t\t</div>' +
'<div rel="ve:Alien" about="#vet1"> Bar </div>' ).toArray(),
internal: {
whitespace: [ ' ', undefined, undefined, ' ' ]
}
},
{ type: '/alienBlock' },
{ type: 'internalList' },
{ type: '/internalList' }
],
innerWhitespace: [ ' ', ' ' ]
},
'block node inside annotation node is alienated': {
body: '<span>\n<p>Bar</p></span>',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
[ '\n', [ ve.dm.example.span ] ],
{
type: 'alienInline',
originalDomElements: $( '<p>Bar</p>' ).toArray(),
annotations: [ ve.dm.example.span ]
},
{ type: '/alienInline' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '\n<span><p>Bar</p></span>'
},
'block node inside annotation node surrounded by tables': {
body: '<table></table><span>\n<p>Bar</p></span><table></table>',
data: [
{ type: 'table' },
{ type: '/table' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
[ '\n', [ ve.dm.example.span ] ],
{
type: 'alienInline',
originalDomElements: $( '<p>Bar</p>' ).toArray(),
annotations: [ ve.dm.example.span ]
},
{ type: '/alienInline' },
{ type: '/paragraph' },
{ type: 'table' },
{ type: '/table' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<table></table>\n<span><p>Bar</p></span><table></table>'
},
'block node inside annotation node is alienated and continues wrapping': {
body: 'Foo<span>\n<p>Bar</p></span>Baz',
data: [
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'F',
'o',
'o',
[ '\n', [ ve.dm.example.span ] ],
{
type: 'alienInline',
originalDomElements: $( '<p>Bar</p>' ).toArray(),
annotations: [ ve.dm.example.span ]
},
{ type: '/alienInline' },
'B',
'a',
'z',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: 'Foo\n<span><p>Bar</p></span>Baz'
},
'whitespace before meta node in wrapping mode': {
body: '<table><tbody><tr><td>Foo\n<meta content="bar" /></td></tr></tbody></table>',
data: [
{ type: 'table' },
{ type: 'tableSection', attributes: { style: 'body' } },
{ type: 'tableRow' },
{
type: 'tableCell',
attributes: { style: 'data' },
internal: { whitespace: [ undefined, undefined, '\n' ] }
},
{
type: 'paragraph',
internal: {
generated: 'wrapper',
whitespace: [ undefined, undefined, undefined, '\n' ]
}
},
'F',
'o',
'o',
{ type: '/paragraph' },
{
type: 'alienMeta',
internal: { whitespace: [ '\n' ] },
originalDomElements: $( '<meta content="bar" />' ).toArray()
},
{ type: '/alienMeta' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: '/tableSection' },
{ type: '/table' },
{ type: 'internalList' },
{ type: '/internalList' }
]
},
'table with merged cells': {
body: ve.dm.example.mergedCellsHtml,
data: ve.dm.example.mergedCells
},
'table with caption, head, foot and body': {
body: ve.dm.example.complexTableHtml,
data: ve.dm.example.complexTable
},
'div set to RTL with paragraph inside': {
body: '<div style="direction: rtl;"><p>a<b>b</b>c<i>d</i>e</p></div>',
data: [
{ type: 'div' },
{ type: 'paragraph' },
'a',
[ 'b', [ ve.dm.example.bold ] ],
'c',
[ 'd', [ ve.dm.example.italic ] ],
'e',
{ type: '/paragraph' },
{ type: '/div' },
{ type: 'internalList' },
{ type: '/internalList' }
],
fromDataBody: '<div><p>a<b>b</b>c<i>d</i>e</p></div>'
},
'comment escaping': {
body: '<p><!---Foo-bar-->b&r---></p>',
data: [
{ type: 'paragraph' },
{ type: 'comment', attributes: { text: '-Foo-bar-->b&r-' } },
{ type: '/comment' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
clipboardBody: '<p><span rel="ve:Comment" data-ve-comment="-Foo-bar-->b&r-"> </span></p>'
},
'comment escaping is normalized': {
body: '<p><!-->Foo-bar-->b&r---></p>',
data: [
{ type: 'paragraph' },
{ type: 'comment', attributes: { text: '>Foo-bar-->b&r-' } },
{ type: '/comment' },
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
],
normalizedBody: '<p><!-->Foo-bar-->b&r---></p>',
clipboardBody: '<p><span rel="ve:Comment" data-ve-comment=">Foo-bar-->b&r-"> </span></p>'
},
'other block nodes': {
body: '<center>Foo</center><hr><blockquote>Bar</blockquote>',
data: [
{ type: 'center' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'F', 'o', 'o',
{ type: '/paragraph' },
{ type: '/center' },
{ type: 'horizontalRule' },
{ type: '/horizontalRule' },
{ type: 'blockquote' },
'B', 'a', 'r',
{ type: '/blockquote' },
{ type: 'internalList' },
{ type: '/internalList' }
],
ceHtml: '<div class="ve-ce-branchNode-slug ve-ce-branchNode-blockSlug"></div>' +
'<center class="ve-ce-branchNode"><p class="ve-ce-branchNode ve-ce-contentBranchNode ve-ce-paragraphNode ve-ce-generated-wrapper">Foo</p></center>' +
'<div class="ve-ce-branchNode-slug ve-ce-branchNode-blockSlug"></div>' +
'<div class="ve-ce-focusableNode ve-ce-horizontalRuleNode" contenteditable="false"><hr class="ve-ce-leafNode"></div>' +
'<blockquote class="ve-ce-branchNode ve-ce-contentBranchNode">Bar</blockquote>'
}
};
ve.dm.example.isolationHtml =
'<ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>' +
'Paragraph' +
'<ul><li>Item 4</li><li>Item 5</li><li>Item 6</li></ul>' +
'<table><tbody><tr><td>Cell 1</td><td>Cell 2</td><td>Cell 3</td></tr><tr><td>Cell 4</td></tr></tbody></table>' +
'Not allowed by dm:' +
'<ul><li><h1>Title in list</h1></li><li><pre>Preformatted in list</pre></li></ul>' +
'<ul><li><ol><li>Nested 1</li><li>Nested 2</li><li>Nested 3</li></ol></li></ul>' +
'<ul><li><p>P1</p><p>P2</p><p>P3</p></li></ul>';
ve.dm.example.isolationData = [
// 0
{ type: 'list', attributes: { style: 'bullet' } },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'I', 't', 'e', 'm', ' ', '1',
{ type: '/paragraph' },
// 10
{ type: '/listItem' },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'I', 't', 'e', 'm', ' ', '2',
{ type: '/paragraph' },
// 20
{ type: '/listItem' },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'I', 't', 'e', 'm', ' ', '3',
{ type: '/paragraph' },
// 30
{ type: '/listItem' },
{ type: '/list' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'P', 'a', 'r', 'a', 'g', 'r', 'a',
// 40
'p', 'h',
{ type: '/paragraph' },
{ type: 'list', attributes: { style: 'bullet' } },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'I', 't', 'e', 'm',
// 50
' ', '4',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'I', 't', 'e', 'm',
// 60
' ', '5',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'I', 't', 'e', 'm',
// 70
' ', '6',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'table' },
{ type: 'tableSection', attributes: { style: 'body' } },
{ type: 'tableRow' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
// 80
'C', 'e', 'l', 'l', ' ', '1',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
// 90
'C', 'e', 'l', 'l', ' ', '2',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
// 100
'C', 'e', 'l', 'l', ' ', '3',
{ type: '/paragraph' },
{ type: '/tableCell' },
{ type: '/tableRow' },
{ type: 'tableRow' },
// 110
{ type: 'tableCell', attributes: { style: 'data' } },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'C', 'e', 'l', 'l', ' ', '4',
{ type: '/paragraph' },
{ type: '/tableCell' },
// 120
{ type: '/tableRow' },
{ type: '/tableSection' },
{ type: '/table' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'N', 'o', 't', ' ', 'a', 'l',
// 130
'l', 'o', 'w', 'e', 'd', ' ', 'b', 'y', ' ', 'd',
// 140
'm', ':',
{ type: '/paragraph' },
{ type: 'list', attributes: { style: 'bullet' } },
{ type: 'listItem' },
{ type: 'heading', attributes: { level: 1 } },
'T', 'i', 't', 'l',
// 150
'e', ' ', 'i', 'n', ' ', 'l', 'i', 's', 't',
{ type: '/heading' },
// 160
{ type: '/listItem' },
{ type: 'listItem' },
{ type: 'preformatted' },
'P', 'r', 'e', 'f', 'o', 'r', 'm',
// 170
'a', 't', 't', 'e', 'd', ' ', 'i', 'n', ' ', 'l',
// 180
'i', 's', 't',
{ type: '/preformatted' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'list', attributes: { style: 'bullet' } },
{ type: 'listItem' },
{ type: 'list', attributes: { style: 'number' } },
{ type: 'listItem' },
// 190
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'N', 'e', 's', 't', 'e', 'd', ' ', '1',
{ type: '/paragraph' },
// 200
{ type: '/listItem' },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'N', 'e', 's', 't', 'e', 'd', ' ',
// 210
'2',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: 'listItem' },
{ type: 'paragraph', internal: { generated: 'wrapper' } },
'N', 'e', 's', 't', 'e',
// 220
'd', ' ', '3',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'list', attributes: { style: 'bullet' } },
{ type: 'listItem' },
// 230
{ type: 'paragraph' },
'P', '1',
{ type: '/paragraph' },
{ type: 'paragraph' },
'P', '2',
{ type: '/paragraph' },
{ type: 'paragraph' },
'P',
// 240
'3',
{ type: '/paragraph' },
{ type: '/listItem' },
{ type: '/list' },
{ type: 'internalList' },
{ type: '/internalList' }
// 246
];
ve.dm.example.RDFaDoc = ve.dm.converter.getModelFromDom( ve.createDocumentFromHtml(
'<p content="b" datatype="c" property="d" rel="e" resource="f" rev="g" typeof="h" ' +
// Non-RDFa attribute
'class="i">' +
'Foo</p>'
) );
ve.dm.example.UnboldableNode = function () {
// Parent constructor
ve.dm.example.UnboldableNode.super.apply( this, arguments );
};
OO.inheritClass( ve.dm.example.UnboldableNode, ve.dm.LeafNode );
ve.dm.example.UnboldableNode.static.name = 'exampleUnboldable';
ve.dm.example.UnboldableNode.static.isContent = true;
ve.dm.example.UnboldableNode.static.blacklistedAnnotationTypes = [ 'textStyle/bold' ];
ve.dm.example.UnboldableNode.static.matchTagNames = [];
ve.dm.modelRegistry.register( ve.dm.example.UnboldableNode );
ve.dm.example.IgnoreChildrenNode = function () {
// Parent constructor
ve.dm.example.IgnoreChildrenNode.super.apply( this, arguments );
};
OO.inheritClass( ve.dm.example.IgnoreChildrenNode, ve.dm.BranchNode );
ve.dm.example.IgnoreChildrenNode.static.name = 'exampleIgnoreChildren';
ve.dm.example.IgnoreChildrenNode.static.ignoreChildren = true;
ve.dm.example.IgnoreChildrenNode.static.matchTagNames = [];
ve.dm.modelRegistry.register( ve.dm.example.IgnoreChildrenNode );
ve.dm.example.annotationData = [
{ type: 'paragraph' },
'F',
'o',
'o',
{ type: 'exampleUnboldable' },
// 5
{ type: '/exampleUnboldable' },
'B',
'a',
'r',
{ type: '/paragraph' },
// 10
{ type: 'exampleIgnoreChildren' },
{ type: 'paragraph' },
'B',
{ type: '/paragraph' },
{ type: 'exampleIgnoreChildren' },
// 15
{ type: 'paragraph' },
'a',
{ type: '/paragraph' },
{ type: '/exampleIgnoreChildren' },
{ type: 'paragraph' },
// 20
'r',
{ type: '/paragraph' },
{ type: '/exampleIgnoreChildren' },
{ type: 'paragraph' },
'B',
// 25
'a',
'z',
{ type: '/paragraph' },
{ type: 'internalList' },
{ type: '/internalList' }
// 30
];
ve.dm.example.selectNodesCases = [
{
range: new ve.Range( 1 ),
mode: 'branches',
expected: [
// heading
{
node: [ 0 ],
range: new ve.Range( 1 ),
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 0, 5 ),
parentOuterRange: new ve.Range( 0, 63 )
}
]
},
{
range: new ve.Range( 10 ),
mode: 'branches',
expected: [
// table/tableSection/tableRow/tableCell/paragraph
{
node: [ 1, 0, 0, 0, 0 ],
range: new ve.Range( 10 ),
index: 0,
nodeRange: new ve.Range( 10, 11 ),
nodeOuterRange: new ve.Range( 9, 12 ),
parentOuterRange: new ve.Range( 8, 34 )
}
]
},
{
range: new ve.Range( 20 ),
mode: 'branches',
expected: [
// table/tableSection/tableRow/tableCell/list/listItem/list/listItem/paragraph
{
node: [ 1, 0, 0, 0, 1, 0, 1, 0, 0 ],
range: new ve.Range( 20 ),
index: 0,
nodeRange: new ve.Range( 20, 21 ),
nodeOuterRange: new ve.Range( 19, 22 ),
parentOuterRange: new ve.Range( 18, 23 )
}
]
},
{
range: new ve.Range( 1, 20 ),
mode: 'branches',
expected: [
// heading
{
node: [ 0 ],
range: new ve.Range( 1, 4 ),
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 0, 5 ),
parentOuterRange: new ve.Range( 0, 63 )
},
// table/tableSection/tableRow/tableCell/paragraph
{
node: [ 1, 0, 0, 0, 0 ],
index: 0,
nodeRange: new ve.Range( 10, 11 ),
nodeOuterRange: new ve.Range( 9, 12 ),
parentOuterRange: new ve.Range( 8, 34 )
},
// table/tableSection/tableRow/tableCell/list/listItem/paragraph
{
node: [ 1, 0, 0, 0, 1, 0, 0 ],
index: 0,
nodeRange: new ve.Range( 15, 16 ),
nodeOuterRange: new ve.Range( 14, 17 ),
parentOuterRange: new ve.Range( 13, 25 )
},
// table/tableSection/tableRow/tableCell/list/listItem/list/listItem/paragraph
{
node: [ 1, 0, 0, 0, 1, 0, 1, 0, 0 ],
range: new ve.Range( 20 ),
index: 0,
nodeRange: new ve.Range( 20, 21 ),
nodeOuterRange: new ve.Range( 19, 22 ),
parentOuterRange: new ve.Range( 18, 23 )
}
]
},
{
range: new ve.Range( 1 ),
mode: 'branches',
expected: [
// heading
{
node: [ 0 ],
range: new ve.Range( 1 ),
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 0, 5 ),
parentOuterRange: new ve.Range( 0, 63 )
}
]
},
{
range: new ve.Range( 0, 3 ),
mode: 'leaves',
expected: [
// heading/text
{
node: [ 0, 0 ],
range: new ve.Range( 1, 3 ),
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 1, 4 ),
parentOuterRange: new ve.Range( 0, 5 )
}
],
msg: 'partial leaf results have ranges with global offsets'
},
{
range: new ve.Range( 0, 11 ),
mode: 'leaves',
expected: [
// heading/text
{
node: [ 0, 0 ],
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 1, 4 ),
parentOuterRange: new ve.Range( 0, 5 )
},
// table/tableSection/tableRow/tableCell/paragraph/text
{
node: [ 1, 0, 0, 0, 0, 0 ],
index: 0,
nodeRange: new ve.Range( 10, 11 ),
nodeOuterRange: new ve.Range( 10, 11 ),
parentOuterRange: new ve.Range( 9, 12 )
}
],
msg: 'leaf nodes do not have ranges, leaf nodes from different levels'
},
{
range: new ve.Range( 29, 43 ),
mode: 'leaves',
expected: [
// table/tableSection/tableRow/tableCell/list/listItem/paragraph/text
{
node: [ 1, 0, 0, 0, 2, 0, 0, 0 ],
index: 0,
nodeRange: new ve.Range( 29, 30 ),
nodeOuterRange: new ve.Range( 29, 30 ),
parentOuterRange: new ve.Range( 28, 31 )
},
// preformatted/text
{
node: [ 2, 0 ],
index: 0,
nodeRange: new ve.Range( 38, 39 ),
nodeOuterRange: new ve.Range( 38, 39 ),
parentOuterRange: new ve.Range( 37, 43 )
},
// preformatted/image
{
node: [ 2, 1 ],
index: 1,
nodeRange: new ve.Range( 40 ),
nodeOuterRange: new ve.Range( 39, 41 ),
parentOuterRange: new ve.Range( 37, 43 )
},
// preformatted/text
{
node: [ 2, 2 ],
index: 2,
nodeRange: new ve.Range( 41, 42 ),
nodeOuterRange: new ve.Range( 41, 42 ),
parentOuterRange: new ve.Range( 37, 43 )
}
],
msg: 'leaf nodes that are not text nodes'
},
{
range: new ve.Range( 2, 16 ),
mode: 'siblings',
expected: [
// heading
{
node: [ 0 ],
range: new ve.Range( 2, 4 ),
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 0, 5 ),
parentOuterRange: new ve.Range( 0, 63 )
},
// table
{
node: [ 1 ],
range: new ve.Range( 6, 16 ),
index: 1,
nodeRange: new ve.Range( 6, 36 ),
nodeOuterRange: new ve.Range( 5, 37 ),
parentOuterRange: new ve.Range( 0, 63 )
}
],
msg: 'siblings at the document level'
},
{
range: new ve.Range( 2, 51 ),
mode: 'siblings',
expected: [
// heading
{
node: [ 0 ],
range: new ve.Range( 2, 4 ),
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 0, 5 ),
parentOuterRange: new ve.Range( 0, 63 )
},
// table
{
node: [ 1 ],
index: 1,
nodeRange: new ve.Range( 6, 36 ),
nodeOuterRange: new ve.Range( 5, 37 ),
parentOuterRange: new ve.Range( 0, 63 )
},
// preformatted
{
node: [ 2 ],
index: 2,
nodeRange: new ve.Range( 38, 42 ),
nodeOuterRange: new ve.Range( 37, 43 ),
parentOuterRange: new ve.Range( 0, 63 )
},
// definitionList
{
node: [ 3 ],
range: new ve.Range( 44, 51 ),
index: 3,
nodeRange: new ve.Range( 44, 54 ),
nodeOuterRange: new ve.Range( 43, 55 ),
parentOuterRange: new ve.Range( 0, 63 )
}
],
msg: 'more than 2 siblings at the document level'
},
{
range: new ve.Range( 1 ),
mode: 'leaves',
expected: [
// heading/text
{
node: [ 0, 0 ],
range: new ve.Range( 1 ),
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 1, 4 ),
parentOuterRange: new ve.Range( 0, 5 )
}
],
msg: 'zero-length range at the start of a text node returns text node rather than parent'
},
{
range: new ve.Range( 4 ),
mode: 'leaves',
expected: [
// heading/text
{
node: [ 0, 0 ],
range: new ve.Range( 4 ),
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 1, 4 ),
parentOuterRange: new ve.Range( 0, 5 )
}
],
msg: 'zero-length range at the end of a text node returns text node rather than parent'
},
{
range: new ve.Range( 2, 3 ),
mode: 'leaves',
expected: [
// heading/text
{
node: [ 0, 0 ],
range: new ve.Range( 2, 3 ),
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 1, 4 ),
parentOuterRange: new ve.Range( 0, 5 )
}
],
msg: 'range entirely within one leaf node'
},
{
range: new ve.Range( 5 ),
mode: 'leaves',
expected: [
// document
{
node: [],
range: new ve.Range( 5 ),
// no 'index' because documentNode has no parent
indexInNode: 1,
nodeRange: new ve.Range( 0, 63 ),
nodeOuterRange: new ve.Range( 0, 63 )
}
],
msg: 'zero-length range between two children of the document'
},
{
range: new ve.Range( 0 ),
mode: 'leaves',
expected: [
// document
{
node: [],
range: new ve.Range( 0 ),
// no 'index' because documentNode has no parent
indexInNode: 0,
nodeRange: new ve.Range( 0, 63 ),
nodeOuterRange: new ve.Range( 0, 63 )
}
],
msg: 'zero-length range at the start of the document'
},
{
range: new ve.Range( 32, 39 ),
mode: 'leaves',
expected: [
// table/tableSection/tableRow/tableCell/list
{
node: [ 1, 0, 0, 0, 2 ],
range: new ve.Range( 32 ),
index: 2,
indexInNode: 1,
nodeRange: new ve.Range( 27, 32 ),
nodeOuterRange: new ve.Range( 26, 33 )
},
// preformatted/text
{
node: [ 2, 0 ],
// no 'range' because the text node is covered completely
index: 0,
nodeRange: new ve.Range( 38, 39 ),
nodeOuterRange: new ve.Range( 38, 39 ),
parentOuterRange: new ve.Range( 37, 43 )
}
],
msg: 'range with 5 closings and a text node'
},
{
range: new ve.Range( 2, 57 ),
mode: 'covered',
expected: [
// heading/text
{
node: [ 0, 0 ],
range: new ve.Range( 2, 4 ),
index: 0,
nodeRange: new ve.Range( 1, 4 ),
nodeOuterRange: new ve.Range( 1, 4 ),
parentOuterRange: new ve.Range( 0, 5 )
},
// table
{
node: [ 1 ],
// no 'range' because the table is covered completely
index: 1,
nodeRange: new ve.Range( 6, 36 ),
nodeOuterRange: new ve.Range( 5, 37 ),
parentOuterRange: new ve.Range( 0, 63 )
},
// preformatted
{
node: [ 2 ],
// no 'range' because the node is covered completely
index: 2,
nodeRange: new ve.Range( 38, 42 ),
nodeOuterRange: new ve.Range( 37, 43 ),
parentOuterRange: new ve.Range( 0, 63 )
},
// definitionList
{
node: [ 3 ],
// no 'range' because the node is covered completely
index: 3,
nodeRange: new ve.Range( 44, 54 ),
nodeOuterRange: new ve.Range( 43, 55 ),
parentOuterRange: new ve.Range( 0, 63 )
},
// paragraph/text
{
node: [ 4, 0 ],
// no 'range' because the text node is covered completely
index: 0,
nodeRange: new ve.Range( 56, 57 ),
nodeOuterRange: new ve.Range( 56, 57 ),
parentOuterRange: new ve.Range( 55, 58 )
}
],
msg: 'range from the first heading into the second-to-last paragraph, in covered mode'
},
{
range: new ve.Range( 14 ),
mode: 'siblings',
expected: [
// table/tableSection/tableRow/tableCell/list/listItem
{
node: [ 1, 0, 0, 0, 1, 0 ],
range: new ve.Range( 14 ),
index: 0,
indexInNode: 0,
nodeRange: new ve.Range( 14, 24 ),
nodeOuterRange: new ve.Range( 13, 25 )
}
],
msg: 'zero-length range at the beginning of a listItem, in siblings mode'
},
{
range: new ve.Range( 25, 27 ),
mode: 'covered',
expected: [
// table/tableSection/tableRow/tableCell/list
{
node: [ 1, 0, 0, 0, 1 ],
range: new ve.Range( 25 ),
index: 1,
indexInNode: 1,
nodeRange: new ve.Range( 13, 25 ),
nodeOuterRange: new ve.Range( 12, 26 )
},
// table/tableSection/tableRow/tableCell/list
{
node: [ 1, 0, 0, 0, 2 ],
range: new ve.Range( 27 ),
index: 2,
indexInNode: 0,
nodeRange: new ve.Range( 27, 32 ),
nodeOuterRange: new ve.Range( 26, 33 )
}
],
msg: 'range covering a list closing and a list opening'
},
{
range: new ve.Range( 39 ),
mode: 'leaves',
expected: [
// preformatted/text
{
node: [ 2, 0 ],
range: new ve.Range( 39 ),
index: 0,
nodeRange: new ve.Range( 38, 39 ),
nodeOuterRange: new ve.Range( 38, 39 ),
parentOuterRange: new ve.Range( 37, 43 )
}
],
msg: 'zero-length range in text node before inline node'
},
{
range: new ve.Range( 41 ),
mode: 'leaves',
expected: [
// preformatted/text
{
node: [ 2, 2 ],
range: new ve.Range( 41 ),
index: 2,
nodeRange: new ve.Range( 41, 42 ),
nodeOuterRange: new ve.Range( 41, 42 ),
parentOuterRange: new ve.Range( 37, 43 )
}
],
msg: 'zero-length range in text node after inline node'
},
{
doc: 'emptyBranch',
range: new ve.Range( 1 ),
mode: 'leaves',
expected: [
// table
{
node: [ 0 ],
range: new ve.Range( 1 ),
index: 0,
indexInNode: 0,
nodeRange: new ve.Range( 1 ),
nodeOuterRange: new ve.Range( 0, 2 ),
parentOuterRange: new ve.Range( 0, 4 )
}
],
msg: 'zero-length range in empty branch node'
},
{
doc: 'internalData',
range: new ve.Range( 6, 13 ),
mode: 'leaves',
expected: [
// internal item
{
node: [ 1, 0 ],
index: 0,
nodeRange: new ve.Range( 7, 12 ),
nodeOuterRange: new ve.Range( 6, 13 ),
parentOuterRange: new ve.Range( 5, 21 )
}
],
msg: 'range covering ignoreChildren node doesn\'t descend'
}
];