Add viewing options panel

Enable, disable, and get confirmations.

Lots of UI work on this one.

Change-Id: I1f731992bd240e7ea403592872f7c7e270158753
This commit is contained in:
Mark Holmquist 2014-07-23 16:00:37 -07:00 committed by Gilles Dubuc
parent 82831601b8
commit 1853540cd3
22 changed files with 873 additions and 303 deletions

View file

@ -141,6 +141,7 @@ $wgResourceModules += array(
'mmv.ui.download.dialog',
'mmv.ui.metadataPanel',
'mmv.ui.reuse.dialog',
'mmv.ui.viewingOptions',
),
'messages' => array(
@ -748,12 +749,50 @@ $wgResourceModules += array(
'dependencies' => array(
'mmv.ui',
'mmv.ui.viewingOptions',
'oojs',
),
'messages' => array(
'multimediaviewer-download-link',
'multimediaviewer-reuse-link',
'multimediaviewer-options-tooltip',
),
),
'mmv.ui.viewingOptions' => $wgMediaViewerResourceTemplate + array(
'scripts' => array(
'mmv/ui/mmv.ui.viewingOptions.js',
),
'styles' => array(
'mmv/ui/mmv.ui.viewingOptions.less',
),
'dependencies' => array(
'mmv.ui',
'mmv.ui.dialog',
'oojs',
),
'messages' => array(
'multimediaviewer-options-dialog-header',
'multimediaviewer-option-header-viewer',
'multimediaviewer-option-header-filepage',
'multimediaviewer-option-desc-viewer',
'multimediaviewer-option-desc-filepage',
'multimediaviewer-option-submit-button',
'multimediaviewer-option-cancel-button',
'multimediaviewer-options-text-header',
'multimediaviewer-enable-alert',
'multimediaviewer-options-text-body',
'multimediaviewer-disable-confirmation-header',
'multimediaviewer-disable-confirmation-text',
'multimediaviewer-enable-dialog-header',
'multimediaviewer-enable-text-header',
'multimediaviewer-enable-submit-button',
'multimediaviewer-enable-confirmation-header',
'multimediaviewer-enable-confirmation-text',
),
),

View file

@ -112,5 +112,20 @@
"multimediaviewer-download-attribution-cta-header": "You need to attribute the author",
"multimediaviewer-download-attribution-cta": "Show me how",
"multimediaviewer-attr-plain": "Plain",
"multimediaviewer-attr-html": "HTML"
"multimediaviewer-attr-html": "HTML",
"multimediaviewer-options-tooltip": "Settings: Disable or enable previews",
"multimediaviewer-options-dialog-header": "Disable Media Viewer?",
"multimediaviewer-options-text-header": "Skip this viewing feature for all files.",
"multimediaviewer-options-text-body": "You can enable it later through the file details page.",
"multimediaviewer-options-learn-more": "Learn more",
"multimediaviewer-option-submit-button": "Disable Media Viewer",
"multimediaviewer-option-cancel-button": "Cancel",
"multimediaviewer-disable-confirmation-header": "You have disabled Media Viewer",
"multimediaviewer-disable-confirmation-text": "Next time you click on a thumbnail on $1, you will directly view all file details.",
"multimediaviewer-enable-dialog-header": "Enable Media Viewer?",
"multimediaviewer-enable-text-header": "Enable this media viewing feature for all files by default.",
"multimediaviewer-enable-submit-button": "Enable Media Viewer",
"multimediaviewer-enable-confirmation-header": "You have enabled Media Viewer for all files",
"multimediaviewer-enable-confirmation-text": "Next time you click on a thumbnail on $1, Media Viewer will be used.",
"multimediaviewer-enable-alert": "Media Viewer is now disabled"
}

View file

@ -118,5 +118,22 @@
"multimediaviewer-download-attribution-cta-header": "Header for telling the user that the author of an image must be attributed, during a download action.",
"multimediaviewer-download-attribution-cta": "Call to action for a user to find out how to attribute the author of an image.",
"multimediaviewer-attr-plain": "Label for a button that lets the user pick plain text as an output format.",
"multimediaviewer-attr-html": "Label for a button that lets the user pick HTML as an output format."
"multimediaviewer-attr-html": "Label for a button that lets the user pick HTML as an output format.",
"multimediaviewer-options-dialog-header": "Header for a dialog that gives the user the option to disable the media viewer.",
"multimediaviewer-option-header-filepage": "Header for the viewing option that gives the user the file page when they click on image thumbnails.",
"multimediaviewer-option-desc-filepage": "Longer form description for the viewing option that gives the user the file page when they click on image thumbnails.",
"multimediaviewer-option-submit-button": "Button for submitting a preference change that modifies the default behaviour for image clicks - disables the viewer.",
"multimediaviewer-option-cancel-button": "Button for canceling an action on a preference change that modifies the default behaviour for image clicks - closes the dialog with no action.",
"multimediaviewer-options-tooltip": "Tooltip for a button that opens a panel for enabling or disabling the media viewer.",
"multimediaviewer-options-text-header": "Text explaining the changes that happen when a user disables media viewer.",
"multimediaviewer-options-text-body": "Text explaining how to re-enable the media viewer after disabling.",
"multimediaviewer-options-learn-more": "Text for a link to more information about the media viewer.",
"multimediaviewer-disable-confirmation-header": "Header on a dialog that informs the user they've successfully disabled the media viewer.",
"multimediaviewer-disable-confirmation-text": "Text informing the user that they've successfully disabled the media viewer. $1 is the wiki's name.",
"multimediaviewer-enable-dialog-header": "Header for a dialog that allows users to re-enable media viewer.",
"multimediaviewer-enable-text-header": "Text for a dialog that allows users to re-enable media viewer.",
"multimediaviewer-enable-submit-button": "Text for a button that re-enables media viewer.",
"multimediaviewer-enable-confirmation-header": "Header for a dialog that confirms that the user successfully re-enabled the media viewer.",
"multimediaviewer-enable-confirmation-text": "Text confirming that the user successfully re-enabled the media viewer. $1 is the wiki's name.",
"multimediaviewer-enable-alert": "Text shown in the enable panel to alert the user that the media viewer is currently disabled."
}

View file

@ -170,7 +170,8 @@
// which in turn will cause the options API to delete the row and revert the pref to default
newPrefValue = enabled ? '1' : undefined;
}
return this.setUserPreference( 'multimediaviewer-enable', newPrefValue ).then( function () {
return this.setUserPreference( 'multimediaviewer-enable', newPrefValue ).done( function () {
config.mwConfig.set( 'wgMediaViewerOnClick', enabled );
} );
}

View file

@ -217,6 +217,7 @@
link = $thumb.closest( 'a' ).prop( 'href' );
$( '<p>' )
.addClass( 'mw-mmv-filepage-menu' )
.append(
$link = $( '<a>' )
// It won't matter because we catch the click event anyway, but

View file

@ -16,3 +16,8 @@
// Arrow size for dialogs
@arrow-size: 20px;
@arrow-border-size: 2px;
// Some button things that get included all over
@navbutton-width: 18px;
@buttons-offset-right: 5px;
@buttons-offset-each-top: 37px;

View file

@ -102,6 +102,7 @@
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 );
};
/**
@ -209,6 +210,7 @@
this.fileReuse.attach();
this.downloadDialog.attach();
this.optionsDialog.attach();
// Reset the cursor fading
this.fadeStopped();
@ -238,6 +240,9 @@
this.downloadDialog.unattach();
this.downloadDialog.closeDialog();
this.optionsDialog.unattach();
this.optionsDialog.closeDialog();
this.clearEvents();
// We trigger this event on the document because unattach() can run

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" xml:space="preserve"><path style="fill:#ffffff;stroke:none" d="M96,56.8V39.2l-10.8-1.8c-0.8-2.9-2-5.7-3.4-8.2l6.3-8.9L75.7,7.8l-8.9,6.3c-2.6-1.4-5.3-2.6-8.2-3.4L56.8,0H39.2l-1.8,10.8c-2.9,0.8-5.7,2-8.2,3.4l-8.9-6.3L7.8,20.3l6.3,8.9c-1.4,2.6-2.6,5.3-3.4,8.3L0,39.2v17.6l10.7,1.8c0.8,2.9,2,5.7,3.4,8.3l-6.3,8.9l12.4,12.4l8.9-6.3c2.6,1.4,5.3,2.6,8.3,3.4L39.2,96h17.6l1.8-10.8c2.9-0.8,5.7-2,8.2-3.4l8.9,6.3l12.4-12.4l-6.3-8.9c1.4-2.6,2.6-5.3,3.4-8.3L96,56.8z M48,66.4c-10.2,0-18.4-8.3-18.4-18.4c0-10.2,8.3-18.4,18.4-18.4S66.4,37.8,66.4,48C66.4,58.2,58.2,66.4,48,66.4z"/></svg>

After

Width:  |  Height:  |  Size: 788 B

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 58.4 52.3" enable-background="new 0 0 58.4 52.3" xml:space="preserve">
<g>
<g>
<path fill="#347BFF" d="M0,0v44.7h58.4V0H0z M51.9,35.4H6l13.7-17.7l2-0.8l12.9,13.7l6.8-4L51.9,35.4L51.9,35.4L51.9,35.4z"/>
<polygon fill="#FFFFFF" points="48,4.2 49.9,6.1 44.8,11.2 43.5,9.9 43.2,15.2 48.4,14.9 47.1,13.6 52.3,8.5 54.1,10.4 54.5,3.8
"/>
</g>
<rect x="0" y="44.7" fill="#E6E6E6" width="58.4" height="7.7"/>
<rect x="1.9" y="46.2" fill="#347BFF" width="13.8" height="1.6"/>
<rect x="1.9" y="49.3" fill="#347BFF" width="54.7" height="1.6"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 949 B

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 58.4 52.3" enable-background="new 0 0 58.4 52.3" xml:space="preserve">
<g>
<rect x="0" y="0" fill="#E6E6E6" width="58.4" height="52.3"/>
<g>
<rect x="14.9" y="28.2" fill="#347BFF" width="13.8" height="1.6"/>
<rect x="14.9" y="31.3" fill="#347BFF" width="38.8" height="1.6"/>
<rect x="14.9" y="34.3" fill="#347BFF" width="38.8" height="1.6"/>
<rect x="14.9" y="39.5" fill="#347BFF" width="13.8" height="1.6"/>
<rect x="14.9" y="42.5" fill="#347BFF" width="38.8" height="1.6"/>
<rect x="14.9" y="45.6" fill="#347BFF" width="38.8" height="1.6"/>
</g>
<path fill="#347BFF" d="M14.4,7.8v17.4h22.7V7.8H14.4z M34.6,21.6H16.7l5.3-6.9l0.8-0.3l5,5.3l2.7-1.6L34.6,21.6L34.6,21.6
L34.6,21.6z"/>
<rect x="0" y="10.8" fill="#CCCCCC" width="9.8" height="41.6"/>
<circle fill="#CCCCCC" cx="5.4" cy="4.8" r="3.7"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 612 792" enable-background="new 0 0 612 792" xml:space="preserve" inkscape:version="0.48.2 r9819" width="100%" height="100%" sodipodi:docname="x_darkgreen.svg"><metadata id="metadata9"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata><defs id="defs7"/><sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="721" inkscape:window-height="480" id="namedview5" showgrid="false" inkscape:zoom="0.2979798" inkscape:cx="306" inkscape:cy="364.11864" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="0" inkscape:current-layer="Layer_1"/>
<polygon fill="#999999" points="612,179.2 522.8,90 306,306.8 89.2,90 0,179.2 216.8,396 0,612.8 89.2,702 306,485.2 522.8,702 612,612.8 395.2,396 " id="polygon3" style="fill:#0a604c;fill-opacity:1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -174,9 +174,15 @@
case 'mmv-reuse-closed':
this.reuseOpen = false;
break;
case 'mmv-options-opened':
this.optionsOpen = true;
break;
case 'mmv-options-closed':
this.optionsOpen = false;
break;
}
this.dialogOpen = this.reuseOpen || this.downloadOpen;
this.dialogOpen = this.reuseOpen || this.downloadOpen || this.optionsOpen;
this.$image.toggleClass( 'mw-mmv-dialog-is-open', this.dialogOpen );
};
@ -190,6 +196,8 @@
this.handleEvent( 'mmv-reuse-closed', $.proxy( this.handleDialogEvent, this ) );
this.handleEvent( 'mmv-download-opened', $.proxy( this.handleDialogEvent, this ) );
this.handleEvent( 'mmv-download-closed', $.proxy( this.handleDialogEvent, this ) );
this.handleEvent( 'mmv-options-opened', $.proxy( this.handleDialogEvent, this ) );
this.handleEvent( 'mmv-options-closed', $.proxy( this.handleDialogEvent, this ) );
this.$image
.on( 'click.mmv-view-original', function () {

View file

@ -47,6 +47,11 @@
gravity: isRtl ? 'sw' : 'se'
} );
this.$options = $( '<div>' )
.text( ' ' )
.prop( 'title', mw.message( 'multimediaviewer-options-tooltip' ) )
.addClass( 'mw-mmv-options-button' );
this.$download = $( '<div>' )
.addClass( 'mw-mmv-download-button' )
.html( '&nbsp;' )
@ -71,6 +76,7 @@
.add( this.$download )
.add( this.$reuse )
.add( this.$fullscreen )
.add( this.$options )
.add( this.$next )
.add( this.$prev );
@ -200,7 +206,7 @@
var buttons = this;
this.$reuse.on( 'click.mmv-canvasButtons', function ( e ) {
$( document ).trigger( 'mmv-reuse-open' );
$( document ).trigger( 'mmv-reuse-open', e );
e.stopPropagation(); // the dialog would take it as an outside click and close
} );
this.handleEvent( 'mmv-reuse-opened', function () {
@ -211,7 +217,7 @@
} );
this.$download.on( 'click.mmv-canvasButtons', function ( e ) {
$( document ).trigger( 'mmv-download-open' );
$( document ).trigger( 'mmv-download-open', e );
e.stopPropagation();
} );
this.handleEvent( 'mmv-download-opened', function () {
@ -220,6 +226,17 @@
this.handleEvent( 'mmv-download-closed', function () {
buttons.$download.removeClass( 'open' );
} );
this.$options.on( 'click.mmv-canvasButtons', function ( e ) {
$( document ).trigger( 'mmv-options-open', e );
e.stopPropagation();
} );
this.handleEvent( 'mmv-options-opened', function () {
buttons.$options.addClass( 'open' );
} );
this.handleEvent( 'mmv-options-closed', function () {
buttons.$options.removeClass( 'open' );
} );
};
/**

View file

@ -1,11 +1,9 @@
@import "../mmv.globals";
@import "../mmv.mixins";
@navbutton-width: 18px;
@buttons-offset: 5px;
.mw-mmv-download-button,
.mw-mmv-reuse-button,
.mw-mmv-options-button,
.mw-mmv-close,
.mw-mmv-fullscreen,
.mw-mmv-next-image,
@ -27,6 +25,7 @@
.mw-mmv-download-button.hidden,
.mw-mmv-reuse-button.hidden,
.mw-mmv-options-button.hidden,
.mw-mmv-close.hidden,
.mw-mmv-fullscreen.hidden,
.mw-mmv-next-image.hidden,
@ -47,9 +46,10 @@
.mw-mmv-download-button,
.mw-mmv-reuse-button,
.mw-mmv-options-button,
.mw-mmv-close,
.mw-mmv-fullscreen {
right: @buttons-offset;
right: @buttons-offset-right;
left: auto;
transition: opacity 0.25s;
background-position: center;
@ -71,7 +71,7 @@
}
.mw-mmv-close {
top: @buttons-offset;
top: @buttons-offset-right;
/* @embed */
background-image: url(img/mw-close.svg);
height: 23px;
@ -79,13 +79,21 @@
}
.mw-mmv-fullscreen {
top: (@buttons-offset + 37px);
top: (@buttons-offset-right + (@buttons-offset-each-top));
/* @embed */
background-image: url(img/mw-fullscreen-ltr.svg);
width: 21px;
height: 22px;
}
.mw-mmv-options-button {
top: (@buttons-offset-right + (2 * @buttons-offset-each-top));
/* @embed */
background-image: url(img/gear.svg);
height: 23px;
width: 23px;
}
.jq-fullscreened {
.mw-mmv-fullscreen {
/* @embed */
@ -116,8 +124,8 @@
}
.mw-mmv-reuse-button {
right: @buttons-offset - 2px;
bottom: @buttons-offset + @metadatabar-above-fold-height + @progress-bar-height;
right: @buttons-offset-right - 2px;
bottom: @buttons-offset-right + @metadatabar-above-fold-height + @progress-bar-height;
/* @embed */
background-image: url(img/use-ltr.svg);
height: 24px;
@ -125,7 +133,7 @@
}
.mw-mmv-download-button {
bottom: @buttons-offset + @metadatabar-above-fold-height + @progress-bar-height + 37px;
bottom: @buttons-offset-right + @metadatabar-above-fold-height + @progress-bar-height + 37px;
/* @embed */
background-image: url(img/mw-download.svg);
height: 24px;

View file

@ -63,13 +63,15 @@
/**
* Handles click on link that opens/closes the dialog.
* @param {Event} openEvent Event object for the mmv-$dialog-open event.
* @param {Event} e Event object for the click event.
*/
DP.handleOpenCloseClick = function () {
DP.handleOpenCloseClick = function ( openEvent, e ) {
var dialog = this;
mw.loader.using( this.loadDependencies, function () {
dialog.dependenciesLoaded = true;
dialog.toggleDialog();
dialog.toggleDialog( e );
}, function (error) {
if ( window.console && window.console.error ) {
window.console.error( 'mw.loader.using error when trying to load dialog dependencies', error );
@ -81,10 +83,12 @@
/**
* Toggles the open state on the dialog.
* @param {Event} [e] Event object when the close action is caused by a user
* action, as opposed to closing the window or something.
*/
DP.toggleDialog = function () {
DP.toggleDialog = function ( e ) {
if ( this.isOpen ) {
this.closeDialog();
this.closeDialog( e );
} else {
this.openDialog();
}

View file

@ -47,6 +47,7 @@
this.handleEvent( 'mmv-download-open', $.proxy( this.handleOpenCloseClick, this ) );
this.handleEvent( 'mmv-reuse-open', $.proxy( this.closeDialog, this ) );
this.handleEvent( 'mmv-options-open', $.proxy( this.closeDialog, this ) );
};
/**

View file

@ -43,7 +43,6 @@
this.initializeHeader( localStorage );
this.initializeImageMetadata();
this.initializeAboutLinks();
this.initializePreferenceLinks();
}
oo.inheritClass( MetadataPanel, mw.mmv.ui.Element );
MPP = MetadataPanel.prototype;
@ -78,11 +77,6 @@
MPP.unattach = function() {
this.$title.tipsy( 'hide' );
// This is created conditionally if MMV is in beta
if ( this.$mmvOptOutLink ) {
this.$mmvOptOutLink.tipsy( 'hide' );
}
this.$authorAndSource.tipsy( 'hide' );
this.scroller.unattach();
@ -367,88 +361,6 @@
.appendTo( this.$imageMetadata );
};
/**
* @private
* Set text and appearance of the optin/optout link.
* This is a helper function for #initializePreferenceLinks().
* @param {jQuery} $link the link that needs to be changed
* @param {boolean} status true if the user is currently opted in
* @param {boolean} pending true if the link has been clicked already (there is a request pending)
*/
MPP.setOptInOutLink = function ( $link, status, pending ) {
var messageKey = ( status ? 'optout' : 'optin' ) + ( pending ? '-pending' : '' );
$link
.toggleClass( 'pending', !!pending )
.text( mw.message( 'multimediaviewer-' + messageKey + '-mmv' ).text() )
.attr( 'title', pending ? '' : mw.message( 'multimediaviewer-' + messageKey + '-help' ).text() )
.attr( 'original-title', pending ? '' : mw.message( 'multimediaviewer-' + messageKey + '-help' ).text() );
};
/**
* Initialize the link for enabling/disabling MediaViewer.
*/
MPP.initializePreferenceLinks = function () {
var target,
panel = this,
separator = ' | ',
optInStatus = this.config.isMediaViewerEnabledOnClick();
if ( !mw.config.get( 'wgMediaViewerIsInBeta' ) && this.config.canSetMediaViewerEnabledOnClick() ) {
this.$mmvOptOutLink = $( '<a>' )
.prop( 'href', '#' )
.addClass( 'mw-mmv-optout-link' )
.tipsy( { gravity: 's' } )
.click( function ( e ) {
var changePreferencePromise,
newOptInStatus = !panel.config.isMediaViewerEnabledOnClick();
e.preventDefault();
if ( $( this).is( '.pending' ) ) {
return false;
}
changePreferencePromise = panel.config.setMediaViewerEnabledOnClick( newOptInStatus );
if ( changePreferencePromise.state() === 'pending' ) {
// use ! for status param because we want to show text for old state
// (e.g. enabled -> "Disabling...") while pending
panel.setOptInOutLink( panel.$mmvOptOutLink, !newOptInStatus, true );
}
changePreferencePromise.done( function () {
panel.setOptInOutLink( panel.$mmvOptOutLink, newOptInStatus );
panel.$mmvOptOutLink.tipsy( 'hide' );
mw.mmv.actionLogger.log( 'opt' + ( newOptInStatus ? 'in' : 'out' )
+ '-' + ( mw.user.isAnon() ? 'anon' : 'loggedin' ) );
} ).fail( function () {
mw.notify( 'Error while trying to change preference' );
} );
} );
this.setOptInOutLink( this.$mmvOptOutLink, optInStatus );
this.$mmvAboutLinks.append(
separator,
this.$mmvOptOutLink
);
}
if ( !mw.user.isAnon() && mw.config.get( 'wgMediaViewerIsInBeta' ) ) {
target = mw.Title.newFromText( 'Special:Preferences' ).getUrl();
target += '#mw-prefsection-betafeatures';
this.$mmvPreferenceLink = $( '<a>' )
.prop( 'href', target )
.text( mw.message( 'mypreferences' ) )
.addClass( 'mw-mmv-preference-link' );
this.$mmvAboutLinks.append(
separator,
this.$mmvPreferenceLink
);
}
};
// *********************************
// ******** Setting methods ********
// *********************************

View file

@ -144,6 +144,7 @@
this.handleEvent( 'mmv-reuse-open', $.proxy( this.handleOpenCloseClick, this ) );
this.handleEvent( 'mmv-download-open', $.proxy( this.closeDialog, this ) );
this.handleEvent( 'mmv-options-open', $.proxy( this.closeDialog, this ) );
this.attachDependencies();
};

View file

@ -0,0 +1,360 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
( function ( mw, $, oo ) {
// Shortcut for prototype later
var ODP;
/**
* Represents the viewing options dialog and the link to open it.
* @class mw.mmv.ui.OptionsDialog
* @extends mw.mmv.ui.Dialog
* @param {jQuery} $container the element to which the dialog will be appended
* @param {jQuery} $openButton the button which opens the dialog. Only used for positioning.
* @param {mw.mmv.Config} config
*/
function OptionsDialog( $container, $openButton, config ) {
mw.mmv.ui.Dialog.call( this, $container, $openButton, config );
this.$dialog.addClass( 'mw-mmv-options-dialog' );
this.eventPrefix = 'options';
this.initPanel();
}
oo.inheritClass( OptionsDialog, mw.mmv.ui.Dialog );
ODP = OptionsDialog.prototype;
ODP.attach = function () {
this.handleEvent( 'mmv-options-open', $.proxy( this.handleOpenCloseClick, this ) );
this.handleEvent( 'mmv-reuse-open', $.proxy( this.closeDialog, this ) );
this.handleEvent( 'mmv-download-open', $.proxy( this.closeDialog, this ) );
};
/**
* Initialises UI elements.
*/
ODP.initPanel = function () {
this.initEnableConfirmation();
this.initDisableConfirmation();
this.initEnableDiv();
this.initDisableDiv();
};
/**
* Initialises the enable confirmation pane.
*/
ODP.initEnableConfirmation = function () {
this.createConfirmationPane(
'mw-mmv-enable-confirmation',
'$enableConfirmation',
[
mw.message( 'multimediaviewer-enable-confirmation-header' ).text(),
mw.message( 'multimediaviewer-enable-confirmation-text', mw.config.get( 'wgSiteName' ) ).text()
] );
};
/**
* Initialises the disable confirmation pane.
*/
ODP.initDisableConfirmation = function () {
this.createConfirmationPane(
'mw-mmv-disable-confirmation',
'$disableConfirmation',
[
mw.message( 'multimediaviewer-disable-confirmation-header' ).text(),
mw.message( 'multimediaviewer-disable-confirmation-text', mw.config.get( 'wgSiteName' ) ).text()
] );
};
/**
* Initialises the enable action pane.
*/
ODP.initEnableDiv = function () {
this.createActionPane(
'mw-mmv-options-enable',
'$enableDiv',
mw.message( 'multimediaviewer-enable-submit-button' ).text(),
[
mw.message( 'multimediaviewer-enable-dialog-header' ).text(),
mw.message( 'multimediaviewer-enable-text-header' ).text()
], true );
};
/**
* Initialises the disable action pane.
*/
ODP.initDisableDiv = function () {
this.createActionPane(
'mw-mmv-options-disable',
'$disableDiv',
mw.message( 'multimediaviewer-option-submit-button' ).text(),
[
mw.message( 'multimediaviewer-options-dialog-header' ).text(),
mw.message( 'multimediaviewer-options-text-header' ).text(),
mw.message( 'multimediaviewer-options-text-body' ).text()
], false );
};
/**
* Hides all of the divs.
*/
ODP.hideDivs = function () {
this.$dialog.removeClass( 'mw-mmv-disable-confirmation-shown mw-mmv-enable-confirmation-shown mw-mmv-enable-div-shown' );
this.$disableDiv
.add( this.$disableConfirmation )
.add( this.$enableDiv )
.add( this.$enableConfirmation )
.removeClass( 'mw-mmv-shown' );
};
/**
* Shows the confirmation div for the disable action.
*/
ODP.showDisableConfirmation = function () {
this.hideDivs();
this.$disableConfirmation.addClass( 'mw-mmv-shown' );
this.$dialog.addClass( 'mw-mmv-disable-confirmation-shown' );
};
/**
* Shows the confirmation div for the enable action.
*/
ODP.showEnableConfirmation = function () {
this.hideDivs();
this.$enableConfirmation.addClass( 'mw-mmv-shown' );
this.$dialog.addClass( 'mw-mmv-enable-confirmation-shown' );
};
/**
* @event mmv-options-opened
* Fired when the dialog is opened.
*/
/**
* Opens a dialog with information about file reuse.
*/
ODP.openDialog = function () {
if ( this.isEnabled() ) {
this.$disableDiv.addClass( 'mw-mmv-shown' );
} else {
this.$enableDiv.addClass( 'mw-mmv-shown' );
this.$dialog.addClass( 'mw-mmv-enable-div-shown' );
}
mw.mmv.ui.Dialog.prototype.openDialog.call( this );
$( document ).trigger( 'mmv-options-opened' );
};
/**
* @event mmv-options-closed
* Fired when the dialog is closed.
*/
/**
* Closes the options dialog.
* @param {Event} [e] Event object when the close action is caused by a user
* action, as opposed to closing the window or something.
*/
ODP.closeDialog = function ( e ) {
var wasConfirmation = this.$dialog.is( '.mw-mmv-disable-confirmation-shown' ) || this.$dialog.is( '.mw-mmv-enable-confirmation-shown' );
mw.mmv.ui.Dialog.prototype.closeDialog.call( this );
$( document ).trigger( 'mmv-options-closed' );
this.hideDivs();
if ( e && $( e.target ).is( '.mw-mmv-options-button' ) && wasConfirmation ) {
this.openDialog();
}
};
/**
* Creates a confirmation pane.
*/
ODP.createConfirmationPane = function ( divClass, propName, msgs ) {
var dialog = this,
$div = $( '<div>' )
.addClass( divClass )
.appendTo( this.$dialog );
$( '<div>' )
.html( '&nbsp;' )
.addClass( 'mw-mmv-confirmation-close' )
.click( function () {
dialog.closeDialog();
} )
.appendTo( $div );
this.addText( $div, msgs );
this[propName] = $div;
};
/**
* Creates an action pane.
* @param {string} divClass Class applied to main div.
* @param {string} propName Name of the property on this object to which we'll assign the div.
* @param {string} smsg Message for the submit button.
* @param {string} msgs See #addText
* @param {boolean} enabled Whether this dialog is an enable one.
*/
ODP.createActionPane = function ( divClass, propName, smsg, msgs, enabled ) {
var $div = $( '<div>' )
.addClass( divClass )
.appendTo( this.$dialog );
if ( enabled ) {
$( '<div>' )
.addClass( 'mw-mmv-options-enable-alert' )
.text( mw.message( 'multimediaviewer-enable-alert' ).text() )
.appendTo( $div );
}
$( '<div>' )
.html( '&nbsp;' )
.addClass( 'mw-mmv-options-icon' )
.appendTo( $div );
this.addText( $div, msgs );
this.makeButtons( $div, smsg, enabled );
this[propName] = $div;
};
/**
* Creates buttons for the dialog.
* @param {jQuery} $container
* @param {string} smsg Message for the submit button.
* @param {boolean} enabled Whether the viewer is enabled after this dialog is submitted.
*/
ODP.makeButtons = function ( $container, smsg, enabled ) {
var $submitDiv = $( '<div>' )
.addClass( 'mw-mmv-options-submit' )
.appendTo( $container );
this.makeSubmitButton(
$submitDiv,
smsg,
enabled
);
this.makeCancelButton( $submitDiv );
};
/**
* Makes a submit button for one of the panels.
* @param {jQuery} $submitDiv The div for the buttons in the dialog.
* @param {string} msg The string to put in the button.
* @param {boolean} enabled Whether to turn the viewer on or off when this button is pressed.
*/
ODP.makeSubmitButton = function ( $submitDiv, msg, enabled ) {
var dialog = this;
return $( '<button>' )
.addClass( 'mw-mmv-options-submit-button mw-ui-button mw-ui-progressive' )
.text( msg )
.appendTo( $submitDiv )
.click( function () {
var $buttons = $( this ).closest( '.mw-mmv-options-submit' ).find( '.mw-mmv-options-submit-button, .mw-mmv-options-cancel-button' );
$buttons.prop( 'disabled', true );
dialog.config.setMediaViewerEnabledOnClick( enabled ).done( function () {
mw.mmv.actionLogger.log( 'opt' + ( enabled ? 'in' : 'out' ) + '-' + ( mw.user.isAnon() ? 'anon' : 'loggedin' ) );
if ( enabled ) {
dialog.showEnableConfirmation();
} else {
dialog.showDisableConfirmation();
}
} )
.always( function() {
$buttons.prop( 'disabled', false );
} );
return false;
} );
};
/**
* Makes a cancel button for one of the panels.
* @param {jQuery} $submitDiv The div for the buttons in the dialog.
*/
ODP.makeCancelButton = function ( $submitDiv ) {
var dialog = this;
return $( '<button>' )
.addClass( 'mw-mmv-options-cancel-button mw-ui-button mw-ui-quiet' )
.text( mw.message( 'multimediaviewer-option-cancel-button' ).text() )
.appendTo( $submitDiv )
.click( function () {
dialog.closeDialog();
return false;
} );
};
/**
* Adds text to a dialog.
* @param {jQuery} $container
* @param {string[]} msgs The messages to be added.
*/
ODP.addText = function ( $container, msgs ) {
var i, $text,
adders = [
function ( msg ) {
$( '<h3>' )
.text( msg )
.addClass( 'mw-mmv-options-dialog-header' )
.appendTo( $container );
},
function ( msg ) {
$( '<p>' )
.text( msg )
.addClass( 'mw-mmv-options-text-header' )
.appendTo( $text );
},
function ( msg ) {
$( '<p>' )
.text( msg )
.addClass( 'mw-mmv-options-text-body' )
.appendTo( $text );
}
];
$text = $( '<div>' )
.addClass( 'mw-mmv-options-text' )
.appendTo( $container );
for ( i = 0; i < msgs.length && i < adders.length; i++ ) {
adders[i]( msgs[i] );
}
};
/**
* Checks the preference.
*/
ODP.isEnabled = function () {
return this.config.isMediaViewerEnabledOnClick();
};
mw.mmv.ui.OptionsDialog = OptionsDialog;
}( mediaWiki, jQuery, OO ) );

View file

@ -0,0 +1,209 @@
@import "../mmv.globals";
@import "../mmv.mixins";
@shadow-color: #aaaaaa;
@divider-border-height: 1px;
.mw-mmv-options-dialog {
@offset-top: (@buttons-offset-right + (2 * @buttons-offset-each-top) + 6px);
top: @offset-top;
height: auto;
z-index: 1004;
padding: 10px;
&.mw-mmv-enable-confirmation-shown {
background-color: #00AF89;
box-shadow: 0 2px 0 #00634E;
.mw-mmv-dialog-down-arrow {
background-color: #00AF89;
}
}
&.mw-mmv-disable-confirmation-shown {
background-color: #EEE;
.mw-mmv-dialog-down-arrow {
background-color: #eee;
}
}
&.mw-mmv-enable-div-shown {
.mw-mmv-dialog-down-arrow {
background-color: #EEE;
}
}
.mw-mmv-dialog-down-arrow {
@arrow-size: 20px;
top: (@offset-top + (@arrow-size / 2 ));
}
.mw-mmv-options-enable,
.mw-mmv-options-disable {
height: 150px;
}
.mw-mmv-enable-confirmation,
.mw-mmv-disable-confirmation {
height: 100px;
}
.mw-mmv-enable-confirmation,
.mw-mmv-disable-confirmation,
.mw-mmv-options-enable,
.mw-mmv-options-disable {
position: relative;
display: none;
&.mw-mmv-shown {
display: block;
}
}
.mw-mmv-confirmation-close {
display: inline-block;
position: absolute;
right: 0;
top: 0;
width: 20px;
height: 20px;
cursor: pointer;
opacity: 0.75;
/* @embed */
background-image: url(img/x_gray.svg);
&:hover {
opacity: 1;
}
}
.mw-mmv-disable-confirmation {
.mw-mmv-options-dialog-header {
color: #333;
padding: 0;
}
.mw-mmv-options-text-header {
color: #555;
}
}
.mw-mmv-enable-confirmation {
.mw-mmv-options-dialog-header {
color: #FFF;
padding: 0;
}
.mw-mmv-options-text-header {
color: #FFF;
}
.mw-mmv-confirmation-close {
/* @embed */
background-image: url(img/x_darkgreen.svg);
background-size: 20px 20px;
background-repeat: no-repeat;
}
}
.mw-mmv-disable-confirmation,
.mw-mmv-enable-confirmation {
padding: 0;
.mw-mmv-options-text {
left: 0;
}
}
}
.mw-mmv-options-text,
.mw-mmv-options-icon {
position: absolute;
top: 35px;
.mw-mmv-options-enable & {
top: 70px;
}
}
.mw-mmv-options-submit {
position: absolute;
bottom: 0;
right: 0;
}
.mw-mmv-options-text {
left: 68px;
right: 0;
}
.mw-mmv-options-icon {
left: 0;
width: 58px;
height: 52px;
/* @embed */
background-image: url(img/icon_mmv.svg);
}
.mw-mmv-options-cancel-button,
.mw-mmv-options-submit-button {
float: right;
}
.mw-mmv-filepage-menu .mw-mmv-options-button {
display: inline-block;
margin-left: 10px;
width: 23px;
height: 23px;
cursor: pointer;
border: 1px solid #dddddd;
/* @embed */
background-image: url('img/gear.svg');
padding: 5px;
}
.mw-mmv-options-dialog-header {
padding-top: 0;
font-weight: normal;
font-size: 1.25em;
color: #333;
position: absolute;
top: 0;
.mw-mmv-options-enable & {
top: 35px;
}
}
.mw-mmv-options-text-header {
margin: 0;
font-size: 1em;
color: #555;
}
.mw-mmv-options-text-body {
font-size: .75em;
color: #555;
}
.mw-mmv-options-enable-alert {
position: absolute;
left: -10px;
right: -10px;
top: -10px;
padding: 10px;
background-color: #EEE;
color: #555;
font-weight: 500;
}

View file

@ -202,7 +202,7 @@
restoreScrollTo();
} );
QUnit.test( 'isAnyActiveButtonHovered', 30, function ( assert ) {
QUnit.test( 'isAnyActiveButtonHovered', 35, function ( assert ) {
var lightbox = new mw.mmv.LightboxInterface();
stubScrollTo();

View file

@ -188,7 +188,7 @@
} );
} );
QUnit.test( 'About links', 9, function ( assert ) {
QUnit.test( 'About links', 3, function ( assert ) {
var panel,
$qf = $( '#qunit-fixture'),
oldWgMediaViewerIsInBeta = mw.config.get( 'wgMediaViewerIsInBeta' );
@ -200,84 +200,6 @@
assert.strictEqual( $qf.find( '.mw-mmv-about-link' ).length, 1, 'About link is created.' );
assert.strictEqual( $qf.find( '.mw-mmv-discuss-link' ).length, 1, 'Discuss link is created.' );
assert.strictEqual( $qf.find( '.mw-mmv-help-link' ).length, 1, 'Help link is created.' );
assert.strictEqual( $qf.find( '.mw-mmv-optout-link' ).length, 1, 'Opt-out link is created.' );
assert.strictEqual( $qf.find( '.mw-mmv-preference-link' ).length, 0, 'Preferences link is not created when not in beta.' );
mw.config.set( 'wgMediaViewerIsInBeta', true );
mw.user.isAnon.returns( false );
panel = new mw.mmv.ui.MetadataPanel( $qf.empty(), $( '<div>' ).appendTo( $qf ), window.localStorage, new mw.mmv.Config( {}, mw.config, mw.user, new mw.Api(), window.localStorage ) );
assert.strictEqual( $qf.find( '.mw-mmv-optout-link' ).length, 0, 'Opt-out link is not created when in beta.' );
assert.strictEqual( $qf.find( '.mw-mmv-preference-link' ).length, 1, 'Preferences link is created for logged-in user.' );
mw.user.isAnon.returns( true );
panel = new mw.mmv.ui.MetadataPanel( $qf.empty(), $( '<div>' ).appendTo( $qf ), window.localStorage, new mw.mmv.Config( {}, mw.config, mw.user, new mw.Api(), window.localStorage ) );
assert.strictEqual( $qf.find( '.mw-mmv-optout-link' ).length, 0, 'Opt-out link is not created when in beta.' );
assert.strictEqual( $qf.find( '.mw-mmv-preference-link' ).length, 0, 'Preferences link is not created for anon user.' );
mw.config.set( 'wgMediaViewerIsInBeta', oldWgMediaViewerIsInBeta );
} );
QUnit.test( 'About links', 12, function ( assert ) {
var panel,
deferred,
$qf = $( '#qunit-fixture' ),
config = {
isMediaViewerEnabledOnClick: this.sandbox.stub(),
setMediaViewerEnabledOnClick: this.sandbox.stub(),
canSetMediaViewerEnabledOnClick: this.sandbox.stub()
},
oldWgMediaViewerIsInBeta = mw.config.get( 'wgMediaViewerIsInBeta' );
this.sandbox.stub( mw.user, 'isAnon' );
this.sandbox.stub( mw.mmv.actionLogger, 'log' );
this.sandbox.stub( $.fn, 'tipsy' ).returnsThis(); // interferes with the fake clock in other tests
mw.config.set( 'wgMediaViewerIsInBeta', false );
panel = new mw.mmv.ui.MetadataPanel( $qf, $( '<div>' ).appendTo( $qf ), window.localStorage, new mw.mmv.Config( {}, mw.config, mw.user, new mw.Api(), window.localStorage ) );
panel.config = config;
// FIXME should not do work in the constructor
panel.$mmvOptOutLink.remove();
panel.$mmvOptOutLink = undefined;
config.canSetMediaViewerEnabledOnClick.returns( false );
panel.initializePreferenceLinks();
assert.strictEqual( $qf.find( '.mw-mmv-optout-link' ).length, 0, 'Optout link is hidden when option cannot be set' );
config.canSetMediaViewerEnabledOnClick.returns( true );
config.isMediaViewerEnabledOnClick.returns( true );
panel.initializePreferenceLinks();
assert.ok( $qf.find( '.mw-mmv-optout-link' ).text().match( /disable/i ), 'Optout link is visible when MediaViewer can be disabled' );
deferred = $.Deferred();
config.setMediaViewerEnabledOnClick.returns( deferred );
mw.user.isAnon.returns( true );
$qf.find( '.mw-mmv-optout-link' ).click();
assert.ok( config.setMediaViewerEnabledOnClick.calledWith( false ), 'When MediaViewer is active, it is disabled on click' );
assert.ok( $qf.find( '.mw-mmv-optout-link' ).is( '.pending' ), 'Pending class is set while disabling in progress' );
config.setMediaViewerEnabledOnClick.reset();
$qf.find( '.mw-mmv-optout-link' ).click();
assert.ok( !config.setMediaViewerEnabledOnClick.called, 'click has no effect when another request is pending' );
deferred.resolve();
assert.ok( !$qf.find( '.mw-mmv-optout-link' ).is( '.pending' ), 'Pending class removed after change has finished' );
assert.ok( mw.mmv.actionLogger.log.called, 'The optout action is logged' );
assert.strictEqual( mw.mmv.actionLogger.log.firstCall.args[0], 'optout-anon' , 'The correct event is logged' );
config.isMediaViewerEnabledOnClick.returns( false );
mw.user.isAnon.returns( false );
panel.initializePreferenceLinks();
assert.ok( $qf.find( '.mw-mmv-optout-link' ).text().match( /enable/i ), 'Optin link is visible when MediaViewer can be enabled' );
mw.mmv.actionLogger.log.reset();
config.setMediaViewerEnabledOnClick.reset();
config.setMediaViewerEnabledOnClick.returns( deferred );
$qf.find( '.mw-mmv-optout-link' ).click();
assert.ok( config.setMediaViewerEnabledOnClick.calledWith( true ), 'When MediaViewer is inactive, it is enabled on click' );
assert.ok( mw.mmv.actionLogger.log.called, 'The optin action is logged' );
assert.ok( mw.mmv.actionLogger.log.firstCall.args[0], 'optin-loggedin', 'The correct event is logged' );
mw.config.set( 'wgMediaViewerIsInBeta', oldWgMediaViewerIsInBeta );
} );
}( mediaWiki, jQuery ) );