2012-07-19 00:11:26 +00:00
|
|
|
/**
|
|
|
|
* VisualEditor data model example data sets.
|
2012-07-19 21:25:16 +00:00
|
|
|
*
|
2012-07-19 00:11:26 +00:00
|
|
|
* @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt
|
|
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
|
|
*/
|
|
|
|
|
2012-04-27 21:59:52 +00:00
|
|
|
/* Static Members */
|
|
|
|
|
|
|
|
ve.dm.example = {};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Serialized HTML.
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-04-27 21:59:52 +00:00
|
|
|
* This is what the parser will emit.
|
2012-08-16 20:06:18 +00:00
|
|
|
* TODO remove some of the <p>s here to test automatic wrapping
|
2012-04-27 21:59:52 +00:00
|
|
|
*/
|
|
|
|
ve.dm.example.html =
|
|
|
|
'<h1>a<b>b</b><i>c</i></h1>' +
|
|
|
|
'<table>' +
|
|
|
|
'<tr>' +
|
|
|
|
'<td>' +
|
|
|
|
'<p>d</p>' +
|
|
|
|
'<ul>' +
|
|
|
|
'<li>' +
|
2012-08-03 19:12:09 +00:00
|
|
|
'<p>e</p>' +
|
2012-04-27 21:59:52 +00:00
|
|
|
'<ul>' +
|
|
|
|
'<li>' +
|
2012-08-03 19:12:09 +00:00
|
|
|
'<p>f</p>' +
|
2012-04-27 21:59:52 +00:00
|
|
|
'</li>' +
|
|
|
|
'</ul>' +
|
|
|
|
'</li>' +
|
|
|
|
'</ul>' +
|
|
|
|
'<ol>' +
|
|
|
|
'<li>' +
|
2012-08-03 19:12:09 +00:00
|
|
|
'<p>g</p>' +
|
2012-04-27 21:59:52 +00:00
|
|
|
'</li>' +
|
|
|
|
'</ol>' +
|
|
|
|
'</td>' +
|
|
|
|
'</tr>' +
|
|
|
|
'</table>' +
|
2012-05-04 18:56:32 +00:00
|
|
|
'<pre>h<img src="image.png">i</pre>'+
|
|
|
|
'<dl>' +
|
|
|
|
'<dt>' +
|
|
|
|
'<p>j</p>' +
|
|
|
|
'</dt>' +
|
|
|
|
'<dd>' +
|
|
|
|
'<p>k</p>' +
|
|
|
|
'</dd>' +
|
2012-05-16 20:33:27 +00:00
|
|
|
'</dl>' +
|
|
|
|
'<p>l</p>' +
|
|
|
|
'<p>m</p>';
|
2012-04-27 21:59:52 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Linear data.
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-04-27 21:59:52 +00:00
|
|
|
* This is what we convert serialized HTML from the parser into so we can work with it more easily.
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-04-27 21:59:52 +00:00
|
|
|
* There are three types of components in content data:
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-04-27 21:59:52 +00:00
|
|
|
* {String} Plain text character
|
2012-05-15 00:15:28 +00:00
|
|
|
*
|
2012-04-27 21:59:52 +00:00
|
|
|
* {Array} Annotated character
|
|
|
|
* 0: {String} Character
|
|
|
|
* 1: {Object} List of references to immutable annotation objects, keyed by JSON
|
|
|
|
* serializations of their values (hashes)
|
2012-05-15 00:15:28 +00:00
|
|
|
*
|
2012-04-27 21:59:52 +00:00
|
|
|
* {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', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } }],
|
|
|
|
// 3 - Italic "c"
|
|
|
|
['c', { '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } }],
|
|
|
|
// 4 - End of heading
|
|
|
|
{ 'type': '/heading' },
|
|
|
|
// 5 - Beginning of table
|
|
|
|
{ 'type': 'table' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 6 - Beginning of body
|
|
|
|
{ 'type': 'tableSection', 'attributes': { 'style': 'body' } },
|
|
|
|
// 7 - Beginning of row
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': 'tableRow' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 8 - Beginning of cell
|
2012-06-08 05:02:25 +00:00
|
|
|
{ 'type': 'tableCell', 'attributes': { 'style': 'data' } },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 9 - Beginning of paragraph
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': 'paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 10 - Plain "d"
|
2012-04-27 21:59:52 +00:00
|
|
|
'd',
|
2012-06-08 05:08:49 +00:00
|
|
|
// 11 - End of paragraph
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 12 - Beginning of bullet list
|
2012-05-03 21:54:27 +00:00
|
|
|
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 13 - Beginning of list item
|
2012-05-04 22:50:32 +00:00
|
|
|
{ 'type': 'listItem' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 14 - Beginning of paragraph
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': 'paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 15 - Plain "e"
|
2012-04-27 21:59:52 +00:00
|
|
|
'e',
|
2012-06-08 05:08:49 +00:00
|
|
|
// 16 - End of paragraph
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 17 - Beginning of nested bullet list
|
2012-05-03 21:54:27 +00:00
|
|
|
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 18 - Beginning of nested bullet list item
|
2012-05-04 22:50:32 +00:00
|
|
|
{ 'type': 'listItem' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 19 - Beginning of paragraph
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': 'paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 20 - Plain "f"
|
2012-04-27 21:59:52 +00:00
|
|
|
'f',
|
2012-06-08 05:08:49 +00:00
|
|
|
// 21 - End of paragraph
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 22 - End of nested bullet list item
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/listItem' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 23 - End of nested bullet list
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/list' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 24 - End of bullet list item
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/listItem' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 25 - End of bullet list
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/list' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 26 - Beginning of numbered list
|
2012-05-03 21:54:27 +00:00
|
|
|
{ 'type': 'list', 'attributes': { 'style': 'number' } },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 27 - Beginning of numbered list item
|
2012-05-04 22:50:32 +00:00
|
|
|
{ 'type': 'listItem' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 28 - Beginning of paragraph
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': 'paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 29 - Plain "g"
|
2012-04-27 21:59:52 +00:00
|
|
|
'g',
|
2012-06-08 05:08:49 +00:00
|
|
|
// 30 - End of paragraph
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 31 - End of item
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/listItem' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 32 - End of list
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/list' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 33 - End of cell
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/tableCell' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 34 - End of row
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/tableRow' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 35 - End of body
|
|
|
|
{ 'type': '/tableSection' },
|
|
|
|
// 36 - End of table
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/table' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 37 - Beginning of preformatted
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': 'preformatted' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 38 - Plain "h"
|
2012-04-27 21:59:52 +00:00
|
|
|
'h',
|
2012-06-08 05:08:49 +00:00
|
|
|
// 39 - Beginning of inline image
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': 'image', 'attributes': { 'html/src': 'image.png' } },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 40 - End of inline image
|
2012-04-27 21:59:52 +00:00
|
|
|
{ 'type': '/image' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 41 - Plain "i"
|
2012-04-27 21:59:52 +00:00
|
|
|
'i',
|
2012-06-08 05:08:49 +00:00
|
|
|
// 42 - End of preformatted
|
2012-05-04 18:56:32 +00:00
|
|
|
{ 'type': '/preformatted' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 43 - Beginning of definition list
|
2012-05-04 18:56:32 +00:00
|
|
|
{ 'type': 'definitionList' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 44 - Beginning of definition list term item
|
2012-05-04 18:56:32 +00:00
|
|
|
{ 'type': 'definitionListItem', 'attributes': { 'style': 'term' } },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 45 - Beginning of paragraph
|
2012-05-04 18:56:32 +00:00
|
|
|
{ 'type': 'paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 46 - Plain "j"
|
2012-05-04 18:56:32 +00:00
|
|
|
'j',
|
2012-06-08 05:08:49 +00:00
|
|
|
// 47 - End of paragraph
|
2012-05-04 18:56:32 +00:00
|
|
|
{ 'type': '/paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 48 - End of definition list term item
|
2012-05-04 18:56:32 +00:00
|
|
|
{ 'type': '/definitionListItem' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 49 - Beginning of definition list definition item
|
2012-05-04 18:56:32 +00:00
|
|
|
{ 'type': 'definitionListItem', 'attributes': { 'style': 'definition' } },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 50 - Beginning of paragraph
|
2012-05-04 18:56:32 +00:00
|
|
|
{ 'type': 'paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 51 - Plain "k"
|
2012-05-11 17:29:09 +00:00
|
|
|
'k',
|
2012-06-08 05:08:49 +00:00
|
|
|
// 52 - End of paragraph
|
2012-05-04 18:56:32 +00:00
|
|
|
{ 'type': '/paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 53 - End of definition list definition item
|
2012-05-04 18:56:32 +00:00
|
|
|
{ 'type': '/definitionListItem' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 54 - End of definition list
|
2012-05-16 20:33:27 +00:00
|
|
|
{ 'type': '/definitionList' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 55 - Beginning of paragraph
|
2012-05-16 20:33:27 +00:00
|
|
|
{ 'type': 'paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 56 - Plain "l"
|
2012-05-16 20:33:27 +00:00
|
|
|
'l',
|
2012-06-08 05:08:49 +00:00
|
|
|
// 57 - End of paragraph
|
2012-05-16 20:33:27 +00:00
|
|
|
{ 'type': '/paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 58 - Beginning of paragraph
|
2012-05-16 20:33:27 +00:00
|
|
|
{ 'type': 'paragraph' },
|
2012-06-08 05:08:49 +00:00
|
|
|
// 59 - Plain "m"
|
2012-05-16 20:33:27 +00:00
|
|
|
'm',
|
2012-06-08 05:08:49 +00:00
|
|
|
// 60 - End of paragraph
|
2012-05-16 20:33:27 +00:00
|
|
|
{ 'type': '/paragraph' }
|
2012-06-08 05:08:49 +00:00
|
|
|
// 61 - End of document
|
2012-06-04 22:59:04 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
ve.dm.example.alienData = [
|
|
|
|
// 0 - Open alienBlock
|
|
|
|
{ 'type': 'alienBlock' },
|
|
|
|
// 1 - Close alienBlock
|
|
|
|
{ 'type': '/alienBlock' },
|
|
|
|
// 2 - Open paragraph
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
// 3 - Plain character 'a'
|
|
|
|
'a',
|
|
|
|
// 4 - Open alienInline
|
|
|
|
{ 'type': 'alienBlock' },
|
|
|
|
// 5 - Close alienInline
|
|
|
|
{ 'type': '/alienBlock' },
|
|
|
|
// 6 - Plain character 'b'
|
|
|
|
'b',
|
|
|
|
// 7 - Close paragraph
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
// 8 - Open alienBlock
|
|
|
|
{ 'type': 'alienBlock' },
|
|
|
|
// 9 - Close alienBlock
|
|
|
|
{ 'type': '/alienBlock' }
|
|
|
|
// 10 - End of document
|
2012-04-27 21:59:52 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sample content data index.
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-04-27 21:59:52 +00:00
|
|
|
* This is part of what a ve.dm.DocumentFragment generates when given linear data.
|
2012-05-14 22:05:09 +00:00
|
|
|
*
|
2012-05-04 18:56:32 +00:00
|
|
|
* (21) branch nodes
|
2012-04-30 20:23:46 +00:00
|
|
|
* (01) document node
|
|
|
|
* (01) heading node
|
|
|
|
* (01) table node
|
|
|
|
* (01) tableRow node
|
|
|
|
* (01) tableCell node
|
2012-05-04 18:56:32 +00:00
|
|
|
* (06) paragraph nodes
|
2012-04-30 20:23:46 +00:00
|
|
|
* (03) list nodes
|
|
|
|
* (03) listItem nodes
|
|
|
|
* (01) preformatted node
|
2012-05-04 18:56:32 +00:00
|
|
|
* (01) definitionList node
|
|
|
|
* (02) definitionListItem nodes
|
|
|
|
* (10) leaf nodes
|
|
|
|
* (09) text nodes
|
2012-04-30 20:23:46 +00:00
|
|
|
* (01) image node
|
2012-04-27 21:59:52 +00:00
|
|
|
*/
|
2012-04-30 19:37:08 +00:00
|
|
|
ve.dm.example.tree = new ve.dm.DocumentNode( [
|
2012-04-27 21:59:52 +00:00
|
|
|
// Heading with "abc"
|
|
|
|
new ve.dm.HeadingNode( [new ve.dm.TextNode( 3 )], ve.dm.example.data[0].attributes ),
|
|
|
|
new ve.dm.TableNode( [
|
2012-06-08 05:08:49 +00:00
|
|
|
new ve.dm.TableSectionNode( [
|
|
|
|
new ve.dm.TableRowNode( [
|
|
|
|
new ve.dm.TableCellNode( [
|
|
|
|
// Paragraph with "d"
|
|
|
|
new ve.dm.ParagraphNode( [new ve.dm.TextNode( 1 )] ),
|
|
|
|
new ve.dm.ListNode( [
|
|
|
|
// 1st level bullet list item with "e"
|
|
|
|
new ve.dm.ListItemNode( [
|
|
|
|
new ve.dm.ParagraphNode( [new ve.dm.TextNode( 1 )] ),
|
|
|
|
new ve.dm.ListNode( [
|
|
|
|
// 2nd level bullet list item with "f"
|
|
|
|
new ve.dm.ListItemNode( [
|
|
|
|
new ve.dm.ParagraphNode( [new ve.dm.TextNode( 1 )] )
|
|
|
|
] )
|
|
|
|
], ve.dm.example.data[17].attributes )
|
|
|
|
] )
|
|
|
|
], ve.dm.example.data[12].attributes ),
|
|
|
|
new ve.dm.ListNode( [
|
|
|
|
// Numbered list item with "g"
|
|
|
|
new ve.dm.ListItemNode( [
|
|
|
|
new ve.dm.ParagraphNode( [new ve.dm.TextNode( 1 )] )
|
|
|
|
] )
|
|
|
|
], ve.dm.example.data[26].attributes )
|
|
|
|
], ve.dm.example.data[8].attributes )
|
|
|
|
] )
|
|
|
|
], ve.dm.example.data[6].attributes )
|
2012-04-27 21:59:52 +00:00
|
|
|
] ),
|
|
|
|
// Preformatted with "h[image.png]i"
|
|
|
|
new ve.dm.PreformattedNode( [
|
|
|
|
new ve.dm.TextNode( 1 ),
|
2012-06-08 05:08:49 +00:00
|
|
|
new ve.dm.ImageNode( [], ve.dm.example.data[39].attributes ),
|
2012-04-27 21:59:52 +00:00
|
|
|
new ve.dm.TextNode( 1 )
|
2012-05-04 18:56:32 +00:00
|
|
|
] ),
|
|
|
|
new ve.dm.DefinitionListNode( [
|
|
|
|
// Definition list term item with "j"
|
|
|
|
new ve.dm.DefinitionListItemNode( [
|
|
|
|
new ve.dm.ParagraphNode( [new ve.dm.TextNode( 1 )] )
|
2012-06-08 05:08:49 +00:00
|
|
|
], ve.dm.example.data[44].attributes ),
|
2012-05-04 18:56:32 +00:00
|
|
|
// Definition list definition item with "k"
|
|
|
|
new ve.dm.DefinitionListItemNode( [
|
|
|
|
new ve.dm.ParagraphNode( [new ve.dm.TextNode( 1 )] )
|
2012-06-08 05:08:49 +00:00
|
|
|
], ve.dm.example.data[49].attributes )
|
2012-05-16 20:33:27 +00:00
|
|
|
] ),
|
|
|
|
new ve.dm.ParagraphNode( [new ve.dm.TextNode( 1 )] ),
|
|
|
|
new ve.dm.ParagraphNode( [new ve.dm.TextNode( 1 )] )
|
2012-04-30 19:37:08 +00:00
|
|
|
] );
|
2012-04-27 21:59:52 +00:00
|
|
|
|
2012-06-06 17:17:30 +00:00
|
|
|
ve.dm.example.conversions = {
|
|
|
|
'definitionListItem term': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'dt' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'definitionListItem', 'attributes': { 'style': 'term' } }
|
|
|
|
},
|
|
|
|
'definitionListItem definition': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'dd' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'definitionListItem', 'attributes': { 'style': 'definition' } }
|
|
|
|
},
|
|
|
|
'definitionList definition': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'dl' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'definitionList' }
|
|
|
|
},
|
|
|
|
'heading level 1': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'h1' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'heading', 'attributes': { 'level': 1 } }
|
|
|
|
},
|
|
|
|
'heading level 2': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'h2' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'heading', 'attributes': { 'level': 2 } }
|
|
|
|
},
|
|
|
|
'heading level 3': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'h3' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'heading', 'attributes': { 'level': 3 } }
|
|
|
|
},
|
|
|
|
'heading level 4': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'h4' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'heading', 'attributes': { 'level': 4 } }
|
|
|
|
},
|
|
|
|
'heading level 5': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'h5' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'heading', 'attributes': { 'level': 5 } }
|
|
|
|
},
|
|
|
|
'heading level 6': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'h6' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'heading', 'attributes': { 'level': 6 } }
|
|
|
|
},
|
|
|
|
'image': {
|
2012-06-08 22:16:55 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'img' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'image' }
|
|
|
|
},
|
|
|
|
'listItem': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'li' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'listItem' }
|
|
|
|
},
|
|
|
|
'list bullet': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'ul' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'list', 'attributes': { 'style': 'bullet' } }
|
|
|
|
},
|
|
|
|
'list number': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'ol' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'list', 'attributes': { 'style': 'number' } }
|
|
|
|
},
|
|
|
|
'paragraph': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'p' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'paragraph' }
|
|
|
|
},
|
|
|
|
'preformatted': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'pre' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'preformatted' }
|
|
|
|
},
|
|
|
|
'tableCell': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'td' ),
|
2012-06-08 05:02:25 +00:00
|
|
|
'dataElement': { 'type': 'tableCell', 'attributes': { 'style': 'data' } }
|
2012-06-06 17:17:30 +00:00
|
|
|
},
|
|
|
|
'table': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'table' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'table' }
|
|
|
|
},
|
|
|
|
'tableRow': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'tr' ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'tableRow' }
|
|
|
|
},
|
|
|
|
'paragraph with mw-data attribute': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'p', { 'data-mw': '{"test":1234}' } ),
|
2012-06-07 00:47:27 +00:00
|
|
|
'dataElement': { 'type': 'paragraph', 'attributes': { 'html/data-mw': '{"test":1234}' } }
|
2012-06-06 17:17:30 +00:00
|
|
|
},
|
2012-06-07 00:47:27 +00:00
|
|
|
'paragraph with style attribute': {
|
2012-06-07 22:02:25 +00:00
|
|
|
'domElement': ve.example.createDomElement( 'p', { 'style': 'color:blue' } ),
|
2012-06-06 17:17:30 +00:00
|
|
|
'dataElement': { 'type': 'paragraph', 'attributes': { 'html/style': 'color:blue' } }
|
|
|
|
}
|
|
|
|
};
|
2012-06-08 05:00:25 +00:00
|
|
|
|
|
|
|
ve.dm.example.domToDataCases = {
|
|
|
|
'paragraph with plain text': {
|
2012-08-10 23:49:14 +00:00
|
|
|
'html': '<p>abc</p>',
|
|
|
|
'data': [
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'a',
|
|
|
|
'b',
|
|
|
|
'c',
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'annotated text with bold, italic, underline formatting': {
|
|
|
|
'html': '<p><b>a</b><i>b</i><u>c</u></p>',
|
|
|
|
'data': [
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
['a', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } }],
|
|
|
|
['b', { '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } }],
|
|
|
|
['c', { '{"type":"textStyle/underline"}': { 'type': 'textStyle/underline' } }],
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'image': {
|
|
|
|
'html': '<img src="image.png">',
|
|
|
|
'data': [
|
|
|
|
{ 'type': 'image', 'attributes' : { 'html/src' : 'image.png' } },
|
|
|
|
{ 'type' : '/image' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'paragraph with alienInline inside': {
|
2012-08-21 00:44:55 +00:00
|
|
|
'html': '<p>a<tt class="foo">b</tt>c</p>',
|
2012-08-10 23:49:14 +00:00
|
|
|
'data': [
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'a',
|
|
|
|
{
|
|
|
|
'type': 'alienInline',
|
2012-08-21 00:44:55 +00:00
|
|
|
'attributes': { 'html': '<tt class="foo">b</tt>' }
|
2012-08-10 23:49:14 +00:00
|
|
|
},
|
|
|
|
{ 'type': '/alienInline' },
|
|
|
|
'c',
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
// TODO these last two are broken due to newline hacks, will be unbroken once we remove the newline hacks
|
|
|
|
'paragraphs with an alienBlock between them': {
|
2012-08-21 00:44:55 +00:00
|
|
|
'html': '<p>abc</p><figure>abc</figure><p>def</p>',
|
2012-08-10 23:49:14 +00:00
|
|
|
'data': [
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'a',
|
|
|
|
'b',
|
|
|
|
'c',
|
|
|
|
{ 'type': '/paragraph' },
|
2012-08-21 00:44:55 +00:00
|
|
|
{ 'type': 'alienBlock', 'attributes': { 'html': '<figure>abc</figure>' } },
|
2012-08-10 23:49:14 +00:00
|
|
|
{ 'type': '/alienBlock' },
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
'd',
|
|
|
|
'e',
|
|
|
|
'f',
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'example document': {
|
|
|
|
'html': ve.dm.example.html,
|
|
|
|
'data': ve.dm.example.data
|
|
|
|
},
|
|
|
|
'list item with space followed by link': {
|
2012-08-14 00:59:16 +00:00
|
|
|
'html': '<ul><li><p> <a rel="mw:WikiLink" href="Foo_bar" data-rt="{"sHref":"foo bar"}">bar</a></p></li></ul>',
|
2012-08-10 23:49:14 +00:00
|
|
|
'data': [
|
|
|
|
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
|
|
|
|
{ 'type': 'listItem' },
|
2012-08-16 17:53:33 +00:00
|
|
|
{ 'type': 'paragraph', 'internal': { 'whitespace': [ undefined, ' ' ] } },
|
2012-08-10 23:49:14 +00:00
|
|
|
[
|
2012-06-08 05:00:25 +00:00
|
|
|
'b',
|
|
|
|
{
|
2012-08-15 18:14:44 +00:00
|
|
|
'{"data":{"htmlAttributes":{"data-rt":"{\\"sHref\\":\\"foo bar\\"}","href":"Foo_bar","rel":"mw:WikiLink"},"title":"Foo bar"},"type":"link/WikiLink"}': {
|
2012-08-10 23:49:14 +00:00
|
|
|
'type': 'link/WikiLink',
|
|
|
|
'data': {
|
|
|
|
'title': 'Foo bar',
|
|
|
|
'htmlAttributes': {
|
|
|
|
'data-rt': '{"sHref":"foo bar"}',
|
2012-08-14 00:59:16 +00:00
|
|
|
'href': 'Foo_bar',
|
2012-08-10 23:49:14 +00:00
|
|
|
'rel': 'mw:WikiLink'
|
2012-06-14 01:54:24 +00:00
|
|
|
}
|
2012-07-26 03:46:57 +00:00
|
|
|
}
|
2012-06-08 22:16:55 +00:00
|
|
|
}
|
2012-08-10 23:49:14 +00:00
|
|
|
}
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'a',
|
|
|
|
{
|
2012-08-15 18:14:44 +00:00
|
|
|
'{"data":{"htmlAttributes":{"data-rt":"{\\"sHref\\":\\"foo bar\\"}","href":"Foo_bar","rel":"mw:WikiLink"},"title":"Foo bar"},"type":"link/WikiLink"}': {
|
2012-08-10 23:49:14 +00:00
|
|
|
'type': 'link/WikiLink',
|
|
|
|
'data': {
|
|
|
|
'title': 'Foo bar',
|
|
|
|
'htmlAttributes': {
|
|
|
|
'data-rt': '{"sHref":"foo bar"}',
|
2012-08-14 00:59:16 +00:00
|
|
|
'href': 'Foo_bar',
|
2012-08-10 23:49:14 +00:00
|
|
|
'rel': 'mw:WikiLink'
|
2012-06-14 01:54:24 +00:00
|
|
|
}
|
2012-07-26 03:46:57 +00:00
|
|
|
}
|
2012-06-08 22:16:55 +00:00
|
|
|
}
|
2012-08-10 23:49:14 +00:00
|
|
|
}
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'r',
|
|
|
|
{
|
2012-08-15 18:14:44 +00:00
|
|
|
'{"data":{"htmlAttributes":{"data-rt":"{\\"sHref\\":\\"foo bar\\"}","href":"Foo_bar","rel":"mw:WikiLink"},"title":"Foo bar"},"type":"link/WikiLink"}': {
|
2012-08-10 23:49:14 +00:00
|
|
|
'type': 'link/WikiLink',
|
|
|
|
'data': {
|
|
|
|
'title': 'Foo bar',
|
|
|
|
'htmlAttributes': {
|
|
|
|
'data-rt': '{"sHref":"foo bar"}',
|
2012-08-14 00:59:16 +00:00
|
|
|
'href': 'Foo_bar',
|
2012-08-10 23:49:14 +00:00
|
|
|
'rel': 'mw:WikiLink'
|
2012-06-14 01:54:24 +00:00
|
|
|
}
|
2012-07-26 03:46:57 +00:00
|
|
|
}
|
2012-06-08 22:16:55 +00:00
|
|
|
}
|
2012-08-10 23:49:14 +00:00
|
|
|
}
|
|
|
|
],
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' },
|
|
|
|
{ 'type': '/list' }
|
|
|
|
]
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
},
|
2012-08-23 21:29:43 +00:00
|
|
|
'internal link with ./ and ../': {
|
|
|
|
'html': '<p><a rel="mw:WikiLink" href="./../../../Foo/Bar">Foo</a></p>',
|
Preserve whitespace between elements
This commit fully utilizes all four positions in the internal.whitespace
array. Outer whitespace is now preserved as well, and is duplicated
either in the adjacent sibling (one node's outerPost is the next
sibling's outerPre) or in the parent (a branch node's innerPre is its
first child's outerPre, and its innerPost is its last child's
outerPost). Before restoring saved whitespace, we check that these two
agree with each other, and if they disagree we assume the user has been
moving stuff around and don't restore any whitespace in that spot. The
whitespace at the very beginning and the very end of the document (i.e.
the first node's outerPre and the last node's outerPost) isn't
duplicated anywhere, nor is inner whitespace in content nodes.
The basic outline of the implementation is:
* When we encounter whitespace, strip it and store it in the previous
node's outerPost. Also store it in nextWhitespace so we can put it in
the next node's outerPre once we encounter that node.
* When we encounter whitespace in wrapped bare text, we don't know in
advance if it's gonna be succeeded by more non-whitespace (in which
case it needs to be output verbatim), or not (in which case it's
leading whitespace and needs to be stripped and stored). The fact that
annotations are nodes in HTML makes this trickier. So we write the
whitespace to the temporary linmod and store it in wrappedWhitespace,
then if it turns out to be trailing whitespace we take it back out of
the data array and record it the usual way.
* Because text nodes can contain any combination of leading whitespace
actual text and trailing whitespace, and because we may or may not
already have opened a wrapping paragraph, there are a lot of different
combinations to handle. We handle all of them but the resulting code
is pretty dense and verbose.
More low-level list of changes:
In getDataFromDom():
* Added helper function addWhitespace() for storing whitespace for an
element
* Added helper function processNextWhitespace() for processing any
whitespace passed on from the previous node via the nextWhitespace var
* Rename paragraph to wrappingParagraph. Make wrapping default to
alreadyWrapped so we can simplify wrapping||alreadyWrapped and
!wrapping&&!alreadyWrapped. Add wrappingIsOurs to track whether the
wrapping originated in this recursion level (needed for deciding when
to close the wrapper).
* Add prevElement to track the previous element so we can propagate
whitespace to it, and nextWhitespace so we can propagate whitespace to
the next element.
* Remove previous newline stripping hacks
* Integrate the logic for wrapping bare content with the outer
whitespace preservation code
* Remove wrapperElement, no longer needed because we have a dedicated
variable for the wrapping paragraph now and what was previously inner
whitespace preservation for wrapper paragraphs is now covered by the
outer whitespace preservation code.
In getDomFromData():
* Reinsert whitespace where appropriate
** outerPre is inserted when opening the element
** This covers outerPost as well except for the last child's outerPost,
which is handled as the parent's innerPost when closing the parent.
** innerPre and innerPost are inserted when closing the element. Care is
taken not to insert these if they're duplicates of something else.
* Propagate each node's outerPost to the next node (either the next
sibling or the parent) using parentDomElement.lastOuterPost. We can't
get this using .lastChild because we will have destroyed that child's
.veInternal by then, and we can't tell whether a node will be its
parent's last child when we process it (all other processing,
including first child handling is done when processing the node itself,
but this cannot be).
* Special handling is needed for the last node's outerPost, which ends
up in the container's .lastOuterPost property.
Tests:
* Allow .html to be null in data<->DOM converter tests. This indicates
that the test is a one-way data->DOM test, not a DOM->data->DOM
round-trip test. The data will be converted to HTML and checked
against .normalizedHtml
* Update existing tests as needed
* Add tests for outer whitespace preservation and storage
* Add test for squashing of whitespace in case of disagreement (this
requires .html=null)
Change-Id: I4db4fe372a421182e80a2535657af7784ff15f95
2012-08-21 00:37:42 +00:00
|
|
|
'normalizedHtml': '<p><a rel="mw:WikiLink" href="Foo/Bar">Foo</a></p>', // FIXME preserve ./ and ../
|
2012-08-14 00:59:16 +00:00
|
|
|
'data': [
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
[
|
|
|
|
'F',
|
|
|
|
{
|
2012-08-23 21:29:43 +00:00
|
|
|
'{"data":{"htmlAttributes":{"href":"./../../../Foo/Bar","rel":"mw:WikiLink"},"title":"Foo/Bar"},"type":"link/WikiLink"}': {
|
2012-08-14 00:59:16 +00:00
|
|
|
'type': 'link/WikiLink',
|
|
|
|
'data': {
|
|
|
|
'title': 'Foo/Bar',
|
|
|
|
'htmlAttributes': {
|
2012-08-23 21:29:43 +00:00
|
|
|
'href': './../../../Foo/Bar',
|
2012-08-14 00:59:16 +00:00
|
|
|
'rel': 'mw:WikiLink'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'o',
|
|
|
|
{
|
2012-08-23 21:29:43 +00:00
|
|
|
'{"data":{"htmlAttributes":{"href":"./../../../Foo/Bar","rel":"mw:WikiLink"},"title":"Foo/Bar"},"type":"link/WikiLink"}': {
|
2012-08-14 00:59:16 +00:00
|
|
|
'type': 'link/WikiLink',
|
|
|
|
'data': {
|
|
|
|
'title': 'Foo/Bar',
|
|
|
|
'htmlAttributes': {
|
2012-08-23 21:29:43 +00:00
|
|
|
'href': './../../../Foo/Bar',
|
2012-08-14 00:59:16 +00:00
|
|
|
'rel': 'mw:WikiLink'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'o',
|
|
|
|
{
|
2012-08-23 21:29:43 +00:00
|
|
|
'{"data":{"htmlAttributes":{"href":"./../../../Foo/Bar","rel":"mw:WikiLink"},"title":"Foo/Bar"},"type":"link/WikiLink"}': {
|
2012-08-14 00:59:16 +00:00
|
|
|
'type': 'link/WikiLink',
|
|
|
|
'data': {
|
|
|
|
'title': 'Foo/Bar',
|
|
|
|
'htmlAttributes': {
|
2012-08-23 21:29:43 +00:00
|
|
|
'href': './../../../Foo/Bar',
|
2012-08-14 00:59:16 +00:00
|
|
|
'rel': 'mw:WikiLink'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
2012-08-14 00:50:43 +00:00
|
|
|
'numbered external link': {
|
|
|
|
'html': '<p><a rel="mw:ExtLink/Numbered" href="http://www.mediawiki.org/">[1]</a></p>',
|
|
|
|
'data': [
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
[
|
|
|
|
'[',
|
|
|
|
{
|
2012-08-15 18:14:44 +00:00
|
|
|
'{"data":{"href":"http://www.mediawiki.org/","htmlAttributes":{"href":"http://www.mediawiki.org/","rel":"mw:ExtLink/Numbered"}},"type":"link/ExtLink/Numbered"}': {
|
2012-08-14 00:50:43 +00:00
|
|
|
'type': 'link/ExtLink/Numbered',
|
|
|
|
'data': {
|
|
|
|
'href': 'http://www.mediawiki.org/',
|
|
|
|
'htmlAttributes': {
|
|
|
|
'href': 'http://www.mediawiki.org/',
|
|
|
|
'rel': 'mw:ExtLink/Numbered'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'1',
|
|
|
|
{
|
2012-08-15 18:14:44 +00:00
|
|
|
'{"data":{"href":"http://www.mediawiki.org/","htmlAttributes":{"href":"http://www.mediawiki.org/","rel":"mw:ExtLink/Numbered"}},"type":"link/ExtLink/Numbered"}': {
|
2012-08-14 00:50:43 +00:00
|
|
|
'type': 'link/ExtLink/Numbered',
|
|
|
|
'data': {
|
|
|
|
'href': 'http://www.mediawiki.org/',
|
|
|
|
'htmlAttributes': {
|
|
|
|
'href': 'http://www.mediawiki.org/',
|
|
|
|
'rel': 'mw:ExtLink/Numbered'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[
|
|
|
|
']',
|
|
|
|
{
|
2012-08-15 18:14:44 +00:00
|
|
|
'{"data":{"href":"http://www.mediawiki.org/","htmlAttributes":{"href":"http://www.mediawiki.org/","rel":"mw:ExtLink/Numbered"}},"type":"link/ExtLink/Numbered"}': {
|
2012-08-14 00:50:43 +00:00
|
|
|
'type': 'link/ExtLink/Numbered',
|
|
|
|
'data': {
|
|
|
|
'href': 'http://www.mediawiki.org/',
|
|
|
|
'htmlAttributes': {
|
|
|
|
'href': 'http://www.mediawiki.org/',
|
|
|
|
'rel': 'mw:ExtLink/Numbered'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'URL link': {
|
|
|
|
'html': '<p><a rel="mw:ExtLink/URL" href="http://www.mediawiki.org/">mw</a></p>',
|
|
|
|
'data': [
|
|
|
|
{ 'type': 'paragraph' },
|
|
|
|
[
|
|
|
|
'm',
|
|
|
|
{
|
2012-08-15 18:14:44 +00:00
|
|
|
'{"data":{"href":"http://www.mediawiki.org/","htmlAttributes":{"href":"http://www.mediawiki.org/","rel":"mw:ExtLink/URL"}},"type":"link/ExtLink/URL"}': {
|
2012-08-14 00:50:43 +00:00
|
|
|
'type': 'link/ExtLink/URL',
|
|
|
|
'data': {
|
|
|
|
'href': 'http://www.mediawiki.org/',
|
|
|
|
'htmlAttributes': {
|
|
|
|
'href': 'http://www.mediawiki.org/',
|
|
|
|
'rel': 'mw:ExtLink/URL'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'w',
|
|
|
|
{
|
2012-08-15 18:14:44 +00:00
|
|
|
'{"data":{"href":"http://www.mediawiki.org/","htmlAttributes":{"href":"http://www.mediawiki.org/","rel":"mw:ExtLink/URL"}},"type":"link/ExtLink/URL"}': {
|
2012-08-14 00:50:43 +00:00
|
|
|
'type': 'link/ExtLink/URL',
|
|
|
|
'data': {
|
|
|
|
'href': 'http://www.mediawiki.org/',
|
|
|
|
'htmlAttributes': {
|
|
|
|
'href': 'http://www.mediawiki.org/',
|
|
|
|
'rel': 'mw:ExtLink/URL'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
'whitespace preservation in headings': {
|
|
|
|
'html': '<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 },
|
2012-08-16 17:53:33 +00:00
|
|
|
'internal': { 'whitespace': [ undefined, ' ' ] }
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
},
|
|
|
|
'B',
|
|
|
|
'a',
|
|
|
|
'r',
|
|
|
|
{ 'type': '/heading' },
|
|
|
|
{
|
|
|
|
'type': 'heading',
|
|
|
|
'attributes': { 'level': 2 },
|
2012-08-16 17:53:33 +00:00
|
|
|
'internal': { 'whitespace': [ undefined, undefined, ' ' ] }
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
},
|
|
|
|
'B',
|
|
|
|
'a',
|
|
|
|
'z',
|
|
|
|
{ 'type': '/heading' },
|
|
|
|
{
|
|
|
|
'type': 'heading',
|
|
|
|
'attributes': { 'level': 2 },
|
2012-08-16 17:53:33 +00:00
|
|
|
'internal': { 'whitespace': [ undefined, ' ', ' ' ] }
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
},
|
|
|
|
'Q',
|
|
|
|
'u',
|
|
|
|
'u',
|
|
|
|
'x',
|
|
|
|
{ 'type': '/heading' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'whitespace preservation in list items': {
|
|
|
|
'html': '<ul><li>Foo</li><li> Bar</li><li>Baz </li><li> Quux </li></ul>',
|
|
|
|
'data': [
|
|
|
|
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
|
|
|
|
{ 'type': 'listItem' },
|
2012-08-16 20:06:18 +00:00
|
|
|
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
'F',
|
|
|
|
'o',
|
|
|
|
'o',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' },
|
Preserve whitespace between elements
This commit fully utilizes all four positions in the internal.whitespace
array. Outer whitespace is now preserved as well, and is duplicated
either in the adjacent sibling (one node's outerPost is the next
sibling's outerPre) or in the parent (a branch node's innerPre is its
first child's outerPre, and its innerPost is its last child's
outerPost). Before restoring saved whitespace, we check that these two
agree with each other, and if they disagree we assume the user has been
moving stuff around and don't restore any whitespace in that spot. The
whitespace at the very beginning and the very end of the document (i.e.
the first node's outerPre and the last node's outerPost) isn't
duplicated anywhere, nor is inner whitespace in content nodes.
The basic outline of the implementation is:
* When we encounter whitespace, strip it and store it in the previous
node's outerPost. Also store it in nextWhitespace so we can put it in
the next node's outerPre once we encounter that node.
* When we encounter whitespace in wrapped bare text, we don't know in
advance if it's gonna be succeeded by more non-whitespace (in which
case it needs to be output verbatim), or not (in which case it's
leading whitespace and needs to be stripped and stored). The fact that
annotations are nodes in HTML makes this trickier. So we write the
whitespace to the temporary linmod and store it in wrappedWhitespace,
then if it turns out to be trailing whitespace we take it back out of
the data array and record it the usual way.
* Because text nodes can contain any combination of leading whitespace
actual text and trailing whitespace, and because we may or may not
already have opened a wrapping paragraph, there are a lot of different
combinations to handle. We handle all of them but the resulting code
is pretty dense and verbose.
More low-level list of changes:
In getDataFromDom():
* Added helper function addWhitespace() for storing whitespace for an
element
* Added helper function processNextWhitespace() for processing any
whitespace passed on from the previous node via the nextWhitespace var
* Rename paragraph to wrappingParagraph. Make wrapping default to
alreadyWrapped so we can simplify wrapping||alreadyWrapped and
!wrapping&&!alreadyWrapped. Add wrappingIsOurs to track whether the
wrapping originated in this recursion level (needed for deciding when
to close the wrapper).
* Add prevElement to track the previous element so we can propagate
whitespace to it, and nextWhitespace so we can propagate whitespace to
the next element.
* Remove previous newline stripping hacks
* Integrate the logic for wrapping bare content with the outer
whitespace preservation code
* Remove wrapperElement, no longer needed because we have a dedicated
variable for the wrapping paragraph now and what was previously inner
whitespace preservation for wrapper paragraphs is now covered by the
outer whitespace preservation code.
In getDomFromData():
* Reinsert whitespace where appropriate
** outerPre is inserted when opening the element
** This covers outerPost as well except for the last child's outerPost,
which is handled as the parent's innerPost when closing the parent.
** innerPre and innerPost are inserted when closing the element. Care is
taken not to insert these if they're duplicates of something else.
* Propagate each node's outerPost to the next node (either the next
sibling or the parent) using parentDomElement.lastOuterPost. We can't
get this using .lastChild because we will have destroyed that child's
.veInternal by then, and we can't tell whether a node will be its
parent's last child when we process it (all other processing,
including first child handling is done when processing the node itself,
but this cannot be).
* Special handling is needed for the last node's outerPost, which ends
up in the container's .lastOuterPost property.
Tests:
* Allow .html to be null in data<->DOM converter tests. This indicates
that the test is a one-way data->DOM test, not a DOM->data->DOM
round-trip test. The data will be converted to HTML and checked
against .normalizedHtml
* Update existing tests as needed
* Add tests for outer whitespace preservation and storage
* Add test for squashing of whitespace in case of disagreement (this
requires .html=null)
Change-Id: I4db4fe372a421182e80a2535657af7784ff15f95
2012-08-21 00:37:42 +00:00
|
|
|
{ 'type': 'listItem', 'internal': { 'whitespace': [ undefined, ' ' ]} },
|
|
|
|
{ 'type': 'paragraph', 'internal': { 'whitespace': [ ' ' ], 'generated': 'wrapper' } },
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
'B',
|
|
|
|
'a',
|
|
|
|
'r',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' },
|
Preserve whitespace between elements
This commit fully utilizes all four positions in the internal.whitespace
array. Outer whitespace is now preserved as well, and is duplicated
either in the adjacent sibling (one node's outerPost is the next
sibling's outerPre) or in the parent (a branch node's innerPre is its
first child's outerPre, and its innerPost is its last child's
outerPost). Before restoring saved whitespace, we check that these two
agree with each other, and if they disagree we assume the user has been
moving stuff around and don't restore any whitespace in that spot. The
whitespace at the very beginning and the very end of the document (i.e.
the first node's outerPre and the last node's outerPost) isn't
duplicated anywhere, nor is inner whitespace in content nodes.
The basic outline of the implementation is:
* When we encounter whitespace, strip it and store it in the previous
node's outerPost. Also store it in nextWhitespace so we can put it in
the next node's outerPre once we encounter that node.
* When we encounter whitespace in wrapped bare text, we don't know in
advance if it's gonna be succeeded by more non-whitespace (in which
case it needs to be output verbatim), or not (in which case it's
leading whitespace and needs to be stripped and stored). The fact that
annotations are nodes in HTML makes this trickier. So we write the
whitespace to the temporary linmod and store it in wrappedWhitespace,
then if it turns out to be trailing whitespace we take it back out of
the data array and record it the usual way.
* Because text nodes can contain any combination of leading whitespace
actual text and trailing whitespace, and because we may or may not
already have opened a wrapping paragraph, there are a lot of different
combinations to handle. We handle all of them but the resulting code
is pretty dense and verbose.
More low-level list of changes:
In getDataFromDom():
* Added helper function addWhitespace() for storing whitespace for an
element
* Added helper function processNextWhitespace() for processing any
whitespace passed on from the previous node via the nextWhitespace var
* Rename paragraph to wrappingParagraph. Make wrapping default to
alreadyWrapped so we can simplify wrapping||alreadyWrapped and
!wrapping&&!alreadyWrapped. Add wrappingIsOurs to track whether the
wrapping originated in this recursion level (needed for deciding when
to close the wrapper).
* Add prevElement to track the previous element so we can propagate
whitespace to it, and nextWhitespace so we can propagate whitespace to
the next element.
* Remove previous newline stripping hacks
* Integrate the logic for wrapping bare content with the outer
whitespace preservation code
* Remove wrapperElement, no longer needed because we have a dedicated
variable for the wrapping paragraph now and what was previously inner
whitespace preservation for wrapper paragraphs is now covered by the
outer whitespace preservation code.
In getDomFromData():
* Reinsert whitespace where appropriate
** outerPre is inserted when opening the element
** This covers outerPost as well except for the last child's outerPost,
which is handled as the parent's innerPost when closing the parent.
** innerPre and innerPost are inserted when closing the element. Care is
taken not to insert these if they're duplicates of something else.
* Propagate each node's outerPost to the next node (either the next
sibling or the parent) using parentDomElement.lastOuterPost. We can't
get this using .lastChild because we will have destroyed that child's
.veInternal by then, and we can't tell whether a node will be its
parent's last child when we process it (all other processing,
including first child handling is done when processing the node itself,
but this cannot be).
* Special handling is needed for the last node's outerPost, which ends
up in the container's .lastOuterPost property.
Tests:
* Allow .html to be null in data<->DOM converter tests. This indicates
that the test is a one-way data->DOM test, not a DOM->data->DOM
round-trip test. The data will be converted to HTML and checked
against .normalizedHtml
* Update existing tests as needed
* Add tests for outer whitespace preservation and storage
* Add test for squashing of whitespace in case of disagreement (this
requires .html=null)
Change-Id: I4db4fe372a421182e80a2535657af7784ff15f95
2012-08-21 00:37:42 +00:00
|
|
|
{ 'type': 'listItem', 'internal': { 'whitespace': [ undefined, undefined, ' ' ] } },
|
|
|
|
{ 'type': 'paragraph', 'internal': { 'whitespace': [ undefined, undefined, undefined, ' ' ], 'generated': 'wrapper' } },
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
'B',
|
|
|
|
'a',
|
|
|
|
'z',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' },
|
Preserve whitespace between elements
This commit fully utilizes all four positions in the internal.whitespace
array. Outer whitespace is now preserved as well, and is duplicated
either in the adjacent sibling (one node's outerPost is the next
sibling's outerPre) or in the parent (a branch node's innerPre is its
first child's outerPre, and its innerPost is its last child's
outerPost). Before restoring saved whitespace, we check that these two
agree with each other, and if they disagree we assume the user has been
moving stuff around and don't restore any whitespace in that spot. The
whitespace at the very beginning and the very end of the document (i.e.
the first node's outerPre and the last node's outerPost) isn't
duplicated anywhere, nor is inner whitespace in content nodes.
The basic outline of the implementation is:
* When we encounter whitespace, strip it and store it in the previous
node's outerPost. Also store it in nextWhitespace so we can put it in
the next node's outerPre once we encounter that node.
* When we encounter whitespace in wrapped bare text, we don't know in
advance if it's gonna be succeeded by more non-whitespace (in which
case it needs to be output verbatim), or not (in which case it's
leading whitespace and needs to be stripped and stored). The fact that
annotations are nodes in HTML makes this trickier. So we write the
whitespace to the temporary linmod and store it in wrappedWhitespace,
then if it turns out to be trailing whitespace we take it back out of
the data array and record it the usual way.
* Because text nodes can contain any combination of leading whitespace
actual text and trailing whitespace, and because we may or may not
already have opened a wrapping paragraph, there are a lot of different
combinations to handle. We handle all of them but the resulting code
is pretty dense and verbose.
More low-level list of changes:
In getDataFromDom():
* Added helper function addWhitespace() for storing whitespace for an
element
* Added helper function processNextWhitespace() for processing any
whitespace passed on from the previous node via the nextWhitespace var
* Rename paragraph to wrappingParagraph. Make wrapping default to
alreadyWrapped so we can simplify wrapping||alreadyWrapped and
!wrapping&&!alreadyWrapped. Add wrappingIsOurs to track whether the
wrapping originated in this recursion level (needed for deciding when
to close the wrapper).
* Add prevElement to track the previous element so we can propagate
whitespace to it, and nextWhitespace so we can propagate whitespace to
the next element.
* Remove previous newline stripping hacks
* Integrate the logic for wrapping bare content with the outer
whitespace preservation code
* Remove wrapperElement, no longer needed because we have a dedicated
variable for the wrapping paragraph now and what was previously inner
whitespace preservation for wrapper paragraphs is now covered by the
outer whitespace preservation code.
In getDomFromData():
* Reinsert whitespace where appropriate
** outerPre is inserted when opening the element
** This covers outerPost as well except for the last child's outerPost,
which is handled as the parent's innerPost when closing the parent.
** innerPre and innerPost are inserted when closing the element. Care is
taken not to insert these if they're duplicates of something else.
* Propagate each node's outerPost to the next node (either the next
sibling or the parent) using parentDomElement.lastOuterPost. We can't
get this using .lastChild because we will have destroyed that child's
.veInternal by then, and we can't tell whether a node will be its
parent's last child when we process it (all other processing,
including first child handling is done when processing the node itself,
but this cannot be).
* Special handling is needed for the last node's outerPost, which ends
up in the container's .lastOuterPost property.
Tests:
* Allow .html to be null in data<->DOM converter tests. This indicates
that the test is a one-way data->DOM test, not a DOM->data->DOM
round-trip test. The data will be converted to HTML and checked
against .normalizedHtml
* Update existing tests as needed
* Add tests for outer whitespace preservation and storage
* Add test for squashing of whitespace in case of disagreement (this
requires .html=null)
Change-Id: I4db4fe372a421182e80a2535657af7784ff15f95
2012-08-21 00:37:42 +00:00
|
|
|
{ 'type': 'listItem', 'internal': { 'whitespace': [ undefined, ' ', ' '] } },
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
{
|
|
|
|
'type': 'paragraph',
|
Preserve whitespace between elements
This commit fully utilizes all four positions in the internal.whitespace
array. Outer whitespace is now preserved as well, and is duplicated
either in the adjacent sibling (one node's outerPost is the next
sibling's outerPre) or in the parent (a branch node's innerPre is its
first child's outerPre, and its innerPost is its last child's
outerPost). Before restoring saved whitespace, we check that these two
agree with each other, and if they disagree we assume the user has been
moving stuff around and don't restore any whitespace in that spot. The
whitespace at the very beginning and the very end of the document (i.e.
the first node's outerPre and the last node's outerPost) isn't
duplicated anywhere, nor is inner whitespace in content nodes.
The basic outline of the implementation is:
* When we encounter whitespace, strip it and store it in the previous
node's outerPost. Also store it in nextWhitespace so we can put it in
the next node's outerPre once we encounter that node.
* When we encounter whitespace in wrapped bare text, we don't know in
advance if it's gonna be succeeded by more non-whitespace (in which
case it needs to be output verbatim), or not (in which case it's
leading whitespace and needs to be stripped and stored). The fact that
annotations are nodes in HTML makes this trickier. So we write the
whitespace to the temporary linmod and store it in wrappedWhitespace,
then if it turns out to be trailing whitespace we take it back out of
the data array and record it the usual way.
* Because text nodes can contain any combination of leading whitespace
actual text and trailing whitespace, and because we may or may not
already have opened a wrapping paragraph, there are a lot of different
combinations to handle. We handle all of them but the resulting code
is pretty dense and verbose.
More low-level list of changes:
In getDataFromDom():
* Added helper function addWhitespace() for storing whitespace for an
element
* Added helper function processNextWhitespace() for processing any
whitespace passed on from the previous node via the nextWhitespace var
* Rename paragraph to wrappingParagraph. Make wrapping default to
alreadyWrapped so we can simplify wrapping||alreadyWrapped and
!wrapping&&!alreadyWrapped. Add wrappingIsOurs to track whether the
wrapping originated in this recursion level (needed for deciding when
to close the wrapper).
* Add prevElement to track the previous element so we can propagate
whitespace to it, and nextWhitespace so we can propagate whitespace to
the next element.
* Remove previous newline stripping hacks
* Integrate the logic for wrapping bare content with the outer
whitespace preservation code
* Remove wrapperElement, no longer needed because we have a dedicated
variable for the wrapping paragraph now and what was previously inner
whitespace preservation for wrapper paragraphs is now covered by the
outer whitespace preservation code.
In getDomFromData():
* Reinsert whitespace where appropriate
** outerPre is inserted when opening the element
** This covers outerPost as well except for the last child's outerPost,
which is handled as the parent's innerPost when closing the parent.
** innerPre and innerPost are inserted when closing the element. Care is
taken not to insert these if they're duplicates of something else.
* Propagate each node's outerPost to the next node (either the next
sibling or the parent) using parentDomElement.lastOuterPost. We can't
get this using .lastChild because we will have destroyed that child's
.veInternal by then, and we can't tell whether a node will be its
parent's last child when we process it (all other processing,
including first child handling is done when processing the node itself,
but this cannot be).
* Special handling is needed for the last node's outerPost, which ends
up in the container's .lastOuterPost property.
Tests:
* Allow .html to be null in data<->DOM converter tests. This indicates
that the test is a one-way data->DOM test, not a DOM->data->DOM
round-trip test. The data will be converted to HTML and checked
against .normalizedHtml
* Update existing tests as needed
* Add tests for outer whitespace preservation and storage
* Add test for squashing of whitespace in case of disagreement (this
requires .html=null)
Change-Id: I4db4fe372a421182e80a2535657af7784ff15f95
2012-08-21 00:37:42 +00:00
|
|
|
'internal': { 'whitespace': [ ' ', undefined, undefined, ' ' ], 'generated': 'wrapper' }
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
},
|
|
|
|
'Q',
|
|
|
|
'u',
|
|
|
|
'u',
|
|
|
|
'x',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': '/listItem' },
|
|
|
|
{ 'type': '/list' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'whitespace preservation with annotations': {
|
|
|
|
'html': '<p> <i> Foo </i> </p>',
|
|
|
|
'data': [
|
|
|
|
{
|
|
|
|
'type': 'paragraph',
|
2012-08-16 17:53:33 +00:00
|
|
|
'internal': { 'whitespace': [ undefined, ' ', ' ' ] }
|
Strip and preserve inner leading&trailing whitespace in the linear model
This makes things like
== Foo ==
* Bar
render without the leading and trailing spaces, while still
round-tripping those spaces.
* Added a .fringeWhitespace property to the linear model and ve.dm.Node
** Object containing innerPre, innerPost, outerPre, outerPost
** Only inner* are used right now, outer* are planned for future use
** Like .attributes , it's suppressed if it's an empty object
* In getDataFromDom():
** Store the stripped whitespace in .fringeWhitespace
** Move emptiness check up: empty elements with .fringeWhitespace have
to be preserved
** Move paragraph wrapping up: .fringeWhitespace has to be applied to
the generated paragraph, not its parent
** Add wrapperElement to keep track of the element .fringeWhitespace has
to be added to; this is either dataElement or the generated paragraph
or nothing, but we can't modify dataElement because it's used later
* In getDomFromData():
** When processing an opening, store the fringeWhitespace data in the
generated DOM node
** When processing a closing, add the stored whitespace back in
* In the ve.dm.Document constructor, pass through .fringeWhitespace from
the linear model data to the generated nodes
Tests:
* Change one existing test case to account for this change
* Add three new test cases for this behavior
* Add normalizedHtml field so I can test behavior with bare content
Change-Id: I0411544652dd72b923c831c495d69ee4322a2c14
2012-08-10 21:09:04 +00:00
|
|
|
},
|
|
|
|
[ ' ', { '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ 'F', { '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ 'o', { '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ 'o', { '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
Preserve whitespace between elements
This commit fully utilizes all four positions in the internal.whitespace
array. Outer whitespace is now preserved as well, and is duplicated
either in the adjacent sibling (one node's outerPost is the next
sibling's outerPre) or in the parent (a branch node's innerPre is its
first child's outerPre, and its innerPost is its last child's
outerPost). Before restoring saved whitespace, we check that these two
agree with each other, and if they disagree we assume the user has been
moving stuff around and don't restore any whitespace in that spot. The
whitespace at the very beginning and the very end of the document (i.e.
the first node's outerPre and the last node's outerPost) isn't
duplicated anywhere, nor is inner whitespace in content nodes.
The basic outline of the implementation is:
* When we encounter whitespace, strip it and store it in the previous
node's outerPost. Also store it in nextWhitespace so we can put it in
the next node's outerPre once we encounter that node.
* When we encounter whitespace in wrapped bare text, we don't know in
advance if it's gonna be succeeded by more non-whitespace (in which
case it needs to be output verbatim), or not (in which case it's
leading whitespace and needs to be stripped and stored). The fact that
annotations are nodes in HTML makes this trickier. So we write the
whitespace to the temporary linmod and store it in wrappedWhitespace,
then if it turns out to be trailing whitespace we take it back out of
the data array and record it the usual way.
* Because text nodes can contain any combination of leading whitespace
actual text and trailing whitespace, and because we may or may not
already have opened a wrapping paragraph, there are a lot of different
combinations to handle. We handle all of them but the resulting code
is pretty dense and verbose.
More low-level list of changes:
In getDataFromDom():
* Added helper function addWhitespace() for storing whitespace for an
element
* Added helper function processNextWhitespace() for processing any
whitespace passed on from the previous node via the nextWhitespace var
* Rename paragraph to wrappingParagraph. Make wrapping default to
alreadyWrapped so we can simplify wrapping||alreadyWrapped and
!wrapping&&!alreadyWrapped. Add wrappingIsOurs to track whether the
wrapping originated in this recursion level (needed for deciding when
to close the wrapper).
* Add prevElement to track the previous element so we can propagate
whitespace to it, and nextWhitespace so we can propagate whitespace to
the next element.
* Remove previous newline stripping hacks
* Integrate the logic for wrapping bare content with the outer
whitespace preservation code
* Remove wrapperElement, no longer needed because we have a dedicated
variable for the wrapping paragraph now and what was previously inner
whitespace preservation for wrapper paragraphs is now covered by the
outer whitespace preservation code.
In getDomFromData():
* Reinsert whitespace where appropriate
** outerPre is inserted when opening the element
** This covers outerPost as well except for the last child's outerPost,
which is handled as the parent's innerPost when closing the parent.
** innerPre and innerPost are inserted when closing the element. Care is
taken not to insert these if they're duplicates of something else.
* Propagate each node's outerPost to the next node (either the next
sibling or the parent) using parentDomElement.lastOuterPost. We can't
get this using .lastChild because we will have destroyed that child's
.veInternal by then, and we can't tell whether a node will be its
parent's last child when we process it (all other processing,
including first child handling is done when processing the node itself,
but this cannot be).
* Special handling is needed for the last node's outerPost, which ends
up in the container's .lastOuterPost property.
Tests:
* Allow .html to be null in data<->DOM converter tests. This indicates
that the test is a one-way data->DOM test, not a DOM->data->DOM
round-trip test. The data will be converted to HTML and checked
against .normalizedHtml
* Update existing tests as needed
* Add tests for outer whitespace preservation and storage
* Add test for squashing of whitespace in case of disagreement (this
requires .html=null)
Change-Id: I4db4fe372a421182e80a2535657af7784ff15f95
2012-08-21 00:37:42 +00:00
|
|
|
},
|
|
|
|
'outer whitespace preservation in a list with bare text and a wrapper paragraph': {
|
|
|
|
'html': '\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' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'outer whitespace preservation in a list with bare text and a sublist': {
|
|
|
|
'html': '<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' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'whitespace preservation leaves non-edge content whitespace alone': {
|
|
|
|
'html': '<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',
|
|
|
|
' ',
|
|
|
|
' ',
|
|
|
|
' ',
|
|
|
|
[ ' ', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ 'C', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
'\t',
|
|
|
|
'\t',
|
|
|
|
'D',
|
|
|
|
{ 'type': '/paragraph' },
|
|
|
|
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper', 'whitespace': [ '\n', undefined, undefined, ' ' ] } },
|
|
|
|
'E',
|
|
|
|
'\n',
|
|
|
|
'\n',
|
|
|
|
'F',
|
|
|
|
'\n',
|
|
|
|
'\n',
|
|
|
|
'\n',
|
|
|
|
[ '\n', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\n', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\n', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\n', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ 'G', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
' ',
|
|
|
|
' ',
|
|
|
|
'H',
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'whitespace preservation with non-edge content whitespace with nested annotations': {
|
|
|
|
'html': '<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',
|
|
|
|
' ',
|
|
|
|
' ',
|
|
|
|
' ',
|
|
|
|
[ ' ', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ ' ', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ 'C', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ 'D', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ 'E', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\n', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
'\n',
|
|
|
|
'\n',
|
|
|
|
'F',
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'whitespace preservation with tightly nested annotations': {
|
|
|
|
'html': '<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', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ 'C', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
'\n',
|
|
|
|
'\n',
|
|
|
|
'D',
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'whitespace preservation with nested annotations with whitespace on the left side': {
|
|
|
|
'html': '<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', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ 'C', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
'\n',
|
|
|
|
'\n',
|
|
|
|
'D',
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'whitespace preservation with nested annotations with whitespace on the right side': {
|
|
|
|
'html': '<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', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ 'C', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' }, '{"type":"textStyle/italic"}': { 'type': 'textStyle/italic' } } ],
|
|
|
|
[ '\n', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
[ '\t', { '{"type":"textStyle/bold"}': { 'type': 'textStyle/bold' } } ],
|
|
|
|
'\n',
|
|
|
|
'\n',
|
|
|
|
'D',
|
|
|
|
{ 'type': '/paragraph' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'mismatching whitespace data is ignored': {
|
|
|
|
'html': null,
|
|
|
|
'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': '/list' }
|
|
|
|
],
|
|
|
|
'normalizedHtml': ' <ul><li><p>\tA\n</p> <p>B</p></li></ul> '
|
2012-08-10 23:49:14 +00:00
|
|
|
}
|
2012-06-08 05:00:25 +00:00
|
|
|
};
|