/* * 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 . */ const { HtmlUtils } = require( 'mmv.bootstrap' ); const Description = require( './mmv.ui.description.js' ); const UiElement = require( './mmv.ui.js' ); const MetadataPanelScroller = require( './mmv.ui.metadataPanelScroller.js' ); const Permission = require( './mmv.ui.permission.js' ); const ProgressBar = require( './mmv.ui.progressBar.js' ); const StripeButtons = require( './mmv.ui.stripeButtons.js' ); const TruncatableTextField = require( './mmv.ui.truncatableTextField.js' ); /** * Represents the metadata panel in the viewer */ class MetadataPanel extends UiElement { /** * @param {jQuery} $container The container for the panel (.mw-mmv-post-image). * @param {jQuery} $aboveFold The brighter headline of the metadata panel (.mw-mmv-above-fold). * Called "aboveFold" for historical reasons, but actually a part of the next sibling of the element * is also above the fold (bottom of the screen). * @param {mw.SafeStorage} localStorage the localStorage object, for dependency injection * @param {Config} config A configuration object. */ constructor( $container, $aboveFold, localStorage, config ) { super( $container ); this.$aboveFold = $aboveFold; /** @property {Config} config - */ this.config = config; this.initializeHeader( localStorage ); this.initializeImageMetadata(); this.initializeAboutLinks(); } /** * FIXME this should be in the jquery.fullscreen plugin. * * @return {boolean} */ isFullscreened() { return $( this.$container ).closest( '.jq-fullscreened' ).length > 0; } attach() { this.scroller.attach(); this.buttons.attach(); this.title.attach(); this.creditField.attach(); this.$title .add( this.$authorAndSource ) .add( this.title.$ellipsis ) .add( this.creditField.$ellipsis ) .on( 'click.mmv-mp', ( e ) => { const clickTargetIsLink = $( e.target ).is( 'a' ); const clickTargetIsTruncated = !!$( e.target ).closest( '.mw-mmv-ttf-truncated' ).length; const someTextIsExpanded = !!$( e.target ).closest( '.mw-mmv-untruncated' ).length; if ( !clickTargetIsLink && // don't interfere with clicks on links in the text clickTargetIsTruncated && // don't expand when non-truncated text is clicked !someTextIsExpanded // ignore clicks if text is already expanded ) { if ( this.isFullscreened() ) { this.revealTruncatedText(); } else { this.scroller.toggle( 'up' ); } } } ); $( this.$container ).on( 'mmv-metadata-open.mmv-mp mmv-metadata-reveal-truncated-text.mmv-mp', () => { this.revealTruncatedText(); } ).on( 'mmv-metadata-close.mmv-mp', () => { this.hideTruncatedText(); } ).on( 'mouseleave.mmv-mp', () => { if ( this.isFullscreened() ) { const duration = parseFloat( this.$container.css( 'transition-duration' ) ) * 1000 || 0; this.panelShrinkTimeout = setTimeout( () => { this.hideTruncatedText(); }, duration ); } } ).on( 'mouseenter.mmv-mp', () => { clearTimeout( this.panelShrinkTimeout ); } ).on( 'mmv-permission-grow.mmv-mp', () => { this.$permissionLink .text( mw.message( 'multimediaviewer-permission-link-hide' ).text() ); } ).on( 'mmv-permission-shrink.mmv-mp', () => { this.$permissionLink .text( mw.message( 'multimediaviewer-permission-link' ).text() ); } ); this.handleEvent( 'fullscreenchange.lip', () => { this.hideTruncatedText(); } ); } unattach() { this.scroller.freezeHeight(); this.$title .add( this.title.$ellipsis ) .add( this.$authorAndSource ) .add( this.creditField.$ellipsis ) .off( 'click.mmv-mp' ); $( this.$container ).off( '.mmv-mp' ); this.scroller.unattach(); this.buttons.unattach(); this.clearEvents(); } empty() { this.scroller.freezeHeight(); this.scroller.empty(); this.buttons.empty(); this.description.empty(); this.permission.empty(); this.$title.removeClass( 'error' ); this.title.empty(); this.creditField.empty(); this.$license.empty().prop( 'href', '#' ); this.$licenseLi.addClass( 'empty' ); this.$permissionLink.hide(); this.$restrictions.children().hide(); this.$filename.empty(); this.$filenamePrefix.empty(); this.$filenameLi.addClass( 'empty' ); this.$datetimeCreated.empty(); this.$datetimeCreatedLi.addClass( 'empty' ); this.$datetimeUpdated.empty(); this.$datetimeUpdatedLi.addClass( 'empty' ); this.$location.empty(); this.$locationLi.addClass( 'empty' ); this.progressBar.empty(); this.$container.removeClass( 'mw-mmv-untruncated' ); } /* Initialization methods */ /** * Initializes the header, which contains the title, credit, and license elements. * * @param {mw.SafeStorage} localStorage the localStorage object, for dependency injection */ initializeHeader( localStorage ) { this.progressBar = new ProgressBar( this.$aboveFold ); this.scroller = new MetadataPanelScroller( this.$container, this.$aboveFold, localStorage ); this.$titleDiv = $( '
' ) .addClass( 'mw-mmv-title-contain' ) .appendTo( this.$aboveFold ); this.$container.append( this.$aboveFold ); this.initializeButtons(); // float, needs to be on top this.initializeTitle(); } /** * Initializes the title elements. */ initializeTitle() { this.$titlePara = $( '

' ) .addClass( 'mw-mmv-title-para mw-parser-output' ) .appendTo( this.$aboveFold ); this.$title = $( '' ) .addClass( 'mw-mmv-title' ); this.title = new TruncatableTextField( this.$titlePara, this.$title, { styles: [ 'mw-mmv-title-small', 'mw-mmv-title-smaller' ] } ); this.title.setTitle( mw.message( 'multimediaviewer-title-popup-text' ), mw.message( 'multimediaviewer-title-popup-text-more' ) ); this.$title.add( this.title.$ellipsis ); } initializeButtons() { this.buttons = new StripeButtons( this.$titleDiv ); } /** * Initializes the main body of metadata elements. */ initializeImageMetadata() { this.$container.addClass( 'mw-mmv-ttf-ellipsis-container' ); this.$imageMetadata = $( '

' ) .addClass( 'mw-mmv-image-metadata' ) .appendTo( this.$container ); this.$imageMetadataLeft = $( '
' ) .addClass( 'mw-mmv-image-metadata-column mw-mmv-image-metadata-desc-column' ) .appendTo( this.$imageMetadata ); this.$imageMetadataRight = $( '
' ) .addClass( 'mw-mmv-image-metadata-column mw-mmv-image-metadata-links-column' ) .appendTo( this.$imageMetadata ); this.initializeCredit(); this.description = new Description( this.$imageMetadataLeft ); this.permission = new Permission( this.$imageMetadataLeft, this.scroller ); this.initializeImageLinks(); } /** * Initializes the credit elements. */ initializeCredit() { this.$credit = $( '

' ) .addClass( 'mw-mmv-credit empty' ) .appendTo( this.$imageMetadataLeft ); // we need an inline container for tipsy, otherwise it would be centered weirdly this.$authorAndSource = $( '' ) .addClass( 'mw-mmv-source-author' ); this.creditField = new TruncatableTextField( this.$credit, this.$authorAndSource, { styles: [] } ); this.creditField.setTitle( mw.message( 'multimediaviewer-credit-popup-text' ), mw.message( 'multimediaviewer-credit-popup-text-more' ) ); this.$authorAndSource.add( this.creditField.$ellipsis ); } /** * Initializes the list of image metadata on the right side of the panel. */ initializeImageLinks() { this.$imageLinkDiv = $( '

' ) .addClass( 'mw-mmv-image-links-div' ) .appendTo( this.$imageMetadataRight ); this.$imageLinks = $( '