2023-03-16 15:25:06 +00:00
|
|
|
mw.editcheck = {};
|
|
|
|
|
|
|
|
mw.editcheck.doesAddedContentNeedReference = function ( documentModel ) {
|
2023-03-28 08:44:56 +00:00
|
|
|
if ( mw.config.get( 'wgNamespaceNumber' ) !== mw.config.get( 'wgNamespaceIds' )[ '' ] ) {
|
|
|
|
return false;
|
|
|
|
}
|
2023-03-16 15:25:06 +00:00
|
|
|
var ranges = [];
|
|
|
|
var offset = 0;
|
|
|
|
var endOffset = documentModel.getDocumentRange().end;
|
2023-04-03 17:51:41 +00:00
|
|
|
try {
|
|
|
|
documentModel.completeHistory.squash().transactions[ 0 ].operations.every( function ( op ) {
|
|
|
|
if ( op.type === 'retain' ) {
|
|
|
|
offset += op.length;
|
|
|
|
} else if ( op.type === 'replace' ) {
|
2023-06-08 13:01:18 +00:00
|
|
|
var insertedRange = new ve.Range( offset, offset + op.insert.length );
|
2023-04-03 17:51:41 +00:00
|
|
|
offset += op.insert.length;
|
2023-06-08 13:01:18 +00:00
|
|
|
ve.batchPush(
|
|
|
|
ranges,
|
|
|
|
mw.editcheck.getContentRanges( documentModel, insertedRange )
|
|
|
|
);
|
2023-04-03 17:51:41 +00:00
|
|
|
}
|
|
|
|
// Reached the end of the doc / start of internal list, stop searching
|
|
|
|
return offset < endOffset;
|
|
|
|
} );
|
2023-04-22 08:08:54 +00:00
|
|
|
} catch ( err ) {
|
2023-04-03 17:51:41 +00:00
|
|
|
// TransactionSquasher can sometimes throw errors; until T333710 is
|
|
|
|
// fixed just count this as not needing a reference.
|
2023-04-22 08:08:54 +00:00
|
|
|
mw.errorLogger.logError( err, 'error.visualeditor' );
|
2023-04-03 17:51:41 +00:00
|
|
|
return false;
|
|
|
|
}
|
2023-03-16 15:25:06 +00:00
|
|
|
return ranges.some( function ( range ) {
|
|
|
|
var minimumCharacters = 50;
|
|
|
|
// 1. Check that at least minimumCharacters characters have been inserted sequentially
|
|
|
|
if ( range.getLength() >= minimumCharacters ) {
|
|
|
|
// 2. Exclude any ranges that already contain references
|
|
|
|
for ( var i = range.start; i < range.end; i++ ) {
|
|
|
|
if ( documentModel.data.isElementData( i ) && documentModel.data.getType( i ) === 'mwReference' ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2023-07-01 13:14:19 +00:00
|
|
|
// 3. Exclude any ranges that aren't at the document root (i.e. image captions, table cells)
|
|
|
|
var branchNode = documentModel.getBranchNodeFromOffset( range.start );
|
|
|
|
if ( branchNode.getParent() !== documentModel.attachedRoot ) {
|
|
|
|
return false;
|
|
|
|
}
|
2023-03-16 15:25:06 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
} );
|
|
|
|
};
|
2023-06-08 13:01:18 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the content ranges (content branch node interiors) contained within a range
|
|
|
|
*
|
|
|
|
* For a content branch node entirely contained within the range, its entire interior
|
|
|
|
* range will be included. For a content branch node overlapping with the range boundary,
|
|
|
|
* only the covered part of its interior range will be included.
|
|
|
|
*
|
|
|
|
* @param {ve.dm.Document} documentModel The documentModel to search
|
|
|
|
* @param {ve.Range} range The range to include
|
|
|
|
* @return {ve.Range[]} The contained content ranges (content branch node interiors)
|
|
|
|
*/
|
|
|
|
mw.editcheck.getContentRanges = function ( documentModel, range ) {
|
|
|
|
var ranges = [];
|
|
|
|
documentModel.selectNodes( range, 'branches' ).forEach( function ( spec ) {
|
|
|
|
if ( spec.node.canContainContent() ) {
|
|
|
|
ranges.push( spec.range || spec.nodeRange );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
return ranges;
|
|
|
|
};
|