/*
* This file is part of the MediaWiki extension MultimediaViewer.
*
* MultimediaViewer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* MultimediaViewer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MultimediaViewer. If not, see .
*/
( function () {
var LIP;
/**
* Represents the main interface of the lightbox
*
* @class mw.mmv.LightboxInterface
* @extends mw.mmv.ui.Element
* @constructor
*/
function LightboxInterface() {
this.localStorage = mw.storage;
/** @property {mw.mmv.Config} config - */
this.config = new mw.mmv.Config(
mw.config.get( 'wgMultimediaViewer', {} ),
mw.config,
mw.user,
new mw.Api(),
this.localStorage
);
/**
* @property {mw.mmv.ThumbnailWidthCalculator}
* @private
*/
this.thumbnailWidthCalculator = new mw.mmv.ThumbnailWidthCalculator();
this.init();
mw.mmv.ui.Element.call( this, this.$wrapper );
}
OO.inheritClass( LightboxInterface, mw.mmv.ui.Element );
LIP = LightboxInterface.prototype;
/**
* The currently selected LightboxImage.
*
* @type {mw.mmv.LightboxImage}
* @protected
*/
LIP.currentImage = null;
/**
* Initialize the entire interface - helper method.
*/
LIP.init = function () {
// SVG filter, needed to achieve blur in Firefox
// eslint-disable-next-line no-jquery/no-parse-html-literal
this.$filter = $( '' );
this.$wrapper = $( '
' )
.addClass( 'mw-mmv-above-fold' );
this.$main.append(
this.$preDiv,
this.$imageWrapper,
this.$postDiv,
this.$filter
);
this.$wrapper.append(
this.$main
);
this.setupCanvasButtons();
this.panel = new mw.mmv.ui.MetadataPanel( this.$postDiv, this.$aboveFold, this.localStorage, this.config );
this.buttons = new mw.mmv.ui.CanvasButtons( this.$preDiv, this.$closeButton, this.$fullscreenButton );
this.canvas = new mw.mmv.ui.Canvas( this.$innerWrapper, this.$imageWrapper, this.$wrapper );
this.fileReuse = new mw.mmv.ui.reuse.Dialog( this.$innerWrapper, this.buttons.$reuse, this.config );
this.downloadDialog = new mw.mmv.ui.download.Dialog( this.$innerWrapper, this.buttons.$download, this.config );
this.optionsDialog = new mw.mmv.ui.OptionsDialog( this.$innerWrapper, this.buttons.$options, this.config );
};
/**
* Sets up the file reuse data in the DOM
*
* @param {mw.mmv.model.Image} image
* @param {mw.mmv.model.Repo} repo
* @param {string} caption
* @param {string} alt
*/
LIP.setFileReuseData = function ( image, repo, caption, alt ) {
this.fileReuse.set( image, repo, caption, alt );
this.downloadDialog.set( image, repo );
};
/**
* Empties the interface.
*/
LIP.empty = function () {
this.panel.empty();
this.canvas.empty();
this.buttons.empty();
this.$main.addClass( 'metadata-panel-is-closed' )
.removeClass( 'metadata-panel-is-open' );
};
/**
* Opens the lightbox.
*/
LIP.open = function () {
this.empty();
this.attach();
};
/**
* Attaches the interface to the DOM.
*
* @param {string} [parentId] parent id where we want to attach the UI. Defaults to document
* element, override is mainly used for testing.
*/
LIP.attach = function ( parentId ) {
var ui = this,
$parent;
// Advanced description needs to be below the fold when the lightbox opens
// regardless of what the scroll value was prior to opening the lightbox
// If the lightbox is already attached, it means we're doing prev/next, and
// we should avoid scrolling the panel
if ( !this.attached ) {
$( window ).scrollTop( 0 );
}
// Make sure that the metadata is going to be at the bottom when it appears
// 83 is the height of the top metadata area. Which can't be measured by
// reading the DOM at this point of the execution, unfortunately
this.$postDiv.css( 'top', ( $( window ).height() - 83 ) + 'px' );
// Re-appending the same content can have nasty side-effects
// Such as the browser leaving fullscreen mode if the fullscreened element is part of it
if ( this.currentlyAttached ) {
return;
}
this.handleEvent( 'keyup', function ( e ) {
if ( e.keyCode === 27 && !( e.altKey || e.ctrlKey || e.shiftKey || e.metaKey ) ) {
// Escape button pressed
ui.unattach();
}
} );
this.handleEvent( 'jq-fullscreen-change.lip', function ( e ) {
ui.fullscreenChange( e );
} );
this.handleEvent( 'keydown', function ( e ) { ui.keydown( e ); } );
// mousemove generates a ton of events, which is why we throttle it
this.handleEvent( 'mousemove.lip', $.throttle( 250, function ( e ) {
ui.mousemove( e );
} ) );
this.handleEvent( 'mmv-faded-out', function ( e ) { ui.fadedOut( e ); } );
this.handleEvent( 'mmv-fade-stopped', function ( e ) { ui.fadeStopped( e ); } );
this.buttons.connect( this, {
next: [ 'emit', 'next' ],
prev: [ 'emit', 'prev' ]
} );
$parent = $( parentId || document.body );
// Clean up fullscreen data because hard-existing fullscreen might have left
// jquery.fullscreen unable to remove the class and attribute, since $main wasn't
// attached to the DOM anymore at the time the jq-fullscreen-change event triggered
this.$main.data( 'isFullscreened', false ).removeClass( 'jq-fullscreened' );
this.isFullscreen = false;
$parent
.append(
this.$wrapper
);
this.currentlyAttached = true;
this.panel.attach();
this.canvas.attach();
// cross-communication between panel and canvas, sort of
this.$postDiv.on( 'mmv-metadata-open.lip', function () {
ui.$main.addClass( 'metadata-panel-is-open' )
.removeClass( 'metadata-panel-is-closed' );
} ).on( 'mmv-metadata-close.lip', function () {
ui.$main.removeClass( 'metadata-panel-is-open' )
.addClass( 'metadata-panel-is-closed' );
} );
this.$wrapper.on( 'mmv-panel-close-area-click.lip', function () {
ui.panel.scroller.toggle( 'down' );
} );
// Buttons fading might not had been reset properly after a hard fullscreen exit
// This needs to happen after the parent attach() because the buttons need to be attached
// to the DOM for $.fn.stop() to work
this.buttons.stopFade();
this.buttons.attach();
this.fileReuse.attach();
this.downloadDialog.attach();
this.optionsDialog.attach();
// Reset the cursor fading
this.fadeStopped();
this.attached = true;
};
/**
* Detaches the interface from the DOM.
*/
LIP.unattach = function () {
mw.mmv.actionLogger.log( 'close' );
// Has to happen first so that the scroller can freeze with visible elements
this.panel.unattach();
this.$wrapper.detach();
this.currentlyAttached = false;
this.buttons.unattach();
this.$postDiv.off( '.lip' );
this.$wrapper.off( 'mmv-panel-close-area-click.lip' );
this.fileReuse.unattach();
this.fileReuse.closeDialog();
this.downloadDialog.unattach();
this.downloadDialog.closeDialog();
this.optionsDialog.unattach();
this.optionsDialog.closeDialog();
// Canvas listens for events from dialogs, so should be unattached at the end
this.canvas.unattach();
this.clearEvents();
this.buttons.disconnect( this, {
next: [ 'emit', 'next' ],
prev: [ 'emit', 'prev' ]
} );
// We trigger this event on the document because unattach() can run
// when the interface is unattached
$( document ).trigger( $.Event( 'mmv-close' ) )
.off( 'jq-fullscreen-change.lip' );
this.attached = false;
};
/**
* Exits fullscreen mode.
*/
LIP.exitFullscreen = function () {
this.fullscreenButtonJustPressed = true;
this.$main.exitFullscreen();
};
/**
* Enters fullscreen mode.
*/
LIP.enterFullscreen = function () {
this.$main.enterFullscreen();
};
/**
* Setup for canvas navigation buttons
*/
LIP.setupCanvasButtons = function () {
var ui = this,
tooltipDelay = mw.config.get( 'wgMultimediaViewer' ).tooltipDelay;
this.$closeButton = $( '