2020-11-18 09:11:12 +00:00
|
|
|
/**
|
|
|
|
* @class RevisionListView
|
|
|
|
* @param {RevisionList} revisionList
|
|
|
|
* @constructor
|
|
|
|
*/
|
2024-03-12 16:38:57 +00:00
|
|
|
function RevisionListView( revisionList ) {
|
2023-11-14 16:40:52 +00:00
|
|
|
this.revisionList = revisionList;
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
|
|
|
|
2024-06-20 16:23:01 +00:00
|
|
|
Object.assign( RevisionListView.prototype, {
|
2016-06-17 13:06:12 +00:00
|
|
|
/**
|
2023-11-14 16:40:52 +00:00
|
|
|
* @type {RevisionList}
|
2016-06-17 13:06:12 +00:00
|
|
|
*/
|
2023-11-14 16:40:52 +00:00
|
|
|
revisionList: null,
|
2016-05-10 12:42:05 +00:00
|
|
|
|
2020-01-24 16:21:23 +00:00
|
|
|
/**
|
2020-11-18 09:11:12 +00:00
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
revisionWidth: 16,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
minRevisionHeight: 5,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
maxRevisionHeight: 66,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
tooltipTimeout: -1,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
2024-02-20 07:43:15 +00:00
|
|
|
allowRevisionPreviewHighlights: true,
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
selectedUser: '',
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
selectedTag: '',
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {jQuery}
|
|
|
|
*/
|
|
|
|
html: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {number} revisionTickWidth
|
2024-02-09 11:13:44 +00:00
|
|
|
* @param {number} [positionOffset=0]
|
2020-11-18 09:11:12 +00:00
|
|
|
* @return {jQuery}
|
|
|
|
*/
|
|
|
|
render: function ( revisionTickWidth, positionOffset ) {
|
2023-11-14 16:40:52 +00:00
|
|
|
const revs = this.revisionList.getRevisions();
|
|
|
|
const maxChangeSize = this.revisionList.getBiggestChangeSize();
|
|
|
|
const self = this;
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
positionOffset = positionOffset || 0;
|
|
|
|
this.revisionWidth = revisionTickWidth;
|
|
|
|
|
|
|
|
this.$html = $( '<div>' ).addClass( 'mw-revslider-revisions' );
|
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
for ( let i = 0; i < revs.length; i++ ) {
|
|
|
|
const diffSize = revs[ i ].getRelativeSize();
|
2023-11-14 16:40:52 +00:00
|
|
|
const relativeChangeSize = this.calcRelativeChangeSize( diffSize, maxChangeSize );
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
this.$html
|
|
|
|
.append( $( '<div>' )
|
|
|
|
.addClass( 'mw-revslider-revision-wrapper' )
|
|
|
|
.width( this.revisionWidth )
|
|
|
|
.append( $( '<div>' )
|
|
|
|
.addClass( 'mw-revslider-revision' )
|
|
|
|
.attr( 'data-revid', revs[ i ].getId() )
|
|
|
|
.attr( 'data-pos', positionOffset + i + 1 )
|
|
|
|
.attr( 'data-user', revs[ i ].getUser() )
|
|
|
|
.css( {
|
|
|
|
height: relativeChangeSize + 'px',
|
|
|
|
width: this.revisionWidth + 'px',
|
|
|
|
top: diffSize > 0 ? '-' + relativeChangeSize + 'px' : 0
|
|
|
|
} )
|
|
|
|
.addClass( diffSize > 0 ? 'mw-revslider-revision-up' : 'mw-revslider-revision-down' )
|
|
|
|
.append( $( '<div>' ).addClass( 'mw-revslider-revision-border-box' ) )
|
|
|
|
)
|
2016-05-30 10:06:44 +00:00
|
|
|
.append( $( '<div>' )
|
2020-11-18 09:11:12 +00:00
|
|
|
.addClass( 'mw-revslider-revision-wrapper-up' )
|
2016-08-03 12:08:05 +00:00
|
|
|
.width( this.revisionWidth )
|
2020-11-18 09:11:12 +00:00
|
|
|
.append(
|
|
|
|
$( '<div>' )
|
|
|
|
.addClass( 'mw-revslider-pointer mw-revslider-pointer-ghost' )
|
2016-05-18 14:55:39 +00:00
|
|
|
)
|
2020-11-18 09:11:12 +00:00
|
|
|
)
|
|
|
|
.append( $( '<div>' )
|
|
|
|
.addClass( 'mw-revslider-revision-wrapper-down' )
|
|
|
|
.width( this.revisionWidth )
|
|
|
|
.append(
|
|
|
|
$( '<div>' )
|
|
|
|
.addClass( 'mw-revslider-pointer mw-revslider-pointer-ghost' )
|
2017-08-17 14:08:49 +00:00
|
|
|
)
|
2020-11-18 09:11:12 +00:00
|
|
|
)
|
|
|
|
.on( 'mouseenter', function ( event ) {
|
2024-02-20 08:11:21 +00:00
|
|
|
self.onRevisionHover( $( this ), event );
|
2020-11-18 09:11:12 +00:00
|
|
|
} )
|
2024-06-08 04:13:35 +00:00
|
|
|
.on( 'mouseleave', () => {
|
2024-02-20 07:43:15 +00:00
|
|
|
self.removeAllRevisionPreviewHighlights();
|
2023-08-31 10:08:43 +00:00
|
|
|
self.removeCurrentRevisionFocusWithDelay();
|
2020-11-18 09:11:12 +00:00
|
|
|
} )
|
|
|
|
);
|
|
|
|
}
|
2016-05-10 12:42:05 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
this.closeTooltipsOnClick();
|
2016-06-24 13:13:20 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
return this.$html;
|
|
|
|
},
|
2016-05-10 12:42:05 +00:00
|
|
|
|
2024-02-20 07:43:15 +00:00
|
|
|
/**
|
|
|
|
* @param {boolean} [enabled=true]
|
|
|
|
*/
|
|
|
|
enableRevisionPreviewHighlights: function ( enabled ) {
|
|
|
|
this.allowRevisionPreviewHighlights = enabled !== false;
|
|
|
|
if ( !this.allowRevisionPreviewHighlights ) {
|
|
|
|
this.removeAllRevisionPreviewHighlights();
|
|
|
|
}
|
2020-11-18 09:11:12 +00:00
|
|
|
},
|
2017-08-17 14:08:49 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
/**
|
|
|
|
* @param {jQuery} $revisionWrapper
|
|
|
|
* @param {MouseEvent} event
|
|
|
|
*/
|
2024-02-20 07:43:15 +00:00
|
|
|
onRevisionHover: function ( $revisionWrapper, event ) {
|
2024-02-20 08:11:21 +00:00
|
|
|
if ( !this.allowRevisionPreviewHighlights ||
|
|
|
|
$( event.target ).closest( '.mw-revslider-revision-tooltip' ).length
|
|
|
|
) {
|
2021-06-21 13:33:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-31 10:08:43 +00:00
|
|
|
this.setRevisionFocus( $revisionWrapper );
|
2023-08-30 12:35:29 +00:00
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
const hasMovedTop = event.pageY - $revisionWrapper.offset().top < $revisionWrapper.height() / 2,
|
2020-11-18 09:11:12 +00:00
|
|
|
isOlderTop = $revisionWrapper.hasClass( 'mw-revslider-revision-older' ) && hasMovedTop,
|
2023-06-22 09:41:37 +00:00
|
|
|
isNewerBottom = $revisionWrapper.hasClass( 'mw-revslider-revision-newer' ) && !hasMovedTop;
|
|
|
|
let $neighborRevisionWrapper = $revisionWrapper;
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
if ( isOlderTop ) {
|
|
|
|
$neighborRevisionWrapper = $revisionWrapper.prev();
|
|
|
|
} else if ( isNewerBottom ) {
|
|
|
|
$neighborRevisionWrapper = $revisionWrapper.next();
|
|
|
|
}
|
2017-08-17 14:08:49 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
if ( $neighborRevisionWrapper.length === 0 ) {
|
|
|
|
return;
|
|
|
|
}
|
2017-08-17 14:08:49 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
if ( hasMovedTop ) {
|
2024-02-20 07:43:15 +00:00
|
|
|
this.setRevisionPreviewHighlight( $revisionWrapper.find( '.mw-revslider-revision-wrapper-up' ) );
|
2017-08-17 14:08:49 +00:00
|
|
|
if ( isOlderTop ) {
|
2024-02-20 07:43:15 +00:00
|
|
|
this.setRevisionPreviewHighlight( $neighborRevisionWrapper.find( '.mw-revslider-revision-wrapper-down' ) );
|
2017-08-17 14:08:49 +00:00
|
|
|
}
|
2020-11-18 09:11:12 +00:00
|
|
|
} else {
|
2024-02-20 07:43:15 +00:00
|
|
|
this.setRevisionPreviewHighlight( $revisionWrapper.find( '.mw-revslider-revision-wrapper-down' ) );
|
2020-11-18 09:11:12 +00:00
|
|
|
if ( isNewerBottom ) {
|
2024-02-20 07:43:15 +00:00
|
|
|
this.setRevisionPreviewHighlight( $neighborRevisionWrapper.find( '.mw-revslider-revision-wrapper-up' ) );
|
2017-08-17 14:08:49 +00:00
|
|
|
}
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
|
|
|
},
|
2017-08-17 14:08:49 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
/**
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {jQuery} $revisionWrapper
|
|
|
|
*/
|
2024-02-20 07:43:15 +00:00
|
|
|
setRevisionPreviewHighlight: function ( $revisionWrapper ) {
|
2020-11-18 09:11:12 +00:00
|
|
|
$revisionWrapper.addClass( 'mw-revslider-revision-hovered' );
|
|
|
|
},
|
|
|
|
|
2024-02-20 07:43:15 +00:00
|
|
|
removeAllRevisionPreviewHighlights: function () {
|
2020-11-18 09:11:12 +00:00
|
|
|
$( '.mw-revslider-revision-wrapper-up, .mw-revslider-revision-wrapper-down' )
|
|
|
|
.removeClass( 'mw-revslider-revision-hovered' );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {jQuery} $renderedList
|
|
|
|
*/
|
|
|
|
adjustRevisionSizes: function ( $renderedList ) {
|
2023-11-14 16:40:52 +00:00
|
|
|
const revs = this.revisionList.getRevisions();
|
|
|
|
const maxChangeSize = this.revisionList.getBiggestChangeSize();
|
2020-11-18 09:11:12 +00:00
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
for ( let i = 0; i < revs.length; i++ ) {
|
|
|
|
const diffSize = revs[ i ].getRelativeSize();
|
2023-11-14 16:40:52 +00:00
|
|
|
const relativeChangeSize = this.calcRelativeChangeSize( diffSize, maxChangeSize );
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
$renderedList.find( '.mw-revslider-revision[data-pos="' + ( i + 1 ) + '"]' ).css( {
|
|
|
|
height: relativeChangeSize + 'px',
|
|
|
|
top: diffSize > 0 ? '-' + relativeChangeSize + 'px' : 0
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2023-10-24 08:59:40 +00:00
|
|
|
/**
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2023-10-24 08:59:40 +00:00
|
|
|
* @param {number} diffSize
|
2023-11-14 16:40:52 +00:00
|
|
|
* @param {number} maxChangeSize
|
2023-10-24 08:59:40 +00:00
|
|
|
* @return {number}
|
|
|
|
*/
|
2023-11-14 16:40:52 +00:00
|
|
|
calcRelativeChangeSize: function ( diffSize, maxChangeSize ) {
|
2023-10-24 08:59:40 +00:00
|
|
|
if ( !diffSize ) {
|
2020-11-18 09:11:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return Math.ceil(
|
|
|
|
( this.maxRevisionHeight - this.minRevisionHeight ) *
|
2023-11-14 16:40:52 +00:00
|
|
|
Math.log( Math.abs( diffSize ) ) / Math.log( maxChangeSize ) ) +
|
2020-11-18 09:11:12 +00:00
|
|
|
this.minRevisionHeight;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2023-08-31 10:08:43 +00:00
|
|
|
* Clears the current revision focus and removes highlights and tooltip
|
2020-11-18 09:11:12 +00:00
|
|
|
*/
|
2023-08-31 10:08:43 +00:00
|
|
|
removeCurrentRevisionFocus: function () {
|
2023-09-06 07:09:26 +00:00
|
|
|
this.clearRevisionFocusDelay();
|
2023-08-31 10:08:43 +00:00
|
|
|
this.removeCurrentRevisionFocusHighlight();
|
2020-11-18 09:11:12 +00:00
|
|
|
$( '.mw-revslider-revision-tooltip' ).remove();
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2023-09-06 07:09:26 +00:00
|
|
|
* Removes the current revision focus after 750ms
|
2023-10-24 13:45:31 +00:00
|
|
|
*
|
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
*/
|
2023-08-31 10:08:43 +00:00
|
|
|
removeCurrentRevisionFocusWithDelay: function () {
|
2023-09-06 07:09:26 +00:00
|
|
|
this.tooltipTimeout = window.setTimeout( this.removeCurrentRevisionFocus.bind( this ), 750 );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
clearRevisionFocusDelay: function () {
|
|
|
|
window.clearTimeout( this.tooltipTimeout );
|
2023-08-31 10:08:43 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
removeCurrentRevisionFocusHighlight: function () {
|
|
|
|
$( '.mw-revslider-revision-wrapper-hovered' )
|
|
|
|
.removeClass( 'mw-revslider-revision-wrapper-hovered' );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the revision focus adding highlights and tooltip
|
|
|
|
*
|
|
|
|
* @param {jQuery} $revisionWrapper
|
|
|
|
*/
|
|
|
|
setRevisionFocus: function ( $revisionWrapper ) {
|
|
|
|
if ( $revisionWrapper.hasClass( 'mw-revslider-revision-wrapper-hovered' ) ) {
|
2023-09-06 07:09:26 +00:00
|
|
|
this.clearRevisionFocusDelay();
|
2023-08-31 10:08:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.removeCurrentRevisionFocus();
|
|
|
|
|
|
|
|
this.showTooltip( $revisionWrapper );
|
|
|
|
$revisionWrapper.addClass( 'mw-revslider-revision-wrapper-hovered' );
|
2020-11-18 09:11:12 +00:00
|
|
|
},
|
|
|
|
|
2023-08-28 15:45:54 +00:00
|
|
|
/**
|
|
|
|
* Hides the current tooltip when the focus moves away and not to a pointer or tooltip
|
|
|
|
*
|
|
|
|
* @param {jQuery.Event} event
|
|
|
|
*/
|
|
|
|
onFocusBlur: function ( event ) {
|
|
|
|
const $outElement = $( event.relatedTarget );
|
|
|
|
if ( $outElement.hasClass( '.mw-revslider-pointer' ) || $outElement.closest( '.mw-revslider-revision-tooltip' ).length ) {
|
|
|
|
return;
|
|
|
|
}
|
2023-08-31 10:08:43 +00:00
|
|
|
this.removeCurrentRevisionFocus();
|
2023-08-28 15:45:54 +00:00
|
|
|
},
|
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
/**
|
2023-08-25 10:36:26 +00:00
|
|
|
* Hides the previous tooltip and shows the new one. Also styles a revision as hovered.
|
2020-11-18 09:11:12 +00:00
|
|
|
*
|
|
|
|
* @param {jQuery} $revisionWrapper
|
|
|
|
*/
|
|
|
|
showTooltip: function ( $revisionWrapper ) {
|
2023-09-30 09:52:20 +00:00
|
|
|
const $revision = $revisionWrapper.find( '.mw-revslider-revision' );
|
|
|
|
const revId = +$revision.attr( 'data-revid' );
|
|
|
|
const pos = +$revision.attr( 'data-pos' );
|
|
|
|
|
2023-11-14 16:40:52 +00:00
|
|
|
const revision = this.revisionList.getRevisions().find( ( rev ) => rev.getId() === revId );
|
2023-06-22 09:41:37 +00:00
|
|
|
const tooltip = this.makeTooltip( revision, $revisionWrapper );
|
2023-08-31 10:08:43 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
// eslint-disable-next-line mediawiki/class-doc
|
2023-08-28 15:45:54 +00:00
|
|
|
tooltip.$element
|
|
|
|
.addClass( 'mw-revslider-revision-tooltip-' + pos )
|
2023-09-06 07:09:26 +00:00
|
|
|
.on( 'focusout', this.onFocusBlur.bind( this ) )
|
|
|
|
// Set event handlers so that tooltips do not disappear immediately when hover is gone
|
|
|
|
.on( 'mouseleave', this.removeCurrentRevisionFocusWithDelay.bind( this ) )
|
|
|
|
.on( 'mouseenter', this.clearRevisionFocusDelay.bind( this ) );
|
2017-03-03 13:55:42 +00:00
|
|
|
|
2023-08-23 10:04:10 +00:00
|
|
|
const $focusedRevisionPointer = $( '.mw-revslider-pointer[data-pos="' + pos + '"]' );
|
|
|
|
if ( $focusedRevisionPointer.length ) {
|
|
|
|
// Make sure tooltips are added next to the pointer so they can be reached when tabbing
|
|
|
|
$focusedRevisionPointer.parent().append( tooltip.$element );
|
|
|
|
} else {
|
|
|
|
$( document.body ).append( tooltip.$element );
|
|
|
|
}
|
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
tooltip.toggle( true );
|
|
|
|
},
|
2016-08-03 12:08:05 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
/**
|
|
|
|
* Sets an event handler to close tooltips when clicking somewhere outside
|
2023-10-24 13:45:31 +00:00
|
|
|
*
|
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
*/
|
|
|
|
closeTooltipsOnClick: function () {
|
2023-06-22 09:41:37 +00:00
|
|
|
const self = this;
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
$( document )
|
2024-06-08 04:13:35 +00:00
|
|
|
.on( 'click', ( event ) => {
|
2023-10-23 12:48:50 +00:00
|
|
|
const $inside = $( event.target )
|
|
|
|
.closest( '.mw-revslider-revision-tooltip, .mw-revslider-revisions-container' );
|
|
|
|
if ( !$inside.length ) {
|
2023-08-31 10:08:43 +00:00
|
|
|
self.removeCurrentRevisionFocus();
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
2016-08-03 12:08:05 +00:00
|
|
|
} );
|
2020-11-18 09:11:12 +00:00
|
|
|
},
|
2018-08-13 04:42:09 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
/**
|
|
|
|
* Generates the HTML for a tooltip that appears on hover above each revision on the slider
|
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {Revision} revision
|
|
|
|
* @param {jQuery} $revisionWrapper
|
|
|
|
* @return {OO.ui.PopupWidget}
|
|
|
|
*/
|
|
|
|
makeTooltip: function ( revision, $revisionWrapper ) {
|
2023-06-22 09:41:37 +00:00
|
|
|
const $tooltip = $( '<div>' )
|
2020-11-18 09:11:12 +00:00
|
|
|
.append(
|
|
|
|
$( '<p>' ).append(
|
|
|
|
$( '<strong>' ).text( mw.msg( 'revisionslider-label-date' ) + mw.msg( 'colon-separator' ) ),
|
|
|
|
$( '<a>' ).attr( 'href', mw.util.getUrl( null, { oldid: revision.id } ) )
|
|
|
|
.text( revision.getFormattedDate() )
|
2018-08-13 04:42:09 +00:00
|
|
|
),
|
2020-11-18 09:11:12 +00:00
|
|
|
this.makeUserLine( revision.getUser(), revision.getUserGender() ),
|
|
|
|
this.makeCommentLine( revision ),
|
|
|
|
this.makePageSizeLine( revision.getSize() ),
|
|
|
|
this.makeChangeSizeLine( revision.getRelativeSize() ),
|
2023-04-21 06:35:27 +00:00
|
|
|
revision.isMinor() ? $( '<p>' ).text( mw.msg( 'revisionslider-minoredit' ) ) : '',
|
2020-11-18 09:11:12 +00:00
|
|
|
this.makeTagsLine( revision )
|
2016-07-26 09:03:08 +00:00
|
|
|
);
|
2020-11-18 09:11:12 +00:00
|
|
|
return new OO.ui.PopupWidget( {
|
|
|
|
$content: $tooltip,
|
|
|
|
$floatableContainer: $revisionWrapper,
|
|
|
|
padded: true,
|
|
|
|
classes: [ 'mw-revslider-tooltip', 'mw-revslider-revision-tooltip' ]
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a link to user page or to contributions page for IP addresses
|
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {string} user
|
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
getUserPage: function ( user ) {
|
2023-09-30 09:52:20 +00:00
|
|
|
return ( mw.util.isIPAddress( user ) ? 'Special:Contributions/' : 'User:' ) + this.stripInvalidCharacters( user );
|
2020-11-18 09:11:12 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates the HTML for the user label
|
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {string} userString
|
2023-09-30 09:52:20 +00:00
|
|
|
* @param {string} [userGender='unknown']
|
2020-11-18 09:11:12 +00:00
|
|
|
* @return {string|jQuery}
|
|
|
|
*/
|
|
|
|
makeUserLine: function ( userString, userGender ) {
|
2023-06-22 09:41:37 +00:00
|
|
|
const self = this;
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
if ( !userString ) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
if ( !userGender ) {
|
|
|
|
userGender = 'unknown';
|
|
|
|
}
|
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
const $userBubble = $( '<div>' ).addClass( 'mw-revslider-bubble' )
|
2021-10-07 13:55:44 +00:00
|
|
|
.on( 'click mouseenter mouseleave', function ( event ) {
|
|
|
|
self.setUserFilterEvents( $( this ), userString, event );
|
|
|
|
} );
|
2023-09-01 07:44:40 +00:00
|
|
|
const $userLine = $( '<p>' ).addClass( 'mw-revslider-filter-highlightable-row mw-revslider-username-row' ).append(
|
2020-11-18 09:11:12 +00:00
|
|
|
$( '<strong>' ).text( mw.msg( 'revisionslider-label-username', userGender ) + mw.msg( 'colon-separator' ) ),
|
|
|
|
$( '<bdi>' ).append(
|
|
|
|
$( '<a>' ).addClass( 'mw-userlink' ).attr( 'href', mw.util.getUrl( this.getUserPage( userString ) ) ).text( this.stripInvalidCharacters( userString ) )
|
|
|
|
),
|
2021-10-07 13:55:44 +00:00
|
|
|
$userBubble
|
2020-11-18 09:11:12 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if ( self.selectedUser === userString ) {
|
|
|
|
self.selectedTag = '';
|
2023-09-01 07:44:40 +00:00
|
|
|
$userLine.addClass( 'mw-revslider-filter-highlight' );
|
|
|
|
$userBubble.addClass( 'mw-revslider-filter-highlight-bubble' );
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $userLine;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set user filter events for revisions
|
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {jQuery} $userBubble
|
|
|
|
* @param {string} userName
|
|
|
|
* @param {MouseEvent} event
|
|
|
|
*/
|
|
|
|
setUserFilterEvents: function ( $userBubble, userName, event ) {
|
2023-08-24 08:58:22 +00:00
|
|
|
const $userLine = $userBubble.parent();
|
2018-08-13 04:42:09 +00:00
|
|
|
|
2023-08-24 08:58:22 +00:00
|
|
|
if ( this.selectedUser === userName && event.type !== 'click' ) {
|
2020-11-18 09:11:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-01 07:44:40 +00:00
|
|
|
this.removeRevisionFilterHighlighting();
|
2020-11-18 09:11:12 +00:00
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
let oldUser;
|
2020-11-18 09:11:12 +00:00
|
|
|
switch ( event.type ) {
|
|
|
|
case 'mouseenter':
|
2023-09-01 07:44:40 +00:00
|
|
|
$userLine.addClass( 'mw-revslider-filter-highlight' );
|
|
|
|
$userBubble.addClass( 'mw-revslider-filter-highlight-bubble' );
|
|
|
|
this.filterHighlightSameUserRevisions( userName );
|
2020-11-18 09:11:12 +00:00
|
|
|
break;
|
|
|
|
case 'mouseleave':
|
2023-09-01 07:44:40 +00:00
|
|
|
this.reApplySavedFilterHighlighting( $userLine, $userBubble );
|
2020-11-18 09:11:12 +00:00
|
|
|
break;
|
|
|
|
case 'click':
|
2023-08-24 08:58:22 +00:00
|
|
|
oldUser = this.selectedUser;
|
2023-09-01 07:44:40 +00:00
|
|
|
this.resetRevisionFilterHighlighting();
|
2018-08-13 04:42:09 +00:00
|
|
|
|
2023-09-01 07:44:40 +00:00
|
|
|
$userLine.addClass( 'mw-revslider-filter-highlight' );
|
|
|
|
$userBubble.addClass( 'mw-revslider-filter-highlight-bubble' );
|
2019-04-01 14:42:00 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
if ( oldUser !== userName ) {
|
2023-09-01 07:44:40 +00:00
|
|
|
this.filterHighlightSameUserRevisions( userName );
|
2023-08-24 08:58:22 +00:00
|
|
|
this.selectedUser = userName;
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
2016-05-30 12:36:48 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
/**
|
|
|
|
* Highlights revisions of the sameUser
|
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {string} userString
|
|
|
|
*/
|
2023-09-01 07:44:40 +00:00
|
|
|
filterHighlightSameUserRevisions: function ( userString ) {
|
2020-11-18 09:11:12 +00:00
|
|
|
$( '[data-user="' + userString + '"]' ).parent()
|
2023-09-01 07:44:40 +00:00
|
|
|
.toggleClass( 'mw-revslider-revision-filter-highlight' );
|
2020-11-18 09:11:12 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {string} s
|
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
stripInvalidCharacters: function ( s ) {
|
|
|
|
return s.replace( /[<>&]/g, '' );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates the HTML for the comment label
|
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {Revision} rev
|
|
|
|
* @return {string|jQuery}
|
|
|
|
*/
|
|
|
|
makeCommentLine: function ( rev ) {
|
2023-08-25 12:42:32 +00:00
|
|
|
const html = rev.getParsedComment();
|
|
|
|
if ( !html.trim().length ) {
|
2020-11-18 09:11:12 +00:00
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
return $( '<p>' ).append(
|
|
|
|
$( '<strong>' ).text( mw.msg( 'revisionslider-label-comment' ) + mw.msg( 'colon-separator' ) ),
|
|
|
|
$( '<em>' ).append(
|
2023-08-25 12:42:32 +00:00
|
|
|
$( '<bdi>' ).append( html )
|
2020-11-18 09:11:12 +00:00
|
|
|
)
|
|
|
|
);
|
|
|
|
},
|
2018-10-24 09:07:34 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
/**
|
|
|
|
* Generates the HTML for the tags label
|
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {Revision} rev
|
|
|
|
* @return {string|jQuery}
|
|
|
|
*/
|
|
|
|
makeTagsLine: function ( rev ) {
|
2023-06-22 09:41:37 +00:00
|
|
|
const self = this;
|
2018-10-24 09:07:34 +00:00
|
|
|
|
2023-08-24 09:29:18 +00:00
|
|
|
const tags = rev.getTags();
|
|
|
|
if ( !tags.length ) {
|
2020-11-18 09:11:12 +00:00
|
|
|
return '';
|
|
|
|
}
|
2018-10-24 09:07:34 +00:00
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
const $tagLines = $( '<div>' );
|
2018-10-24 09:07:34 +00:00
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
for ( let i = 0; i < tags.length; i++ ) {
|
|
|
|
const $tagBubble = $( '<div>' ).addClass( 'mw-revslider-bubble' )
|
2021-10-07 13:55:44 +00:00
|
|
|
.on( 'click mouseenter mouseleave', function ( event ) {
|
|
|
|
self.setTagFilterEvents( $( this ), event );
|
|
|
|
} );
|
2023-09-01 07:44:40 +00:00
|
|
|
const $tagLine = $( '<div>' ).addClass( 'mw-revslider-filter-highlightable-row mw-revslider-tag-row' ).append(
|
2020-11-18 09:11:12 +00:00
|
|
|
tags[ i ],
|
2021-10-07 13:55:44 +00:00
|
|
|
$tagBubble,
|
2020-11-18 09:11:12 +00:00
|
|
|
'<br>'
|
|
|
|
);
|
|
|
|
|
|
|
|
if ( self.selectedTag === tags[ i ] ) {
|
|
|
|
self.selectedUser = '';
|
2023-09-01 07:44:40 +00:00
|
|
|
$tagLine.addClass( 'mw-revslider-filter-highlight' );
|
|
|
|
$tagLine.find( $tagBubble ).addClass( 'mw-revslider-filter-highlight-bubble' );
|
2019-04-01 14:42:00 +00:00
|
|
|
}
|
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
$tagLine.attr( 'data-tag-name', tags[ i ] );
|
|
|
|
$tagLines.append( $tagLine );
|
|
|
|
}
|
|
|
|
|
|
|
|
return $tagLines;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set tag filter events for revisions
|
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {jQuery} $tagBubble
|
|
|
|
* @param {MouseEvent} event
|
|
|
|
*/
|
|
|
|
setTagFilterEvents: function ( $tagBubble, event ) {
|
2023-08-24 08:58:22 +00:00
|
|
|
const $tagLine = $tagBubble.parent(),
|
2024-02-21 12:56:44 +00:00
|
|
|
tagName = $tagLine.attr( 'data-tag-name' );
|
2020-11-18 09:11:12 +00:00
|
|
|
|
2023-08-24 08:58:22 +00:00
|
|
|
if ( this.selectedTag === tagName && event.type !== 'click' ) {
|
2020-11-18 09:11:12 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-04-01 14:42:00 +00:00
|
|
|
|
2023-09-01 07:44:40 +00:00
|
|
|
this.removeRevisionFilterHighlighting();
|
2020-11-18 09:11:12 +00:00
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
let oldTag;
|
2020-11-18 09:11:12 +00:00
|
|
|
switch ( event.type ) {
|
|
|
|
case 'mouseenter':
|
2023-09-01 07:44:40 +00:00
|
|
|
$tagLine.addClass( 'mw-revslider-filter-highlight' );
|
|
|
|
$tagBubble.addClass( 'mw-revslider-filter-highlight-bubble' );
|
|
|
|
this.filterHighlightSameTagRevisions( tagName );
|
2020-11-18 09:11:12 +00:00
|
|
|
break;
|
|
|
|
case 'mouseleave':
|
2023-09-01 07:44:40 +00:00
|
|
|
this.reApplySavedFilterHighlighting( $tagLine, $tagBubble );
|
2020-11-18 09:11:12 +00:00
|
|
|
break;
|
|
|
|
case 'click':
|
2023-08-24 08:58:22 +00:00
|
|
|
oldTag = this.selectedTag;
|
2023-09-01 07:44:40 +00:00
|
|
|
this.resetRevisionFilterHighlighting();
|
2020-11-18 09:11:12 +00:00
|
|
|
|
2023-09-01 07:44:40 +00:00
|
|
|
$tagLine.addClass( 'mw-revslider-filter-highlight' );
|
|
|
|
$tagBubble.addClass( 'mw-revslider-filter-highlight-bubble' );
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
if ( oldTag !== tagName ) {
|
2023-09-01 07:44:40 +00:00
|
|
|
this.filterHighlightSameTagRevisions( tagName );
|
2023-08-24 08:58:22 +00:00
|
|
|
this.selectedTag = tagName;
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2023-09-01 07:44:40 +00:00
|
|
|
* Highlight same tag revisions
|
2020-11-18 09:11:12 +00:00
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {string} tagName
|
|
|
|
*/
|
2023-09-01 07:44:40 +00:00
|
|
|
filterHighlightSameTagRevisions: function ( tagName ) {
|
2023-11-14 16:40:52 +00:00
|
|
|
const revs = this.revisionList.getRevisions();
|
2020-11-18 09:11:12 +00:00
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
for ( let i = 0; i < revs.length; i++ ) {
|
2023-10-23 12:53:33 +00:00
|
|
|
if ( revs[ i ].getTags().indexOf( tagName ) !== -1 ) {
|
|
|
|
$( '[data-revid="' + revs[ i ].id + '"]' ).parent()
|
|
|
|
.addClass( 'mw-revslider-revision-filter-highlight' );
|
2018-10-24 09:07:34 +00:00
|
|
|
}
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
|
|
|
},
|
2016-06-15 11:56:10 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
/**
|
2023-09-01 07:44:40 +00:00
|
|
|
* Re-apply filter highlighting from saved state
|
2020-11-18 09:11:12 +00:00
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {jQuery} $line
|
|
|
|
* @param {jQuery} $bubble
|
|
|
|
*/
|
2023-09-01 07:44:40 +00:00
|
|
|
reApplySavedFilterHighlighting: function ( $line, $bubble ) {
|
|
|
|
$line.removeClass( 'mw-revslider-filter-highlight' );
|
|
|
|
$bubble.removeClass( 'mw-revslider-filter-highlight-bubble' );
|
2020-11-18 09:11:12 +00:00
|
|
|
if ( this.selectedTag ) {
|
2023-09-01 07:44:40 +00:00
|
|
|
this.filterHighlightSameTagRevisions( this.selectedTag );
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
|
|
|
if ( this.selectedUser ) {
|
2023-09-01 07:44:40 +00:00
|
|
|
this.filterHighlightSameUserRevisions( this.selectedUser );
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
|
|
|
},
|
2016-06-15 11:56:10 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
/**
|
2023-09-01 07:44:40 +00:00
|
|
|
* Removes the filter highlighting from the revisions
|
2023-10-24 13:45:31 +00:00
|
|
|
*
|
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
*/
|
2023-09-01 07:44:40 +00:00
|
|
|
removeRevisionFilterHighlighting: function () {
|
|
|
|
$( '.mw-revslider-revision-wrapper' ).removeClass( 'mw-revslider-revision-filter-highlight' );
|
2020-11-18 09:11:12 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2023-09-01 07:44:40 +00:00
|
|
|
* Resets filter highlighting from setting state
|
2023-10-24 13:45:31 +00:00
|
|
|
*
|
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
*/
|
2023-09-01 07:44:40 +00:00
|
|
|
resetRevisionFilterHighlighting: function () {
|
|
|
|
$( '.mw-revslider-filter-highlightable-row' ).removeClass( 'mw-revslider-filter-highlight' );
|
|
|
|
$( '.mw-revslider-bubble' ).removeClass( 'mw-revslider-filter-highlight-bubble' );
|
2020-11-18 09:11:12 +00:00
|
|
|
this.selectedTag = '';
|
|
|
|
this.selectedUser = '';
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates the HTML for the page size label
|
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {number} size
|
|
|
|
* @return {jQuery}
|
|
|
|
*/
|
|
|
|
makePageSizeLine: function ( size ) {
|
2023-09-28 16:20:30 +00:00
|
|
|
return $( '<p>' )
|
|
|
|
.text( mw.msg( 'revisionslider-page-size', mw.language.convertNumber( size ), size ) )
|
|
|
|
.prepend( $( '<strong>' ).text( mw.msg( 'revisionslider-label-page-size' ) + mw.msg( 'colon-separator' ) ) );
|
2020-11-18 09:11:12 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates the HTML for the change size label
|
|
|
|
*
|
2023-10-24 13:45:31 +00:00
|
|
|
* @private
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {number} relativeSize
|
|
|
|
* @return {jQuery}
|
|
|
|
*/
|
|
|
|
makeChangeSizeLine: function ( relativeSize ) {
|
2023-06-22 09:41:37 +00:00
|
|
|
let changeSizeClass = 'mw-revslider-change-none',
|
2021-10-07 13:55:44 +00:00
|
|
|
leadingSign = '';
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
if ( relativeSize > 0 ) {
|
|
|
|
changeSizeClass = 'mw-revslider-change-positive';
|
|
|
|
leadingSign = '+';
|
|
|
|
} else if ( relativeSize < 0 ) {
|
|
|
|
changeSizeClass = 'mw-revslider-change-negative';
|
2016-05-10 12:42:05 +00:00
|
|
|
}
|
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
// Classes are documented above
|
|
|
|
// eslint-disable-next-line mediawiki/class-doc
|
2023-06-22 09:41:37 +00:00
|
|
|
const $changeNumber = $( '<span>' )
|
2020-11-18 09:11:12 +00:00
|
|
|
.addClass( changeSizeClass )
|
|
|
|
.attr( {
|
|
|
|
dir: 'ltr' // Make sure that minus/plus is on the left
|
|
|
|
} )
|
|
|
|
.text( leadingSign + mw.language.convertNumber( relativeSize ) );
|
|
|
|
|
|
|
|
return $( '<p>' ).append(
|
|
|
|
$( '<strong>' ).text( mw.msg( 'revisionslider-label-change-size' ) + mw.msg( 'colon-separator' ) ),
|
2024-04-26 11:46:13 +00:00
|
|
|
mw.message( 'revisionslider-change-size', $changeNumber, relativeSize, Math.abs( relativeSize ) ).parseDom()
|
2020-11-18 09:11:12 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {jQuery}
|
|
|
|
*/
|
|
|
|
getElement: function () {
|
|
|
|
return this.$html;
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
2020-03-29 21:15:32 +00:00
|
|
|
module.exports = RevisionListView;
|