summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/MultimediaViewer/resources/mmv/ui/mmv.ui.metadataPanelScroller.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/MultimediaViewer/resources/mmv/ui/mmv.ui.metadataPanelScroller.js')
-rw-r--r--www/wiki/extensions/MultimediaViewer/resources/mmv/ui/mmv.ui.metadataPanelScroller.js247
1 files changed, 247 insertions, 0 deletions
diff --git a/www/wiki/extensions/MultimediaViewer/resources/mmv/ui/mmv.ui.metadataPanelScroller.js b/www/wiki/extensions/MultimediaViewer/resources/mmv/ui/mmv.ui.metadataPanelScroller.js
new file mode 100644
index 00000000..d7095322
--- /dev/null
+++ b/www/wiki/extensions/MultimediaViewer/resources/mmv/ui/mmv.ui.metadataPanelScroller.js
@@ -0,0 +1,247 @@
+/*
+ * This file is part of the MediaWiki extension MediaViewer.
+ *
+ * MediaViewer 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.
+ *
+ * MediaViewer 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 MediaViewer. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+( function ( mw, $, oo ) {
+ var MPSP;
+
+ /**
+ * Handles scrolling behavior of the metadata panel.
+ *
+ * @class mw.mmv.ui.MetadataPanelScroller
+ * @extends mw.mmv.ui.Element
+ * @constructor
+ * @param {jQuery} $container The container for the panel (.mw-mmv-post-image).
+ * @param {jQuery} $aboveFold The control bar element (.mw-mmv-above-fold).
+ * @param {mw.storage} localStorage the localStorage object, for dependency injection
+ */
+ function MetadataPanelScroller( $container, $aboveFold, localStorage ) {
+ mw.mmv.ui.Element.call( this, $container );
+
+ this.$aboveFold = $aboveFold;
+
+ /** @property {mw.storage} localStorage */
+ this.localStorage = localStorage;
+
+ /** @property {boolean} panelWasOpen state flag which will be used to detect open <-> closed transitions */
+ this.panelWasOpen = null;
+
+ /**
+ * Whether this user has ever opened the metadata panel.
+ * Based on a localstorage flag; will be set to true if the client does not support localstorage.
+ * @type {boolean}
+ */
+ this.hasOpenedMetadata = undefined;
+
+ /**
+ * Whether we've already fired an animation for the metadata div in this lightbox session.
+ * @property {boolean}
+ * @private
+ */
+ this.hasAnimatedMetadata = false;
+
+ this.initialize();
+ }
+ oo.inheritClass( MetadataPanelScroller, mw.mmv.ui.Element );
+ MPSP = MetadataPanelScroller.prototype;
+
+ MPSP.attach = function () {
+ var panel = this;
+
+ this.handleEvent( 'keydown', function ( e ) {
+ panel.keydown( e );
+ } );
+
+ $( window ).on( 'scroll.mmvp', $.throttle( 250, function () {
+ panel.scroll();
+ } ) );
+
+ this.$container.on( 'mmv-metadata-open', function () {
+ if ( !panel.hasOpenedMetadata && panel.localStorage.store ) {
+ panel.hasOpenedMetadata = true;
+ panel.localStorage.set( 'mmv.hasOpenedMetadata', '1' );
+ }
+ } );
+
+ // reset animation flag when the viewer is reopened
+ this.hasAnimatedMetadata = false;
+ };
+
+ MPSP.unattach = function () {
+ this.clearEvents();
+ $( window ).off( 'scroll.mmvp' );
+ this.$container.off( 'mmv-metadata-open' );
+ };
+
+ MPSP.empty = function () {
+ // need to remove this to avoid animating again when reopening lightbox on same page
+ this.$container.removeClass( 'invite' );
+
+ this.panelWasOpen = this.panelIsOpen();
+ };
+
+ /**
+ * Returns scroll top position when the panel is fully open.
+ * (In other words, the height of the area that is outside the screen, in pixels.)
+ *
+ * @return {number}
+ */
+ MPSP.getScrollTopWhenOpen = function () {
+ return this.$container.outerHeight() - parseInt( this.$aboveFold.css( 'min-height' ), 10 ) -
+ parseInt( this.$aboveFold.css( 'padding-bottom' ), 10 );
+ };
+
+ /**
+ * Makes sure the panel does not contract when it is emptied and thus keeps its position as much as possible.
+ * This should be called when switching images, before the panel is emptied, and should be undone with
+ * unfreezeHeight after the panel has been populeted with the new metadata.
+ */
+ MPSP.freezeHeight = function () {
+ var scrollTop, scrollTopWhenOpen;
+
+ if ( !this.$container.is( ':visible' ) ) {
+ return;
+ }
+
+ scrollTop = $( window ).scrollTop();
+ scrollTopWhenOpen = this.getScrollTopWhenOpen();
+
+ this.panelWasFullyOpen = ( scrollTop === scrollTopWhenOpen );
+ this.$container.css( 'min-height', this.$container.height() );
+ };
+
+ MPSP.unfreezeHeight = function () {
+ if ( !this.$container.is( ':visible' ) ) {
+ return;
+ }
+
+ this.$container.css( 'min-height', '' );
+ if ( this.panelWasFullyOpen ) {
+ $( window ).scrollTop( this.getScrollTopWhenOpen() );
+ }
+ };
+
+ MPSP.initialize = function () {
+ var value = this.localStorage.get( 'mmv.hasOpenedMetadata' );
+
+ // localStorage will only store strings; if values `null`, `false` or
+ // `0` are set, they'll come out as `"null"`, `"false"` or `"0"`, so we
+ // can be certain that an actual null is a failure to locate the item,
+ // and false is an issue with localStorage itself
+ if ( value !== false ) {
+ this.hasOpenedMetadata = value !== null;
+ } else {
+ // if there was an issue with localStorage, treat it as opened
+ this.hasOpenedMetadata = true;
+ }
+ };
+
+ /**
+ * Animates the metadata area when the viewer is first opened.
+ */
+ MPSP.animateMetadataOnce = function () {
+ if ( !this.hasOpenedMetadata && !this.hasAnimatedMetadata ) {
+ this.hasAnimatedMetadata = true;
+ this.$container.addClass( 'invite' );
+ }
+ };
+
+ /**
+ * Toggles the metadata div being totally visible.
+ *
+ * @param {string} [forceDirection] 'up' or 'down' makes the panel move on that direction (and is a noop
+ * if the panel is already at the upmost/bottommost position); without the parameter, the panel position
+ * is toggled. (Partially open counts as open.)
+ * @return {jQuery.Promise} A promise which resolves after the animation has finished.
+ */
+ MPSP.toggle = function ( forceDirection ) {
+ var scrollTopWhenOpen = this.getScrollTopWhenOpen(),
+ scrollTopWhenClosed = 0,
+ scrollTop = $( window ).scrollTop(),
+ panelIsOpen = scrollTop > scrollTopWhenClosed,
+ direction = forceDirection || ( panelIsOpen ? 'down' : 'up' ),
+ scrollTopTarget = ( direction === 'up' ) ? scrollTopWhenOpen : scrollTopWhenClosed;
+
+ // don't log / animate if the panel is already in the end position
+ if ( scrollTopTarget === scrollTop ) {
+ return $.Deferred().resolve().promise();
+ } else {
+ mw.mmv.actionLogger.log( direction === 'up' ? 'metadata-open' : 'metadata-close' );
+ if ( direction === 'up' && !panelIsOpen ) {
+ // FIXME nasty. This is not really an event but a command sent to the metadata panel;
+ // child UI elements should not send commands to their parents. However, there is no way
+ // to calculate the target scrollTop accurately without revealing the text, and the event
+ // which does that (metadata-open) is only triggered later in the process, when the panel
+ // actually scrolled, so we cannot use it here without risking triggering it multiple times.
+ this.$container.trigger( 'mmv-metadata-reveal-truncated-text' );
+ scrollTopTarget = this.getScrollTopWhenOpen();
+ }
+ return $( 'html, body' ).animate( { scrollTop: scrollTopTarget }, 'fast' ).promise();
+ }
+ };
+
+ /**
+ * Handles keydown events for this element.
+ *
+ * @param {jQuery.Event} e Key down event
+ */
+ MPSP.keydown = function ( e ) {
+ if ( e.altKey || e.shiftKey || e.ctrlKey || e.metaKey ) {
+ return;
+ }
+ switch ( e.which ) {
+ case 40: // Down arrow
+ // fall through
+ case 38: // Up arrow
+ this.toggle();
+ e.preventDefault();
+ break;
+ }
+ };
+
+ /**
+ * Returns whether the metadata panel is open. (Partially open is considered to be open.)
+ *
+ * @return {boolean}
+ */
+ MPSP.panelIsOpen = function () {
+ return $( window ).scrollTop() > 0;
+ };
+
+ /**
+ * Receives the window's scroll events and and turns them into business logic events
+ *
+ * @fires mmv-metadata-open
+ * @fires mmv-metadata-close
+ */
+ MPSP.scroll = function () {
+ var panelIsOpen = this.panelIsOpen();
+
+ if ( panelIsOpen && !this.panelWasOpen ) { // just opened
+ this.$container.trigger( 'mmv-metadata-open' );
+ // This will include keyboard- and mouseclick-initiated open events as well,
+ // since the panel is anomated, which counts as scrolling.
+ // Filtering these seems too much trouble to be worth it.
+ mw.mmv.actionLogger.log( 'metadata-scroll-open' );
+ } else if ( !panelIsOpen && this.panelWasOpen ) { // just closed
+ this.$container.trigger( 'mmv-metadata-close' );
+ mw.mmv.actionLogger.log( 'metadata-scroll-close' );
+ }
+ this.panelWasOpen = panelIsOpen;
+ };
+
+ mw.mmv.ui.MetadataPanelScroller = MetadataPanelScroller;
+}( mediaWiki, jQuery, OO ) );