' )
.addClass( 'mw-mlb-mmv-about-links' )
.append(
this.$mmvAboutLink,
' | ',
this.$mmvDiscussLink
)
.appendTo( this.$imageMetadata );
};
LIP.initializeNavigation = function () {
var viewer = this.viewer;
this.$nextButton = $( '
' )
.addClass( 'mw-mlb-next-image disabled' )
.html( ' ' )
.click( function () {
viewer.nextImage();
} );
this.$prevButton = $( '
' )
.addClass( 'mw-mlb-prev-image disabled' )
.html( ' ' )
.click( function () {
viewer.prevImage();
} );
};
LIP.toggleMetadata = function ( forceDirection ) {
var scrollTopWhenOpen = $( '.mlb-post-image' ).height() - $( '.mlb-controls' ).height(),
scrollTopTarget = $.scrollTo().scrollTop() > 0 ? 0 : scrollTopWhenOpen;
if ( forceDirection ) {
scrollTopTarget = forceDirection === 'down' ? 0 : scrollTopWhenOpen;
}
$.scrollTo( scrollTopTarget, 400 );
};
/**
* @method
* Sets the display name of the repository
* @param {string} displayname
* @param {string} favIcon
* @param {boolean} isLocal true if this is the local repo ( the file has been uploaded locally)
*/
LIP.setRepoDisplay = function ( displayname, favIcon, isLocal ) {
if ( isLocal ) {
this.$repo.text(
mw.message( 'multimediaviewer-repository-local' ).text()
);
} else {
displayname = displayname || mw.config.get( 'wgSiteName' );
this.$repo.text(
mw.message( 'multimediaviewer-repository', displayname ).text()
);
}
// This horror exists because the CSS uses a :before pseudo-class to
// define the repo icon. This is the only way to override it.
if ( favIcon ) {
if ( !this.$repoLiInlineStyle ) {
this.$repoLiInlineStyle = $( '' ).appendTo( 'head' );
}
this.$repoLiInlineStyle.html( '.mw-mlb-image-links li.mw-mlb-repo-li:before '
+ '{ background-image: url("'
+ favIcon
+ '"); }'
);
} else if ( this.$repoLiInlineStyle ) {
this.$repoLiInlineStyle.html( '' );
}
};
/**
* @method
* Sets the URL for the File: page of the image
* @param {string} url
*/
LIP.setFilePageLink = function ( url ) {
this.$repo.prop( 'href', url );
this.$license.prop( 'href', url );
};
/**
* @method
* Sets the image location data in the interface.
* @param {number} latdeg Number of degrees in latitude
* @param {number} latmin Number of minutes in latitude
* @param {number} latsec Number of seconds, rounded to two places, in latitude
* @param {number} latmsg The message representing the cardinal direction of the latitude
* @param {number} longdeg Number of degrees in longitude
* @param {number} longmin Number of minutes in longitude
* @param {number} longsec Number of seconds, rounded to two places, in longitude
* @param {number} longmsg The message representing the cardinal direction of the longitude
* @param {number} latitude The initial, decimal latitude
* @param {number} longitude The initial, decimal longitude
* @param {string} langcode Code for the language being used - like 'en', 'zh', or 'en-gb'
* @param {string} titleText Title of the file
*/
LIP.setLocationData = function (
latdeg, latmin, latsec, latmsg,
longdeg, longmin, longsec, longmsg,
latitude, longitude, langcode, titleText
) {
this.$location.text(
mw.message( 'multimediaviewer-geolocation',
mw.message(
'multimediaviewer-geoloc-coords',
mw.message(
'multimediaviewer-geoloc-coord',
mw.language.convertNumber( latdeg ),
mw.language.convertNumber( latmin ),
mw.language.convertNumber( latsec ),
mw.message( latmsg ).text()
).text(),
mw.message(
'multimediaviewer-geoloc-coord',
mw.language.convertNumber( longdeg ),
mw.language.convertNumber( longmin ),
mw.language.convertNumber( longsec ),
mw.message( longmsg ).text()
).text()
).text()
).text()
);
this.$location.prop( 'href', (
'//tools.wmflabs.org/geohack/geohack.php?pagename=' +
'File:' + titleText +
'¶ms=' +
latitude + '_N_' +
longitude + '_E_' +
'&language=' + langcode
) );
};
/**
* @method
* Saves some data about the image on the $useFile element for later setup.
* @param {mw.Title} title
* @param {string} src The URL for the full-size image
* @param {boolean} isLocal Whether the file is on this wiki or not
*/
LIP.initUseFileData = function ( title, src, isLocal ) {
this.$useFile.data( 'title', title );
this.$useFile.data( 'src', src );
this.$useFile.data( 'isLocal', isLocal );
};
/**
* @method
* Sets the link to the user page where possible
* @param {mw.mmv.model.Repo} repoData
* @param {string} username
* @param {string} gender
*/
LIP.setUserPageLink = function ( repoData, username, gender ) {
var userlink,
userpage = 'User:' + username;
if ( repoData instanceof mw.mmv.model.ForeignDbRepo ) {
// We basically can't do anything about this; fail
this.$username.addClass( 'empty' );
} else {
if ( repoData.absoluteArticlePath ) {
userlink = repoData.absoluteArticlePath;
} else {
userlink = mw.config.get( 'wgArticlePath' );
}
userlink = userlink.replace( '$1', userpage );
this.$username
.text(
mw.message( 'multimediaviewer-userpage-link', username, gender ).text()
)
.prop( 'href', userlink );
this.$usernameLi.toggleClass( 'empty', !username );
}
};
LIP.replaceImageWith = function ( imageEle ) {
var $image = $( imageEle );
this.currentImage.src = imageEle.src;
this.$image.replaceWith( $image );
this.$image = $image;
this.$image.css( {
maxHeight: $image.parent().height(),
maxWidth: $image.parent().width()
} );
};
LIP.fullscreenChange = function( e ) {
MLBInterface.prototype.fullscreenChange.call( this, e );
// Fullscreen change events can happen after unattach(), in which
// case we shouldn't do anything UI-related
if ( !this.currentlyAttached ) {
return;
}
this.viewer.resize( this );
if ( this.isFullscreen ) {
// When entering fullscreen without a mousemove, the browser
// still thinks that the cursor is where it was prior to entering
// fullscreen. I.e. on top of the fullscreen button
// Thus, we purposefully reset the saved position, so that
// the fade out really takes place (otherwise it's cancelled
// by updateControls which is called a few times when fullscreen opens)
this.mousePosition = { x: 0, y: 0 };
this.fadeOutButtons();
}
};
/**
* @method
* Handles keydown events on the document
* @param {jQuery.Event} e The jQuery keypress event object
*/
LIP.keydown = function ( e ) {
var isRtl = $( document.body ).hasClass( 'rtl' );
switch ( e.which ) {
case 37:
// Left arrow
if ( isRtl ) {
this.viewer.nextImage();
} else {
this.viewer.prevImage();
}
break;
case 39:
// Right arrow
if ( isRtl ) {
this.viewer.prevImage();
} else {
this.viewer.nextImage();
}
break;
case 40:
// Down arrow
this.toggleMetadata( 'down' );
e.preventDefault();
break;
case 38:
// Up arrow
this.toggleMetadata( 'up' );
e.preventDefault();
break;
}
};
/**
* @method
* Handles mousemove events on the document
*/
LIP.mousemove = function ( e ) {
if ( e ) {
// Saving the mouse position is useful whenever we need to
// run LIP.mousemove manually, such as when going to the next/prev
// element
this.mousePosition = { x: e.pageX, y: e.pageY};
}
this.revealButtonsAndFadeIfNeeded();
};
/**
* @method
* Reveals all active buttons and schedule a fade out if needed
*/
LIP.revealButtonsAndFadeIfNeeded = function () {
// Only fullscreen mode sees its buttons fade out when not used
if ( !this.isFullscreen ) {
return;
}
if ( this.buttonsFadeTimeout ) {
clearTimeout( this.buttonsFadeTimeout );
}
// Stop ongoing animations and make sure the buttons that need to be displayed are displayed
this.stopButtonsFade();
// this.mousePosition can be empty, for instance when we enter fullscreen and haven't
// recorded a real mousemove event yet
if ( !this.mousePosition
|| !this.isAnyActiveButtonHovered( this.mousePosition.x, this.mousePosition.y ) ) {
this.fadeOutButtons();
}
};
/**
* @method
* Fades out the active buttons
*/
LIP.fadeOutButtons = function () {
var ui = this;
// We don't use animation chaining because delay() can't be stop()ed
this.buttonsFadeTimeout = setTimeout( function() {
ui.$buttons.not( '.disabled' ).animate( { opacity: 0 }, 1000 );
}, 1500 );
};
/**
* @method
* Stops the fading animation of the buttons and cancel any opacity value
*/
LIP.stopButtonsFade = function () {
this.$buttons
.stop( true )
.css( 'opacity', '' );
};
/**
* @method
* Checks if any active buttons are currently hovered, given a position
* @param {number} x The horizontal coordinate of the position
* @param {number} y The vertical coordinate of the position
* @return bool
*/
LIP.isAnyActiveButtonHovered = function ( x, y ) {
// We don't use mouseenter/mouseleave events because content is subject
// to change underneath the cursor, eg. when entering fullscreen or
// when going prev/next (the button can disappear when reaching ends)
var hovered = false;
this.$buttons.not( '.disabled' ).each( function( idx, e ) {
var $e = $( e ),
offset = $e.offset();
if ( y >= offset.top
&& y <= offset.top + $e.height()
&& x >= offset.left
&& x <= offset.left + $e.width() ) {
hovered = true;
}
} );
return hovered;
};
/**
* @method
* Updates the next and prev buttons
* @param bool showPrevButton Whether the prev button should be revealed or not
* @param bool showNextButton Whether the next button should be revealed or not
*/
LIP.updateControls = function ( showPrevButton, showNextButton ) {
var prevNextTop = ( ( this.$imageWrapper.height() / 2 ) - 60 ) + 'px';
this.$postDiv.css( 'top', this.$imageWrapper.height() );
this.$nextButton.add( this.$prevButton ).css( {
top: prevNextTop
} );
this.$nextButton.toggleClass( 'disabled', !showPrevButton );
this.$prevButton.toggleClass( 'disabled', !showNextButton );
this.revealButtonsAndFadeIfNeeded();
};
mw.LightboxInterface = LightboxInterface;
}( mediaWiki, jQuery, OO, window.LightboxInterface ) );