jenkins-bot has submitted this change and it was merged.
Change subject: Move getRelativeOffset, getRelativeRange,
getNearestFocusableNode to model
......................................................................
Move getRelativeOffset, getRelativeRange, getNearestFocusableNode to model
Bonus: Remove ve.dm.Node.getOuterRange get override which was providing
less functionality than ve.Node.
Change-Id: I76f9476c25a64157165fe24fe889d7b9b1020451
---
M src/ce/ve.ce.Document.js
M src/ce/ve.ce.Surface.js
M src/dm/ve.dm.Document.js
M src/dm/ve.dm.Node.js
M tests/ce/ve.ce.Document.test.js
M tests/dm/ve.dm.Document.test.js
6 files changed, 440 insertions(+), 454 deletions(-)
Approvals:
Catrope: Looks good to me, approved
jenkins-bot: Verified
diff --git a/src/ce/ve.ce.Document.js b/src/ce/ve.ce.Document.js
index 3392ddc..de968cf 100644
--- a/src/ce/ve.ce.Document.js
+++ b/src/ce/ve.ce.Document.js
@@ -64,90 +64,6 @@
};
/**
- * Get the nearest word boundary.
- * This method is in CE instead of DM because its behaviour depends on the
browser (IE/non-IE) and
- * that information is closer to view layer. (CE)
- *
- * @method
- * @param {number} offset Offset to start from
- * @param {number} [direction] Direction to prefer matching offset in, -1 for
left and 1 for right
- * @returns {number} Nearest word boundary
- */
-ve.ce.Document.prototype.getSiblingWordBoundary = function ( offset, direction
) {
- var dataString = new ve.dm.DataString( this.model.getData() );
- return unicodeJS.wordbreak.moveBreakOffset( direction, dataString,
offset, true );
-};
-
-/**
- * Get the relative word or character boundary.
- *
- * This method is in CE instead of DM because it uses information about slugs
about which model
- * does not know at all.
- *
- * @method
- * @param {number} offset Offset to start from
- * @param {number} direction Direction to prefer matching offset in, -1 for
left and 1 for right
- * @param {string} [unit] Unit [word|character]
- * @returns {number} Relative offset
- */
-ve.ce.Document.prototype.getRelativeOffset = function ( offset, direction,
unit ) {
- var relativeContentOffset, relativeStructuralOffset, newOffset,
adjacentDataOffset, isFocusable,
- data = this.model.data;
- if ( unit === 'word' ) { // word
- // Method getSiblingWordBoundary does not "move/jump" over
element data. If passed offset is
- // an element data offset then the same offset is returned -
and in such case this method
- // fallback to the other path (character) which does
"move/jump" over element data.
- newOffset = this.getSiblingWordBoundary( offset, direction );
- if ( offset === newOffset ) {
- newOffset = this.getRelativeOffset( offset, direction,
'character' );
- }
- return newOffset;
- } else { // character
- // Check if we are adjacent to a focusable node
- adjacentDataOffset = offset + ( direction > 0 ? 0 : -1 );
- if (
- data.isElementData( adjacentDataOffset ) &&
- ve.dm.nodeFactory.isNodeFocusable( data.getType(
adjacentDataOffset ) )
- ) {
- // We are adjacent to a focusableNode, move inside it
- return offset + direction;
- }
- relativeContentOffset = data.getRelativeContentOffset( offset,
direction );
- relativeStructuralOffset = data.getRelativeStructuralOffset(
offset, direction, true );
- // Check the structural offset is not in the wrong direction
- if ( ( relativeStructuralOffset - offset < 0 ? -1 : 1 ) !==
direction ) {
- relativeStructuralOffset = offset;
- } else {
- isFocusable = ( relativeStructuralOffset - offset < 0 ?
-1 : 1 ) === direction &&
- data.isElementData( relativeStructuralOffset +
direction ) &&
- ve.dm.nodeFactory.isNodeFocusable(
data.getType( relativeStructuralOffset + direction ) );
- }
- // Check if we've moved into a slug or a focusableNode
- if ( isFocusable || this.getSlugAtOffset(
relativeStructuralOffset ) ) {
- if ( isFocusable ) {
- relativeStructuralOffset += direction;
- }
- // Check if the relative content offset is in the
opposite direction we are trying to go
- if (
- relativeContentOffset === offset ||
- ( relativeContentOffset - offset < 0 ? -1 : 1 )
!== direction
- ) {
- return relativeStructuralOffset;
- }
- // There's a slug neaby, go into it if it's closer
- return direction > 0 ?
- Math.min( relativeContentOffset,
relativeStructuralOffset ) :
- Math.max( relativeContentOffset,
relativeStructuralOffset );
- } else {
- // Don't allow the offset to move in the wrong direction
- return direction > 0 ?
- Math.max( relativeContentOffset, offset ) :
- Math.min( relativeContentOffset, offset );
- }
- }
-};
-
-/**
* Get a DOM node and DOM element offset for a document offset.
*
* @method
@@ -219,93 +135,6 @@
current[1]++;
}
throw new Error( 'Offset could not be translated to a DOM element and
offset: ' + offset );
-};
-
-/**
- * Get the nearest focusable node.
- *
- * @method
- * @param {number} offset Offset to start looking at
- * @param {number} direction Direction to look in, +1 or -1
- * @param {number} limit Stop looking after reaching certain offset
- */
-ve.ce.Document.prototype.getNearestFocusableNode = function ( offset,
direction, limit ) {
- // It is never an offset of the node, but just an offset for which
getNodeFromOffset should
- // return that node. Usually it would be node offset + 1 or offset of
node closing tag.
- var coveredOffset;
- this.model.data.getRelativeOffset(
- offset,
- direction === 1 ? 0 : -1,
- function ( index, limit ) {
- // Our result must be between offset and limit
- if ( index >= Math.max( offset, limit ) || index <
Math.min( offset, limit ) ) {
- return true;
- }
- if (
- this.isOpenElementData( index ) &&
- ve.dm.nodeFactory.isNodeFocusable(
this.getType( index ) )
- ) {
- coveredOffset = index + 1;
- return true;
- }
- if (
- this.isCloseElementData( index ) &&
- ve.dm.nodeFactory.isNodeFocusable(
this.getType( index ) )
- ) {
- coveredOffset = index;
- return true;
- }
- },
- limit
- );
- if ( coveredOffset ) {
- return this.getDocumentNode().getNodeFromOffset( coveredOffset
);
- } else {
- return null;
- }
-};
-
-/**
- * Get the relative range.
- *
- * @method
- * @param {ve.Range} range Input range
- * @param {number} direction Direction to look in, +1 or -1
- * @param {string} unit Unit [word|character]
- * @param {boolean} expand Expanding range
- * @returns {ve.Range} Relative range
- */
-ve.ce.Document.prototype.getRelativeRange = function ( range, direction, unit,
expand ) {
- var contentOrSlugOffset,
- focusableNode,
- newOffset,
- newRange,
- to = range.to;
-
- // If you have a non-collapsed range and you move, collapse to the end
- // in the direction you moved, provided you end up at a content or slug
offset
- if ( !range.isCollapsed() && !expand ) {
- newOffset = direction > 0 ? range.end : range.start;
- if ( this.model.data.isContentOffset( newOffset ) ||
this.getSlugAtOffset( newOffset ) ) {
- return new ve.Range( newOffset );
- } else {
- to = newOffset;
- }
- }
-
- contentOrSlugOffset = this.getRelativeOffset( to, direction, unit );
-
- focusableNode = this.getNearestFocusableNode( to, direction,
contentOrSlugOffset );
- if ( focusableNode ) {
- newRange = focusableNode.getOuterRange( direction === -1 );
- } else {
- newRange = new ve.Range( contentOrSlugOffset );
- }
- if ( expand ) {
- return new ve.Range( range.from, newRange.to );
- } else {
- return newRange;
- }
};
/**
diff --git a/src/ce/ve.ce.Surface.js b/src/ce/ve.ce.Surface.js
index acc651d..ae6e3b1 100644
--- a/src/ce/ve.ce.Surface.js
+++ b/src/ce/ve.ce.Surface.js
@@ -1876,7 +1876,7 @@
direction = e.keyCode === OO.ui.Keys.LEFT ? -1 : 1;
}
- range = this.getDocument().getRelativeRange(
+ range = this.model.getDocument().getRelativeRange(
selection,
direction,
( e.altKey === true || e.ctrlKey === true ) ? 'word' :
'character',
@@ -2250,7 +2250,7 @@
if ( direction && rangeToRemove.isCollapsed() ) {
// In case when the range is collapsed use the same logic that
is used for cursor left and
// right movement in order to figure out range to remove.
- rangeToRemove = documentView.getRelativeRange(
+ rangeToRemove = documentModel.getRelativeRange(
rangeToRemove,
direction,
( e.altKey === true || e.ctrlKey === true ) ? 'word' :
'character',
@@ -2340,7 +2340,7 @@
}
// rangeAfterRemove is now guaranteed to be collapsed so make sure that
it is a content offset
if ( !documentModel.data.isContentOffset( rangeAfterRemove.start ) ) {
- rangeAfterRemove = documentView.getRelativeRange(
+ rangeAfterRemove = documentModel.getRelativeRange(
rangeAfterRemove,
// If direction === 0 (cut), default to backwards
movement
direction || -1
diff --git a/src/dm/ve.dm.Document.js b/src/dm/ve.dm.Document.js
index 4ac7d5f..0217196 100644
--- a/src/dm/ve.dm.Document.js
+++ b/src/dm/ve.dm.Document.js
@@ -590,6 +590,172 @@
};
/**
+ * Get the nearest word boundary.
+ *
+ * @method
+ * @param {number} offset Offset to start from
+ * @param {number} [direction] Direction to prefer matching offset in, -1 for
left and 1 for right
+ * @returns {number} Nearest word boundary
+ */
+ve.dm.Document.prototype.getSiblingWordBoundary = function ( offset, direction
) {
+ var dataString = new ve.dm.DataString( this.getData() );
+ return unicodeJS.wordbreak.moveBreakOffset( direction, dataString,
offset, true );
+};
+
+/**
+ * Get the relative word or character boundary.
+ *
+ * @method
+ * @param {number} offset Offset to start from
+ * @param {number} direction Direction to prefer matching offset in, -1 for
left and 1 for right
+ * @param {string} [unit] Unit [word|character]
+ * @returns {number} Relative offset
+ */
+ve.dm.Document.prototype.getRelativeOffset = function ( offset, direction,
unit ) {
+ var relativeContentOffset, relativeStructuralOffset, newOffset,
adjacentDataOffset, isFocusable,
+ data = this.data;
+ if ( unit === 'word' ) { // word
+ // Method getSiblingWordBoundary does not "move/jump" over
element data. If passed offset is
+ // an element data offset then the same offset is returned -
and in such case this method
+ // fallback to the other path (character) which does
"move/jump" over element data.
+ newOffset = this.getSiblingWordBoundary( offset, direction );
+ if ( offset === newOffset ) {
+ newOffset = this.getRelativeOffset( offset, direction,
'character' );
+ }
+ return newOffset;
+ } else { // character
+ // Check if we are adjacent to a focusable node
+ adjacentDataOffset = offset + ( direction > 0 ? 0 : -1 );
+ if (
+ data.isElementData( adjacentDataOffset ) &&
+ ve.dm.nodeFactory.isNodeFocusable( data.getType(
adjacentDataOffset ) )
+ ) {
+ // We are adjacent to a focusableNode, move inside it
+ return offset + direction;
+ }
+ relativeContentOffset = data.getRelativeContentOffset( offset,
direction );
+ relativeStructuralOffset = data.getRelativeStructuralOffset(
offset, direction, true );
+ // Check the structural offset is not in the wrong direction
+ if ( ( relativeStructuralOffset - offset < 0 ? -1 : 1 ) !==
direction ) {
+ relativeStructuralOffset = offset;
+ } else {
+ isFocusable = ( relativeStructuralOffset - offset < 0 ?
-1 : 1 ) === direction &&
+ data.isElementData( relativeStructuralOffset +
direction ) &&
+ ve.dm.nodeFactory.isNodeFocusable(
data.getType( relativeStructuralOffset + direction ) );
+ }
+ // Check if we've moved into a slug or a focusableNode
+ if ( isFocusable || this.hasSlugAtOffset(
relativeStructuralOffset ) ) {
+ if ( isFocusable ) {
+ relativeStructuralOffset += direction;
+ }
+ // Check if the relative content offset is in the
opposite direction we are trying to go
+ if (
+ relativeContentOffset === offset ||
+ ( relativeContentOffset - offset < 0 ? -1 : 1 )
!== direction
+ ) {
+ return relativeStructuralOffset;
+ }
+ // There's a slug neaby, go into it if it's closer
+ return direction > 0 ?
+ Math.min( relativeContentOffset,
relativeStructuralOffset ) :
+ Math.max( relativeContentOffset,
relativeStructuralOffset );
+ } else {
+ // Don't allow the offset to move in the wrong direction
+ return direction > 0 ?
+ Math.max( relativeContentOffset, offset ) :
+ Math.min( relativeContentOffset, offset );
+ }
+ }
+};
+
+/**
+ * Get the relative range.
+ *
+ * @method
+ * @param {ve.Range} range Input range
+ * @param {number} direction Direction to look in, +1 or -1
+ * @param {string} unit Unit [word|character]
+ * @param {boolean} expand Expanding range
+ * @returns {ve.Range} Relative range
+ */
+ve.dm.Document.prototype.getRelativeRange = function ( range, direction, unit,
expand ) {
+ var contentOrSlugOffset,
+ focusableNode,
+ newOffset,
+ newRange,
+ to = range.to;
+
+ // If you have a non-collapsed range and you move, collapse to the end
+ // in the direction you moved, provided you end up at a content or slug
offset
+ if ( !range.isCollapsed() && !expand ) {
+ newOffset = direction > 0 ? range.end : range.start;
+ if ( this.data.isContentOffset( newOffset ) ||
this.hasSlugAtOffset( newOffset ) ) {
+ return new ve.Range( newOffset );
+ } else {
+ to = newOffset;
+ }
+ }
+
+ contentOrSlugOffset = this.getRelativeOffset( to, direction, unit );
+
+ focusableNode = this.getNearestFocusableNode( to, direction,
contentOrSlugOffset );
+ if ( focusableNode ) {
+ newRange = focusableNode.getOuterRange( direction === -1 );
+ } else {
+ newRange = new ve.Range( contentOrSlugOffset );
+ }
+ if ( expand ) {
+ return new ve.Range( range.from, newRange.to );
+ } else {
+ return newRange;
+ }
+};
+
+/**
+ * Get the nearest focusable node.
+ *
+ * @method
+ * @param {number} offset Offset to start looking at
+ * @param {number} direction Direction to look in, +1 or -1
+ * @param {number} limit Stop looking after reaching certain offset
+ */
+ve.dm.Document.prototype.getNearestFocusableNode = function ( offset,
direction, limit ) {
+ // It is never an offset of the node, but just an offset for which
getNodeFromOffset should
+ // return that node. Usually it would be node offset + 1 or offset of
node closing tag.
+ var coveredOffset;
+ this.data.getRelativeOffset(
+ offset,
+ direction === 1 ? 0 : -1,
+ function ( index, limit ) {
+ // Our result must be between offset and limit
+ if ( index >= Math.max( offset, limit ) || index <
Math.min( offset, limit ) ) {
+ return true;
+ }
+ if (
+ this.isOpenElementData( index ) &&
+ ve.dm.nodeFactory.isNodeFocusable(
this.getType( index ) )
+ ) {
+ coveredOffset = index + 1;
+ return true;
+ }
+ if (
+ this.isCloseElementData( index ) &&
+ ve.dm.nodeFactory.isNodeFocusable(
this.getType( index ) )
+ ) {
+ coveredOffset = index;
+ return true;
+ }
+ },
+ limit
+ );
+ if ( coveredOffset ) {
+ return this.getDocumentNode().getNodeFromOffset( coveredOffset
);
+ } else {
+ return null;
+ }
+};
+
+/**
* Get a node from an offset.
*
* @method
diff --git a/src/dm/ve.dm.Node.js b/src/dm/ve.dm.Node.js
index 24b6604..90b52fb 100644
--- a/src/dm/ve.dm.Node.js
+++ b/src/dm/ve.dm.Node.js
@@ -494,17 +494,6 @@
};
/**
- * Get the range outside the node.
- *
- * @method
- * @returns {ve.Range} Outer node range
- */
-ve.dm.Node.prototype.getOuterRange = function () {
- var offset = this.getOffset();
- return new ve.Range( offset, offset + this.getOuterLength() );
-};
-
-/**
* Set the inner length of the node.
*
* This should only be called after a relevant change to the document data.
Calling this method will
diff --git a/tests/ce/ve.ce.Document.test.js b/tests/ce/ve.ce.Document.test.js
index a0df564..ef0f43a 100644
--- a/tests/ce/ve.ce.Document.test.js
+++ b/tests/ce/ve.ce.Document.test.js
@@ -9,272 +9,7 @@
/* Tests */
-QUnit.test( 'getRelativeOffset', function ( assert ) {
- var documentModel = ve.dm.example.createExampleDocument( 'alienData' ),
- surface = ve.test.utils.createSurfaceFromDocument(
documentModel ),
- documentView = surface.getView().getDocument(),
- tests = [
- {
- direction: 1,
- unit: 'character',
- cases: [
- { input: 0, output: 1 },
- { input: 2, output: 3 },
- { input: 3, output: 4 },
- { input: 4, output: 5 },
- { input: 6, output: 7 },
- { input: 7, output: 9 },
- { input: 10, output: 10 }
- ]
- },
- {
- direction: 1,
- unit: 'word',
- cases: [
- { input: 0, output: 1 },
- { input: 2, output: 3 },
- { input: 3, output: 4 },
- { input: 4, output: 5 },
- { input: 6, output: 7 },
- { input: 7, output: 9 },
- { input: 10, output: 10 }
- ]
- },
- {
- direction: -1,
- unit: 'character',
- cases: [
- { input: 10, output: 9 },
- { input: 8, output: 7 },
- { input: 7, output: 6 },
- { input: 6, output: 5 },
- { input: 4, output: 3 },
- { input: 3, output: 1 },
- { input: 0, output: 0 }
- ]
- },
- {
- direction: -1,
- unit: 'word',
- cases: [
- { input: 10, output: 9 },
- { input: 8, output: 7 },
- { input: 7, output: 6 },
- { input: 6, output: 5 },
- { input: 4, output: 3 },
- { input: 3, output: 1 },
- { input: 0, output: 0 }
- ]
- }
- ], i, j, expectCount = 0;
- for ( i = 0; i < tests.length; i++ ) {
- for ( j = 0; j < tests[i].cases.length; j++ ) {
- assert.strictEqual(
- documentView.getRelativeOffset(
- tests[i].cases[j].input,
- tests[i].direction,
- tests[i].unit
- ),
- tests[i].cases[j].output,
- tests[i].cases[j].input + ', ' +
tests[i].direction + ', ' + tests[i].unit
- );
- }
- expectCount += tests[i].cases.length;
- }
- QUnit.expect( expectCount );
- surface.destroy();
-} );
-
-QUnit.test( 'getRelativeRange', function ( assert ) {
- var documentModel, surface, documentView, i, j, expectCount = 0,
- tests = [
- {
- data: [
- /* 0 */ { type: 'paragraph' },
- /* 1 */ 'a',
- /* 2 */ { type: 'alienInline' },
- /* 3 */ { type: '/alienInline' },
- /* 4 */ 'b',
- /* 5 */ { type: '/paragraph' }
- ],
- cases: [
- {
- direction: 1,
- given: new ve.Range( 1 ),
- expected: new ve.Range( 2 )
- },
- {
- direction: 1,
- given: new ve.Range( 2 ),
- expected: new ve.Range( 2, 4 )
- },
- {
- direction: 1,
- given: new ve.Range( 2, 4 ),
- expected: new ve.Range( 4 )
- },
-
- {
- direction: 1,
- expand: true,
- given: new ve.Range( 1 ),
- expected: new ve.Range( 1, 2 )
- },
- {
- direction: 1,
- expand: true,
- given: new ve.Range( 1, 2 ),
- expected: new ve.Range( 1, 4 )
- },
- {
- direction: 1,
- expand: true,
- given: new ve.Range( 1, 4 ),
- expected: new ve.Range( 1, 5 )
- }
- ]
- },
- {
- data: [
- /* 0 */ { type: 'paragraph' },
- /* 1 */ { type: 'alienInline' },
- /* 2 */ { type: '/alienInline' },
- /* 3 */ { type: 'alienInline' },
- /* 4 */ { type: '/alienInline' },
- /* 5 */ { type: '/paragraph' }
- ],
- cases: [
- {
- direction: 1,
- given: new ve.Range( 3 ),
- expected: new ve.Range( 3, 5 )
- },
- {
- direction: 1,
- expand: true,
- given: new ve.Range( 1, 3 ),
- expected: new ve.Range( 1, 5 )
- },
- {
- direction: -1,
- expand: true,
- given: new ve.Range( 1, 5 ),
- expected: new ve.Range( 1, 3 )
- },
- {
- direction: 1,
- expand: true,
- given: new ve.Range( 5, 1 ),
- expected: new ve.Range( 5, 3 )
- }
- ]
- },
- {
- data: ve.copy( ve.dm.example.alienData ),
- cases: [
- {
- direction: 1,
- given: new ve.Range( 0 ),
- expected: new ve.Range( 0, 2 )
- },
- {
- direction: 1,
- given: new ve.Range( 0, 2 ),
- expected: new ve.Range( 3 )
- },
- {
- direction: 1,
- given: new ve.Range( 3 ),
- expected: new ve.Range( 4 )
- },
- {
- direction: 1,
- given: new ve.Range( 4 ),
- expected: new ve.Range( 4, 6 )
- },
- {
- direction: 1,
- given: new ve.Range( 4, 6),
- expected: new ve.Range( 6 )
- },
- {
- direction: 1,
- given: new ve.Range( 6 ),
- expected: new ve.Range( 7 )
- },
- {
- direction: 1,
- given: new ve.Range( 7 ),
- expected: new ve.Range( 8, 10 )
- },
- {
- direction: 1,
- given: new ve.Range( 10 ),
- expected: new ve.Range( 10 )
- },
- {
- direction: -1,
- given: new ve.Range( 10 ),
- expected: new ve.Range( 10, 8 )
- },
- {
- direction: -1,
- given: new ve.Range( 10, 8 ),
- expected: new ve.Range( 7 )
- },
- {
- direction: -1,
- given: new ve.Range( 7 ),
- expected: new ve.Range( 6 )
- },
- {
- direction: -1,
- given: new ve.Range( 6 ),
- expected: new ve.Range( 6, 4 )
- },
- {
- direction: -1,
- given: new ve.Range( 6, 4 ),
- expected: new ve.Range( 4 )
- },
- {
- direction: -1,
- given: new ve.Range( 4 ),
- expected: new ve.Range( 3 )
- },
- {
- direction: -1,
- given: new ve.Range( 3 ),
- expected: new ve.Range( 2, 0 )
- },
- {
- direction: -1,
- given: new ve.Range( 2, 0 ),
- expected: new ve.Range( 0 )
- }
- ]
- }
- ];
- for ( i = 0; i < tests.length; i++ ) {
- documentModel = new ve.dm.Document( tests[i].data );
- surface = ve.test.utils.createSurfaceFromDocument(
documentModel );
- documentView = surface.getView().getDocument();
- for ( j = 0; j < tests[i].cases.length; j++ ) {
- expectCount++;
- assert.equalRange(
- documentView.getRelativeRange(
- tests[i].cases[j].given,
- tests[i].cases[j].direction,
- 'character',
- !!tests[i].cases[j].expand
- ),
- tests[i].cases[j].expected,
- 'Test document ' + i +
- ', range ' + tests[i].cases[j].given.toJSON() +
- ', direction ' + tests[i].cases[j].direction
- );
- }
- surface.destroy();
- }
- QUnit.expect( expectCount );
-} );
+// TODO: getNodeFromOffset
+// TODO: getSlugAtOffset
+// TODO: getNodeAndOffset
+// TODO: getDirectionFromRange
diff --git a/tests/dm/ve.dm.Document.test.js b/tests/dm/ve.dm.Document.test.js
index 6506aef..c89bf77 100644
--- a/tests/dm/ve.dm.Document.test.js
+++ b/tests/dm/ve.dm.Document.test.js
@@ -123,6 +123,273 @@
}
} );
+QUnit.test( 'getRelativeOffset', function ( assert ) {
+ var i, j,
+ expectCount = 0,
+ documentModel = ve.dm.example.createExampleDocument(
'alienData' ),
+ tests = [
+ {
+ direction: 1,
+ unit: 'character',
+ cases: [
+ { input: 0, output: 1 },
+ { input: 2, output: 3 },
+ { input: 3, output: 4 },
+ { input: 4, output: 5 },
+ { input: 6, output: 7 },
+ { input: 7, output: 9 },
+ { input: 10, output: 10 }
+ ]
+ },
+ {
+ direction: 1,
+ unit: 'word',
+ cases: [
+ { input: 0, output: 1 },
+ { input: 2, output: 3 },
+ { input: 3, output: 4 },
+ { input: 4, output: 5 },
+ { input: 6, output: 7 },
+ { input: 7, output: 9 },
+ { input: 10, output: 10 }
+ ]
+ },
+ {
+ direction: -1,
+ unit: 'character',
+ cases: [
+ { input: 10, output: 9 },
+ { input: 8, output: 7 },
+ { input: 7, output: 6 },
+ { input: 6, output: 5 },
+ { input: 4, output: 3 },
+ { input: 3, output: 1 },
+ { input: 0, output: 0 }
+ ]
+ },
+ {
+ direction: -1,
+ unit: 'word',
+ cases: [
+ { input: 10, output: 9 },
+ { input: 8, output: 7 },
+ { input: 7, output: 6 },
+ { input: 6, output: 5 },
+ { input: 4, output: 3 },
+ { input: 3, output: 1 },
+ { input: 0, output: 0 }
+ ]
+ }
+ ];
+ for ( i = 0; i < tests.length; i++ ) {
+ for ( j = 0; j < tests[i].cases.length; j++ ) {
+ assert.strictEqual(
+ documentModel.getRelativeOffset(
+ tests[i].cases[j].input,
+ tests[i].direction,
+ tests[i].unit
+ ),
+ tests[i].cases[j].output,
+ tests[i].cases[j].input + ', ' +
tests[i].direction + ', ' + tests[i].unit
+ );
+ }
+ expectCount += tests[i].cases.length;
+ }
+ QUnit.expect( expectCount );
+} );
+
+QUnit.test( 'getRelativeRange', function ( assert ) {
+ var documentModel, i, j,
+ expectCount = 0,
+ tests = [
+ {
+ data: [
+ /* 0 */ { type: 'paragraph' },
+ /* 1 */ 'a',
+ /* 2 */ { type: 'alienInline' },
+ /* 3 */ { type: '/alienInline' },
+ /* 4 */ 'b',
+ /* 5 */ { type: '/paragraph' }
+ ],
+ cases: [
+ {
+ direction: 1,
+ given: new ve.Range( 1 ),
+ expected: new ve.Range( 2 )
+ },
+ {
+ direction: 1,
+ given: new ve.Range( 2 ),
+ expected: new ve.Range( 2, 4 )
+ },
+ {
+ direction: 1,
+ given: new ve.Range( 2, 4 ),
+ expected: new ve.Range( 4 )
+ },
+
+ {
+ direction: 1,
+ expand: true,
+ given: new ve.Range( 1 ),
+ expected: new ve.Range( 1, 2 )
+ },
+ {
+ direction: 1,
+ expand: true,
+ given: new ve.Range( 1, 2 ),
+ expected: new ve.Range( 1, 4 )
+ },
+ {
+ direction: 1,
+ expand: true,
+ given: new ve.Range( 1, 4 ),
+ expected: new ve.Range( 1, 5 )
+ }
+ ]
+ },
+ {
+ data: [
+ /* 0 */ { type: 'paragraph' },
+ /* 1 */ { type: 'alienInline' },
+ /* 2 */ { type: '/alienInline' },
+ /* 3 */ { type: 'alienInline' },
+ /* 4 */ { type: '/alienInline' },
+ /* 5 */ { type: '/paragraph' }
+ ],
+ cases: [
+ {
+ direction: 1,
+ given: new ve.Range( 3 ),
+ expected: new ve.Range( 3, 5 )
+ },
+ {
+ direction: 1,
+ expand: true,
+ given: new ve.Range( 1, 3 ),
+ expected: new ve.Range( 1, 5 )
+ },
+ {
+ direction: -1,
+ expand: true,
+ given: new ve.Range( 1, 5 ),
+ expected: new ve.Range( 1, 3 )
+ },
+ {
+ direction: 1,
+ expand: true,
+ given: new ve.Range( 5, 1 ),
+ expected: new ve.Range( 5, 3 )
+ }
+ ]
+ },
+ {
+ data: ve.copy( ve.dm.example.alienData ),
+ cases: [
+ {
+ direction: 1,
+ given: new ve.Range( 0 ),
+ expected: new ve.Range( 0, 2 )
+ },
+ {
+ direction: 1,
+ given: new ve.Range( 0, 2 ),
+ expected: new ve.Range( 3 )
+ },
+ {
+ direction: 1,
+ given: new ve.Range( 3 ),
+ expected: new ve.Range( 4 )
+ },
+ {
+ direction: 1,
+ given: new ve.Range( 4 ),
+ expected: new ve.Range( 4, 6 )
+ },
+ {
+ direction: 1,
+ given: new ve.Range( 4, 6),
+ expected: new ve.Range( 6 )
+ },
+ {
+ direction: 1,
+ given: new ve.Range( 6 ),
+ expected: new ve.Range( 7 )
+ },
+ {
+ direction: 1,
+ given: new ve.Range( 7 ),
+ expected: new ve.Range( 8, 10 )
+ },
+ {
+ direction: 1,
+ given: new ve.Range( 10 ),
+ expected: new ve.Range( 10 )
+ },
+ {
+ direction: -1,
+ given: new ve.Range( 10 ),
+ expected: new ve.Range( 10, 8 )
+ },
+ {
+ direction: -1,
+ given: new ve.Range( 10, 8 ),
+ expected: new ve.Range( 7 )
+ },
+ {
+ direction: -1,
+ given: new ve.Range( 7 ),
+ expected: new ve.Range( 6 )
+ },
+ {
+ direction: -1,
+ given: new ve.Range( 6 ),
+ expected: new ve.Range( 6, 4 )
+ },
+ {
+ direction: -1,
+ given: new ve.Range( 6, 4 ),
+ expected: new ve.Range( 4 )
+ },
+ {
+ direction: -1,
+ given: new ve.Range( 4 ),
+ expected: new ve.Range( 3 )
+ },
+ {
+ direction: -1,
+ given: new ve.Range( 3 ),
+ expected: new ve.Range( 2, 0 )
+ },
+ {
+ direction: -1,
+ given: new ve.Range( 2, 0 ),
+ expected: new ve.Range( 0 )
+ }
+ ]
+ }
+ ];
+ for ( i = 0; i < tests.length; i++ ) {
+ documentModel = new ve.dm.Document( tests[i].data );
+ for ( j = 0; j < tests[i].cases.length; j++ ) {
+ expectCount++;
+ assert.equalRange(
+ documentModel.getRelativeRange(
+ tests[i].cases[j].given,
+ tests[i].cases[j].direction,
+ 'character',
+ !!tests[i].cases[j].expand
+ ),
+ tests[i].cases[j].expected,
+ 'Test document ' + i +
+ ', range ' + tests[i].cases[j].given.toJSON() +
+ ', direction ' + tests[i].cases[j].direction
+ );
+ }
+ }
+ QUnit.expect( expectCount );
+} );
+
QUnit.test( 'getNodeFromOffset', function ( assert ) {
var i, j, node,
doc = ve.dm.example.createExampleDocument(),
--
To view, visit https://gerrit.wikimedia.org/r/160840
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I76f9476c25a64157165fe24fe889d7b9b1020451
Gerrit-PatchSet: 5
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits