mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-15 18:39:52 +00:00
Added optional "unrestricted" argument to isStructuralOffset
Using this argument will only return true if the offset is a place you can add any element to (hence the unrestricted part of it). This is good for testing if a paragraph could potentially be inserted there. Change-Id: I6cc91da437c52493de03eb687b28966198270fea
This commit is contained in:
parent
fe1482e0c4
commit
541d786ced
|
@ -207,19 +207,35 @@ ve.dm.Document.isContentOffset = function( data, offset ) {
|
||||||
/**
|
/**
|
||||||
* Checks if structure can be inserted at an offset in document data.
|
* Checks if structure can be inserted at an offset in document data.
|
||||||
*
|
*
|
||||||
|
* If the {unrestricted} param is true than only offsets where any kind of element can be inserted
|
||||||
|
* will return true. This can be used to detect the difference between a location that a paragraph
|
||||||
|
* can be inserted, such as between two tables but not direclty inside a table.
|
||||||
|
*
|
||||||
* This method assumes that any value that has a type property that's a string is an element object.
|
* This method assumes that any value that has a type property that's a string is an element object.
|
||||||
*
|
*
|
||||||
* @example Structural offsets:
|
* @example Structural offsets (unrestricted = false):
|
||||||
* <heading> a </heading> <paragraph> b c <img> </img> </paragraph>
|
* <heading> a </heading> <paragraph> b c <img> </img> </paragraph>
|
||||||
* ^ . . ^ . . . . . ^
|
* ^ . . ^ . . . . . ^
|
||||||
*
|
*
|
||||||
|
* @example Structural offsets (unrestricted = true):
|
||||||
|
* <heading> a </heading> <paragraph> b c <img> </img> </paragraph>
|
||||||
|
* ^ . . ^ . . . . . ^
|
||||||
|
*
|
||||||
|
* @example Structural offsets (unrestricted = false):
|
||||||
|
* <list> <listItem> </listItem> <list>
|
||||||
|
* ^ ^ ^ ^ ^
|
||||||
|
*
|
||||||
|
* @example Content branch offsets (unrestricted = true):
|
||||||
|
* <list> <listItem> </listItem> <list>
|
||||||
|
* ^ . ^ . ^
|
||||||
|
*
|
||||||
* @static
|
* @static
|
||||||
* @method
|
* @method
|
||||||
* @param {Array} data Document data
|
* @param {Array} data Document data
|
||||||
* @param {Integer} offset Document offset
|
* @param {Integer} offset Document offset
|
||||||
* @returns {Boolean} Structure can be inserted at offset
|
* @returns {Boolean} Structure can be inserted at offset
|
||||||
*/
|
*/
|
||||||
ve.dm.Document.isStructuralOffset = function( data, offset ) {
|
ve.dm.Document.isStructuralOffset = function( data, offset, unrestricted ) {
|
||||||
// Edges are always structural
|
// Edges are always structural
|
||||||
if ( offset === 0 || offset === data.length ) {
|
if ( offset === 0 || offset === data.length ) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -242,15 +258,31 @@ ve.dm.Document.isStructuralOffset = function( data, offset ) {
|
||||||
// Is a closing
|
// Is a closing
|
||||||
left.type.charAt( 0 ) === '/' &&
|
left.type.charAt( 0 ) === '/' &&
|
||||||
// Is a branch
|
// Is a branch
|
||||||
factory.canNodeHaveChildren( left.type.substr( 1 ) )
|
factory.canNodeHaveChildren( left.type.substr( 1 ) ) &&
|
||||||
|
(
|
||||||
|
// Only apply this rule in unrestricted mode
|
||||||
|
!unrestricted ||
|
||||||
|
// Right of an unrestricted branch
|
||||||
|
// <list><listItem><paragraph>a</paragraph>|</listItem></list>|
|
||||||
|
// Both are non-content branches that can have any kind of child
|
||||||
|
factory.getParentNodeTypes( left.type.substr( 1 ) ) === null
|
||||||
|
)
|
||||||
) ||
|
) ||
|
||||||
// Left of branch
|
// Left of a branch
|
||||||
// |<list>|<listItem>|<paragraph>a</paragraph></listItem></list>
|
// |<list>|<listItem>|<paragraph>a</paragraph></listItem></list>
|
||||||
(
|
(
|
||||||
// Is not a closing
|
// Is not a closing
|
||||||
right.type.charAt( 0 ) !== '/' &&
|
right.type.charAt( 0 ) !== '/' &&
|
||||||
// Is a branch
|
// Is a branch
|
||||||
factory.canNodeHaveChildren( right.type )
|
factory.canNodeHaveChildren( right.type ) &&
|
||||||
|
(
|
||||||
|
// Only apply this rule in unrestricted mode
|
||||||
|
!unrestricted ||
|
||||||
|
// Left of an unrestricted branch
|
||||||
|
// |<list><listItem>|<paragraph>a</paragraph></listItem></list>
|
||||||
|
// Both are non-content branches that can have any kind of child
|
||||||
|
factory.getParentNodeTypes( right.type ) === null
|
||||||
|
)
|
||||||
) ||
|
) ||
|
||||||
// Inside empty non-content branch
|
// Inside empty non-content branch
|
||||||
// <list>|</list> or <list><listItem>|</listItem></list>
|
// <list>|</list> or <list><listItem>|</listItem></list>
|
||||||
|
@ -258,7 +290,13 @@ ve.dm.Document.isStructuralOffset = function( data, offset ) {
|
||||||
// Inside empty element
|
// Inside empty element
|
||||||
'/' + left.type === right.type &&
|
'/' + left.type === right.type &&
|
||||||
// Both are non-content branches (right is the same type)
|
// Both are non-content branches (right is the same type)
|
||||||
factory.canNodeHaveGrandchildren( left.type )
|
factory.canNodeHaveGrandchildren( left.type ) &&
|
||||||
|
(
|
||||||
|
// Only apply this rule in unrestricted mode
|
||||||
|
!unrestricted ||
|
||||||
|
// Both are non-content branches that can have any kind of child
|
||||||
|
factory.getChildNodeTypes( left.type ) === null
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -554,14 +554,14 @@ test( 'isContentOffset', function() {
|
||||||
{ 'msg': 'between content branches', 'expected': false },
|
{ 'msg': 'between content branches', 'expected': false },
|
||||||
{ 'msg': 'inside emtpy content branch', 'expected': true },
|
{ 'msg': 'inside emtpy content branch', 'expected': true },
|
||||||
{ 'msg': 'between content branches', 'expected': false },
|
{ 'msg': 'between content branches', 'expected': false },
|
||||||
{ 'msg': 'begining of content branch and left of inline leaf', 'expected': true },
|
{ 'msg': 'begining of content branch, left of inline leaf', 'expected': true },
|
||||||
{ 'msg': 'inside content branch with only non-text inline leaf', 'expected': false },
|
{ 'msg': 'inside content branch with non-text inline leaf', 'expected': false },
|
||||||
{ 'msg': 'end of content branch and right of block leaf', 'expected': true },
|
{ 'msg': 'end of content branch, right of block leaf', 'expected': true },
|
||||||
{ 'msg': 'between content and non-content branches', 'expected': false },
|
{ 'msg': 'between content, non-content branches', 'expected': false },
|
||||||
{ 'msg': 'between parent and child branches, descending', 'expected': false },
|
{ 'msg': 'between parent, child branches, descending', 'expected': false },
|
||||||
{ 'msg': 'inside empty non-content branch', 'expected': false },
|
{ 'msg': 'inside empty non-content branch', 'expected': false },
|
||||||
{ 'msg': 'between parent and child branches, ascending', 'expected': false },
|
{ 'msg': 'between parent, child branches, ascending', 'expected': false },
|
||||||
{ 'msg': 'between non-content branch and block leaf', 'expected': false },
|
{ 'msg': 'between non-content branch, block leaf', 'expected': false },
|
||||||
{ 'msg': 'inside block leaf', 'expected': false },
|
{ 'msg': 'inside block leaf', 'expected': false },
|
||||||
{ 'msg': 'right of document', 'expected': false }
|
{ 'msg': 'right of document', 'expected': false }
|
||||||
];
|
];
|
||||||
|
@ -594,30 +594,41 @@ test( 'isStructuralOffset', function() {
|
||||||
{ 'type': '/alienBlock' }
|
{ 'type': '/alienBlock' }
|
||||||
],
|
],
|
||||||
cases = [
|
cases = [
|
||||||
{ 'msg': 'left of document', 'expected': true },
|
{ 'msg': 'left of document', 'expected': [true, true] },
|
||||||
{ 'msg': 'begining of content branch', 'expected': false },
|
{ 'msg': 'begining of content branch', 'expected': [false, false] },
|
||||||
{ 'msg': 'left of non-text inline leaf', 'expected': false },
|
{ 'msg': 'left of non-text inline leaf', 'expected': [false, false] },
|
||||||
{ 'msg': 'inside non-text inline leaf', 'expected': false },
|
{ 'msg': 'inside non-text inline leaf', 'expected': [false, false] },
|
||||||
{ 'msg': 'right of non-text inline leaf', 'expected': false },
|
{ 'msg': 'right of non-text inline leaf', 'expected': [false, false] },
|
||||||
{ 'msg': 'between characters', 'expected': false },
|
{ 'msg': 'between characters', 'expected': [false, false] },
|
||||||
{ 'msg': 'end of content branch', 'expected': false },
|
{ 'msg': 'end of content branch', 'expected': [false, false] },
|
||||||
{ 'msg': 'between content branches', 'expected': true },
|
{ 'msg': 'between content branches', 'expected': [true, true] },
|
||||||
{ 'msg': 'inside emtpy content branch', 'expected': false },
|
{ 'msg': 'inside emtpy content branch', 'expected': [false, false] },
|
||||||
{ 'msg': 'between content branches', 'expected': true },
|
{ 'msg': 'between content branches', 'expected': [true, true] },
|
||||||
{ 'msg': 'begining of content branch and left of inline leaf', 'expected': false },
|
{ 'msg': 'begining of content branch, left of inline leaf', 'expected': [false, false] },
|
||||||
{ 'msg': 'inside content branch with only non-text inline leaf', 'expected': false },
|
{ 'msg': 'inside content branch with non-text inline leaf', 'expected': [false, false] },
|
||||||
{ 'msg': 'end of content branch and right of inline leaf', 'expected': false },
|
{ 'msg': 'end of content branch, right of inline leaf', 'expected': [false, false] },
|
||||||
{ 'msg': 'between content and non-content branches', 'expected': true },
|
{ 'msg': 'between content, non-content branches', 'expected': [true, true] },
|
||||||
{ 'msg': 'between parent and child branches, descending', 'expected': true },
|
{ 'msg': 'between parent, child branches, descending', 'expected': [true, false] },
|
||||||
{ 'msg': 'inside empty non-content branch', 'expected': true },
|
{ 'msg': 'inside empty non-content branch', 'expected': [true, true] },
|
||||||
{ 'msg': 'between parent and child branches, ascending', 'expected': true },
|
{ 'msg': 'between parent, child branches, ascending', 'expected': [true, false] },
|
||||||
{ 'msg': 'between non-content branch and block leaf', 'expected': true },
|
{ 'msg': 'between non-content branch, block leaf', 'expected': [true, true] },
|
||||||
{ 'msg': 'inside block leaf', 'expected': false },
|
{ 'msg': 'inside block leaf', 'expected': [false, false] },
|
||||||
{ 'msg': 'right of document', 'expected': true }
|
{ 'msg': 'right of document', 'expected': [true, true] }
|
||||||
];
|
];
|
||||||
expect( data.length + 1 );
|
expect( ( data.length + 1 ) * 2 );
|
||||||
for ( var i = 0; i < cases.length; i++ ) {
|
for ( var i = 0; i < cases.length; i++ ) {
|
||||||
strictEqual( ve.dm.Document.isStructuralOffset( data, i ), cases[i].expected, cases[i].msg );
|
var left = data[i - 1] ? ( data[i - 1].type || data[i - 1][0] ) : '[start]',
|
||||||
|
right = data[i] ? ( data[i].type || data[i][0] ) : '[end]';
|
||||||
|
strictEqual(
|
||||||
|
ve.dm.Document.isStructuralOffset( data, i ),
|
||||||
|
cases[i].expected[0],
|
||||||
|
cases[i].msg + ' (' + left + '|' + right + ' @ ' + i + ')'
|
||||||
|
);
|
||||||
|
strictEqual(
|
||||||
|
ve.dm.Document.isStructuralOffset( data, i, true ),
|
||||||
|
cases[i].expected[1],
|
||||||
|
cases[i].msg + ', unrestricted (' + left + '|' + right + ' @ ' + i + ')'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
@ -654,14 +665,14 @@ test( 'isElementData', 1, function() {
|
||||||
{ 'msg': 'between content branches', 'expected': true },
|
{ 'msg': 'between content branches', 'expected': true },
|
||||||
{ 'msg': 'inside emtpy content branch', 'expected': true },
|
{ 'msg': 'inside emtpy content branch', 'expected': true },
|
||||||
{ 'msg': 'between content branches', 'expected': true },
|
{ 'msg': 'between content branches', 'expected': true },
|
||||||
{ 'msg': 'begining of content branch and left of inline leaf', 'expected': true },
|
{ 'msg': 'begining of content branch, left of inline leaf', 'expected': true },
|
||||||
{ 'msg': 'inside content branch with only non-text leaf', 'expected': true },
|
{ 'msg': 'inside content branch with non-text leaf', 'expected': true },
|
||||||
{ 'msg': 'end of content branch and right of inline leaf', 'expected': true },
|
{ 'msg': 'end of content branch, right of inline leaf', 'expected': true },
|
||||||
{ 'msg': 'between content and non-content branches', 'expected': true },
|
{ 'msg': 'between content, non-content branches', 'expected': true },
|
||||||
{ 'msg': 'between parent and child branches, descending', 'expected': true },
|
{ 'msg': 'between parent, child branches, descending', 'expected': true },
|
||||||
{ 'msg': 'inside empty non-content branch', 'expected': true },
|
{ 'msg': 'inside empty non-content branch', 'expected': true },
|
||||||
{ 'msg': 'between parent and child branches, ascending', 'expected': true },
|
{ 'msg': 'between parent, child branches, ascending', 'expected': true },
|
||||||
{ 'msg': 'between non-content branch and block leaf', 'expected': true },
|
{ 'msg': 'between non-content branch, block leaf', 'expected': true },
|
||||||
{ 'msg': 'inside block leaf', 'expected': true },
|
{ 'msg': 'inside block leaf', 'expected': true },
|
||||||
{ 'msg': 'right of document', 'expected': false }
|
{ 'msg': 'right of document', 'expected': false }
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in a new issue