mediawiki-extensions-Multim.../resources/mmv/ui/mmv.ui.stripeButtons.js
Gergő Tisza 8d05dd8a6d Make description page link and reuse widget more prominent
Rearrange the above-the-fold part of the metadata panel so that
the link to the file descriprion page and the button for the
download/share/embed widgets are easier to find.

One gotcha is that favicons are blown up to 32x32px (a small icon
in the middle of a big empty space would really look ridiculous)
so if a site uses 16x16px favicons it will look pixelated.
None of the WMF sites do that though, and if anyone else does, they
should just fix it, 16px favicons suck anyway (e.g. not retina friendly).

Change-Id: Icce12174b6ed89731fe75f8069c0b4a2b7161a34
Mingle: https://wikimedia.mingle.thoughtworks.com/projects/multimedia/cards/732
2014-06-28 00:28:11 +00:00

440 lines
13 KiB
JavaScript

/*
* 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 SBP;
/**
* @class mw.mmv.ui.StripeButtons
* @extends mw.mmv.ui.Element
* Class for buttons which are placed on the metadata stripe (the always visible part of the
* metadata panel).
* @constructor
* @param {jQuery} $container the title block (.mw-mmv-title-contain) which wraps the buttons and all
* other title elements
* @param {Object} localStorage the localStorage object, for dependency injection
*/
function StripeButtons( $container, localStorage ) {
mw.mmv.ui.Element.call( this, $container );
/** @property {Object} localStorage the window.localStorage object */
this.localStorage = localStorage;
this.$buttonContainer = $( '<div>' )
.addClass( 'mw-mmv-stripe-button-container' )
.appendTo( $container );
/**
* This holds the actual buttons.
* @property {Object.<string, jQuery>}
*/
this.buttons = {};
if ( this.shouldShowFeedbackSurvey() ) {
this.initFeedbackButton();
}
this.initReuseButton();
this.initDescriptionPageButton();
}
oo.inheritClass( StripeButtons, mw.mmv.ui.Element );
SBP = StripeButtons.prototype;
/**
* @protected
* Creates a new button on the metadata stripe.
* @param {string} cssClass CSS class name for the button
* @param {string} text HTML code for the button text
* @param {string} popupText HTML code for the popup text
*/
SBP.createButton = function ( cssClass, text, popupText ) {
var button,
tooltipDelay = mw.config.get( 'wgMultimediaViewer' ).tooltipDelay;
button = $( '<a>' )
.addClass( 'mw-mmv-stripe-button empty ' + cssClass )
// elements are right-floated so we use prepend instead of append to keep the order
.prependTo( this.$buttonContainer );
if ( text ) {
button.text( text ).addClass( 'has-label' );
}
if ( popupText ) {
button.prop( 'title', popupText ).tipsy( {
gravity: $( document.body ).hasClass( 'rtl' ) ? 'sw' : 'se',
delayIn: tooltipDelay
} );
}
return button;
};
/**
* @protected
* Creates the reuse button.
*/
SBP.initReuseButton = function() {
this.buttons.$reuse = this.createButton(
'mw-mmv-stripe-button-reuse',
mw.message( 'multimediaviewer-reuse-link' ).text()
);
};
/**
* @protected
* Creates a button linking to the file description page.
*/
SBP.initDescriptionPageButton = function() {
var tooltipDelay = mw.config.get( 'wgMultimediaViewer' ).tooltipDelay;
this.buttons.$descriptionPage = $( '<a>' )
.addClass( 'mw-mmv-repo-button empty ' )
.prependTo( this.$container )
.tipsy( {
gravity: $( document.body ).hasClass( 'rtl' ) ? 'se' : 'sw',
delayIn: tooltipDelay
} ).click( function () {
mw.mmv.actionLogger.log( 'file-description-page-abovefold' );
} ).prependTo( this.$container );
};
/**
* @protected
* Creates the feedback button.
*/
SBP.initFeedbackButton = function() {
var buttons = this;
this.buttons.$feedback = this.createButton(
'mw-mmv-stripe-button-feedback',
null,
mw.message( 'multimediaviewer-feedback-popup-text' ).plain()
).attr( {
target: '_blank',
href: this.getFeedbackSurveyUrl()
} ).click( function ( e ) {
buttons.openSurveyInNewWindow();
buttons.maxOutTooltipDisplayCount();
e.preventDefault();
} );
};
SBP.feedbackSettings = {
/** Show the tooltip this many seconds to get the user's attention, even when it is not hovered. */
tooltipDisplayDuration: 5,
/** Wait for this long after the viewer is opened, before showing the tooltip. */
tooltipDelay: 5,
/** Only show the tooltip this many times */
tooltipMaxDisplayCount: 3
};
/**
* Returns the number of times the tooltip was shown so far. This number is set to this.feedbackSettings.tooltipMaxDisplayCount if the
* user clicked on the link already, or we cannot count how many times the tooltip was shown
* already.
* @return {number}
*/
SBP.getTooltipDisplayCount = function () {
if ( !this.localStorage ) {
return this.feedbackSettings.tooltipMaxDisplayCount;
}
if ( this.tooltipDisplayCount === undefined ) {
this.tooltipDisplayCount = this.localStorage.getItem( 'mmv.tooltipDisplayCount' );
if ( this.tooltipDisplayCount === null ) {
this.tooltipDisplayCount = 0;
try {
this.localStorage.setItem( 'mmv.tooltipDisplayCount', 0 );
} catch ( e ) {
// localStorage is full or disabled
this.tooltipDisplayCount = this.feedbackSettings.tooltipMaxDisplayCount;
}
}
}
return this.tooltipDisplayCount;
};
/**
* Increases tooltip display count.
*/
SBP.increaseTooltipDisplayCount = function () {
this.getTooltipDisplayCount();
if ( this.tooltipDisplayCount !== undefined && this.tooltipDisplayCount < this.feedbackSettings.tooltipMaxDisplayCount ) {
this.tooltipDisplayCount++;
if ( this.localStorage ) {
try {
this.localStorage.setItem( 'mmv.tooltipDisplayCount', this.tooltipDisplayCount );
} catch ( e ) {
// localStorage is full or disabled
}
}
}
};
/**
* Sets tooltip display count so large that the tooltip will never be shown again.
* We use this for users who already opened the form.
*/
SBP.maxOutTooltipDisplayCount = function () {
this.getTooltipDisplayCount();
if ( this.tooltipDisplayCount !== undefined ) {
this.tooltipDisplayCount = this.feedbackSettings.tooltipMaxDisplayCount;
if ( this.localStorage ) {
try {
this.localStorage.setItem( 'mmv.tooltipDisplayCount', this.tooltipDisplayCount );
} catch ( e ) {
// localStorage is full or disabled
}
}
}
};
/**
* Show the tooltip to the user if it was not shown often enough yet.
*/
SBP.maybeDisplayTooltip = function () {
if (
this.readyToShowFeedbackTooltip &&
this.getTooltipDisplayCount() < this.feedbackSettings.tooltipMaxDisplayCount
) {
this.buttons.$feedback.tipsy( 'show' );
this.setTimer( 'feedbackTooltip.hide', function () {
this.buttons.$feedback.tipsy( 'hide' );
}, this.feedbackSettings.tooltipDisplayDuration * 1000 );
this.increaseTooltipDisplayCount();
this.readyToShowFeedbackTooltip = false;
} else {
// if the tooltip is visible already, make sure it is not hidden too quickly
this.resetTimer( 'feedbackTooltip.hide' );
}
};
/**
* Returns a link to a survey in the given language or null if that language is not supported
* @param {string|null} langcode
*/
SBP.getFeedbackSurveyBaseUrlForLanguage = function ( langcode ) {
var baseLangcode,
baseUrl = 'https://www.surveymonkey.com/s/media-viewer-1',
surveyTranslations = { ca: 'ca', hu: 'hu', fr: 'fr', pt: 'pt-br', 'pt-br': 'pt-br',
de: 'de', es: 'es', nl: 'nl' };
baseLangcode = langcode.split( /[_-]/ )[0]; // get rid of variants
if ( baseLangcode === 'en') {
return baseUrl;
} else if ( surveyTranslations[langcode] ) {
return baseUrl + '-' + surveyTranslations[langcode];
} else if ( surveyTranslations[baseLangcode] ) {
return baseUrl + '-' + surveyTranslations[baseLangcode];
} else {
return null;
}
};
/**
* Returns a link to a survey in the user language, or null if not supported
*/
SBP.getFeedbackSurveyBaseUrl = function () {
return this.getFeedbackSurveyBaseUrlForLanguage( mw.config.get( 'wgUserLanguage' ) );
};
/**
* Checks if it is suitable to show a survey to the current user.
*/
SBP.shouldShowFeedbackSurvey = function () {
return mw.config.get( 'wgMultimediaViewer' ).showSurvey &&
this.getFeedbackSurveyBaseUrl();
};
/**
* Generates a survey URL (currently constant but the possibility of splitting by
* editor cohort was mentioned).
* @return {string}
*/
SBP.getFeedbackSurveyUrl = function () {
return this.getFeedbackSurveyBaseUrl() + '?c=' + mw.config.get( 'wgDBname' );
};
/**
* Opens the survey in a new window, or brings it up if it is already opened.
*/
SBP.openSurveyInNewWindow = function () {
var surveyWindowWidth = screen.width * 0.85,
surveyWindowHeight = screen.height * 0.85,
feedbackSurveyWindowProperties = {
left: ( screen.width - surveyWindowWidth ) / 2,
top: ( screen.height - surveyWindowHeight ) / 2,
width: surveyWindowWidth,
height: surveyWindowHeight,
menubar: 0,
toolbar: 0,
location: 0,
personalbar: 0,
status: 0,
scrollbars: 1
};
if ( !this.surveyWindow || this.surveyWindow.closed ) {
this.surveyWindow = window.open( this.getFeedbackSurveyUrl(), 'mmv-survey',
this.createWindowOpenPropertyString( feedbackSurveyWindowProperties ) );
} else {
this.surveyWindow.focus();
}
};
/**
* @protected
* Takes a property object and turns it into a string suitable for the last parameter
* of window.open.
* @param {Object} properties
* @return {string}
*/
SBP.createWindowOpenPropertyString = function ( properties ) {
var propertyArray = [];
$.each( properties, function ( key, value ) {
propertyArray.push( key + '=' + value );
} );
return propertyArray.join( ',' );
};
/**
* @protected
* Runs code for each button, similarly to $.each.
* @param {function(jQuery, string)} callback a function that will be called with each button
*/
SBP.eachButton = function ( callback ) {
var buttonName;
for ( buttonName in this.buttons ) {
callback( this.buttons[buttonName], buttonName );
}
};
/**
* @inheritdoc
* @param {mw.mmv.model.Image} imageInfo
* @param {mw.mmv.model.Repo} repoInfo
*/
SBP.set = function ( imageInfo, repoInfo ) {
this.eachButton( function ( $button ) {
$button.removeClass( 'empty' );
} );
this.setDescriptionPageButton( imageInfo, repoInfo );
if ( this.shouldShowFeedbackSurvey() ) {
this.maybeDisplayTooltip();
}
};
/**
* @protected
* Updates the button linking to the file page.
* @param {mw.mmv.model.Image} imageInfo
* @param {mw.mmv.model.Repo} repoInfo
*/
SBP.setDescriptionPageButton = function ( imageInfo, repoInfo ) {
var descPagePopupMessage;
descPagePopupMessage = repoInfo.isLocal
? mw.message( 'multimediaviewer-description-page-button-text' ).plain()
: mw.message( 'multimediaviewer-description-page-popup-text', repoInfo.displayName ).text();
this.buttons.$descriptionPage.attr( {
href: imageInfo.descriptionUrl,
title: descPagePopupMessage,
'original-title': descPagePopupMessage // needed by jquery.tipsy
} );
if ( repoInfo.isCommons() ) {
this.buttons.$descriptionPage.addClass( 'mw-mmv-repo-button-commons' );
} else {
this.buttons.$descriptionPage.addClass( 'mw-mmv-repo-button-dynamic' );
if ( repoInfo.favIcon ) {
this.setInlineStyle( 'repo-button-description-page',
// needs to be more specific then the fallback rule in stripeButtons.less
'html .mw-mmv-repo-button-dynamic {' +
'background-image: url("' + repoInfo.favIcon + '");' +
// If you use a 16x16 favicon, this will look weird. You shouldn't.
'background-size: 32px 32px;' +
'}'
);
}
}
};
/**
* @inheritdoc
*/
SBP.empty = function () {
this.eachButton( function ( $button ) {
$button.addClass( 'empty' );
} );
this.buttons.$reuse.removeClass( 'open' );
this.buttons.$descriptionPage.attr( { href: null, title: null, 'original-title': null } )
.removeClass( 'mw-mmv-repo-button-dynamic mw-mmv-repo-button-commons' );
this.setInlineStyle( 'repo-button-description-page', null );
};
/**
* @event mmv-reuse-open
* Fired when the buttonto open the reuse dialog is clicked.
*/
/**
* Registers listeners.
*/
SBP.attach = function () {
var buttons = this.buttons;
buttons.$reuse.on( 'click.mmv-stripeButtons', function ( e ) {
$( document ).trigger( 'mmv-reuse-open' );
e.stopPropagation(); // the dialog would take it as an outside click and close
} );
this.handleEvent( 'mmv-reuse-opened', function () {
buttons.$reuse.addClass( 'open' );
} );
this.handleEvent( 'mmv-reuse-closed', function () {
buttons.$reuse.removeClass( 'open' );
} );
if ( this.shouldShowFeedbackSurvey() ) {
this.readyToShowFeedbackTooltip = false;
this.setTimer( 'feedbackTooltip.show', function () {
this.readyToShowFeedbackTooltip = true;
this.maybeDisplayTooltip();
}, this.feedbackSettings.tooltipDelay * 1000 );
}
};
/**
* Clears listeners.
*/
SBP.unattach = function () {
this.constructor['super'].prototype.unattach.call( this );
this.buttons.$reuse.off( 'click.mmv-stripeButtons' );
this.clearTimer( 'feedbackTooltip.show' );
$.each( this.buttons, function ( name, $button ) {
$button.tipsy( 'hide' );
} );
};
mw.mmv.ui.StripeButtons = StripeButtons;
}( mediaWiki, jQuery, OO ) );