/*
* 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
' ) .append( this.embedTextHtml.$element, this.embedTextWikitext.$element ) .appendTo( $container ); }; /** * Creates snippet selection buttons. * * @param {jQuery} $container */ EP.createSnippetSelectionButtons = function( $container ) { var wikitextButtonOption, htmlButtonOption; this.embedSwitch = new oo.ui.ButtonSelectWidget( { classes: [ 'mw-mmv-embed-select' ] } ); wikitextButtonOption = new oo.ui.ButtonOptionWidget( 'wikitext', { label: mw.message( 'multimediaviewer-embed-wt' ).text() } ); htmlButtonOption = new oo.ui.ButtonOptionWidget( 'html', { label: mw.message( 'multimediaviewer-embed-html' ).text() } ); this.embedSwitch.addItems( [ wikitextButtonOption, htmlButtonOption ] ); $( '
' )
.append( this.embedSwitch.$element )
.appendTo( $container );
// Default to 'wikitext'
this.embedSwitch.selectItem( wikitextButtonOption );
};
/**
* Creates pulldown menus to select file sizes.
*
* @param {jQuery} $container
*/
EP.createSizePulldownMenus = function( $container ) {
var placeholderDimensions = $( '' )
.addClass( 'mw-mmv-embed-dimensions' )
.text( mw.message( 'multimediaviewer-embed-dimensions', 0, 0 ).text() ).get( 0 ).outerHTML;
// Wikitext sizes pulldown menu
this.embedSizeSwitchWikitext = new oo.ui.InlineMenuWidget( {
classes: [ 'mw-mmv-embed-size', 'active' ]
} );
this.embedSizeChoicesWikitext = {};
this.embedSizeSwitchWikitext.getMenu().addItems( [
this.embedSizeChoicesWikitext.default = new oo.ui.MenuItemWidget( { name: 'default' }, {
label: mw.message( 'multimediaviewer-default-embed-dimensions' ).text(),
autoFitLabel: false
} ),
this.embedSizeChoicesWikitext.small = new oo.ui.MenuItemWidget( {
name: 'small',
height: null,
width: null
},
{
label: $( '' ).html( mw.message( 'multimediaviewer-small-embed-dimensions', placeholderDimensions ).text() ),
autoFitLabel: false
} ),
this.embedSizeChoicesWikitext.medium = new oo.ui.MenuItemWidget( {
name: 'medium',
height: null,
width: null
},
{
label: $( '' ).html( mw.message( 'multimediaviewer-medium-embed-dimensions', placeholderDimensions ).text() ),
autoFitLabel: false
} ),
this.embedSizeChoicesWikitext.large = new oo.ui.MenuItemWidget( {
name: 'large',
height: null,
width: null
},
{
label: $( '' ).html( mw.message( 'multimediaviewer-large-embed-dimensions', placeholderDimensions ).text() ),
autoFitLabel: false
} )
] );
this.embedSizeSwitchWikitext.getMenu().selectItem( this.embedSizeChoicesWikitext.default );
// Html sizes pulldown menu
this.embedSizeSwitchHtml = new oo.ui.InlineMenuWidget( {
classes: [ 'mw-mmv-embed-size' ]
} );
this.embedHtmlSizeChoices = {};
this.embedSizeSwitchHtml.getMenu().addItems( [
this.embedHtmlSizeChoices.small = new oo.ui.MenuItemWidget( {
name: 'small',
height: null,
width: null
},
{
label: $( '' ).html( mw.message( 'multimediaviewer-small-embed-dimensions', placeholderDimensions ).text() ),
autoFitLabel: false
} ),
this.embedHtmlSizeChoices.medium = new oo.ui.MenuItemWidget( {
name: 'medium',
height: null,
width: null
},
{
label: $( '' ).html( mw.message( 'multimediaviewer-medium-embed-dimensions', placeholderDimensions ).text() ),
autoFitLabel: false
} ),
this.embedHtmlSizeChoices.large = new oo.ui.MenuItemWidget( {
name: 'large',
height: null,
width: null
},
{
label: $( '' ).html( mw.message( 'multimediaviewer-large-embed-dimensions', placeholderDimensions ).text() ),
autoFitLabel: false
} ),
this.embedHtmlSizeChoices.original = new oo.ui.MenuItemWidget( {
name: 'original',
height: null,
width: null
},
{
label: $( '' ).html( mw.message( 'multimediaviewer-original-embed-dimensions', placeholderDimensions ).text() ),
autoFitLabel: false
} )
] );
this.embedSizeSwitchHtml.getMenu().selectItem( this.embedHtmlSizeChoices.small );
$( ' ' )
.append(
this.embedSizeSwitchHtml.$element,
this.embedSizeSwitchWikitext.$element
)
.appendTo( $container );
};
/**
* Registers listeners.
*/
EP.attach = function() {
var embed = this,
$htmlTextarea = this.embedTextHtml.$element.find( 'textarea' ),
$wikitextTextarea = this.embedTextWikitext.$element.find( 'textarea' );
// Select all text once element gets focus
this.embedTextHtml.onDOMEvent( 'focus', $.proxy( this.selectAllOnEvent, $htmlTextarea ) );
this.embedTextWikitext.onDOMEvent( 'focus', $.proxy( this.selectAllOnEvent, $wikitextTextarea ) );
// Disable partial text selection inside the textboxes
this.embedTextHtml.onDOMEvent( 'mousedown click', $.proxy( this.onlyFocus, $htmlTextarea ) );
this.embedTextWikitext.onDOMEvent( 'mousedown click', $.proxy( this.onlyFocus, $wikitextTextarea ) );
// Register handler for switching between wikitext/html snippets
this.embedSwitch.on( 'select', $.proxy( embed.handleTypeSwitch, embed ) );
// workaround for bug 63094
this.proxiedHandleSizeSwitch = this.proxiedHandleSizeSwitch || $.proxy( this.handleSizeSwitch, this );
// Register handlers for switching between file sizes
this.embedSizeSwitchHtml.getMenu().on( 'select', this.proxiedHandleSizeSwitch );
this.embedSizeSwitchWikitext.getMenu().on( 'select', this.proxiedHandleSizeSwitch );
};
/**
* Clears listeners.
*/
EP.unattach = function() {
this.constructor.super.prototype.unattach.call( this );
this.embedTextHtml.offDOMEvent( 'focus mousedown click' );
this.embedTextWikitext.offDOMEvent( 'focus mousedown click' );
this.embedSwitch.off( 'select' );
// the noop is needed for some tests which call unattach before calling attach.
this.embedSizeSwitchHtml.getMenu().off( 'select', this.proxiedHandleSizeSwitch || $.noop );
this.embedSizeSwitchWikitext.getMenu().off( 'select', this.proxiedHandleSizeSwitch || $.noop );
};
/**
* Handles size menu change events.
*/
EP.handleSizeSwitch = function ( item ) {
var value = item.getData();
this.changeSize( value.width, value.height );
};
/**
* Handles snippet type switch.
*/
EP.handleTypeSwitch = function ( item ) {
var value = item.getData();
if ( value === 'html' ) {
this.$currentMainEmbedText = this.embedTextHtml.$element;
this.currentSizeMenu = this.embedSizeSwitchHtml.getMenu();
this.embedSizeSwitchWikitext.getMenu().hide();
} else if ( value === 'wikitext' ) {
this.$currentMainEmbedText = this.embedTextWikitext.$element;
this.currentSizeMenu = this.embedSizeSwitchWikitext.getMenu();
this.embedSizeSwitchHtml.getMenu().hide();
}
this.embedTextHtml.$element
.add( this.embedSizeSwitchHtml.$element )
.toggleClass( 'active', value === 'html' );
this.embedTextWikitext.$element
.add( this.embedSizeSwitchWikitext.$element )
.toggleClass( 'active', value === 'wikitext' );
this.select();
this.currentSizeMenu.selectItem( this.currentSizeMenu.getSelectedItem() );
};
/**
* Changes the size, takes different actions based on which sort of
* embed is currently chosen.
*
* @param {number} width New width to set
* @param {number} height New height to set
*/
EP.changeSize = function ( width, height ) {
var currentItem = this.embedSwitch.getSelectedItem();
if ( currentItem === null ) {
return;
}
switch ( currentItem.getData() ) {
case 'html':
this.updateEmbedHtml( {}, width, height );
break;
case 'wikitext':
this.updateEmbedWikitext( width );
break;
}
this.select();
};
/**
* Sets the HTML embed text.
*
* Assumes that the set() method has already been called to update this.embedFileInfo
* @param {mw.mmv.model.Thumbnail} thumbnail (can be just an empty object)
* @param {number} width New width to set
* @param {number} height New height to set
*/
EP.updateEmbedHtml = function ( thumbnail, width, height ) {
var src;
if ( !this.embedFileInfo ) {
return;
}
src = thumbnail.url || this.embedFileInfo.imageInfo.url;
// If the image dimension requested are "large", use the current image url
if ( width > EP.LARGE_IMAGE_WIDTH_THRESHOLD || height > EP.LARGE_IMAGE_HEIGHT_THRESHOLD ) {
src = this.embedFileInfo.imageInfo.url;
}
this.embedTextHtml.setValue(
this.formatter.getThumbnailHtml( this.embedFileInfo, src, width, height ) );
};
/**
* Updates the wikitext embed text with a new value for width.
*
* Assumes that the set method has already been called.
* @param {number} width
*/
EP.updateEmbedWikitext = function ( width ) {
if ( !this.embedFileInfo ) {
return;
}
this.embedTextWikitext.setValue(
this.formatter.getThumbnailWikitextFromEmbedFileInfo( this.embedFileInfo, width )
);
};
/**
* Shows the pane.
*/
EP.show = function () {
this.constructor.super.prototype.show.call( this );
this.select();
};
/**
* Calculates possible image sizes for html snippets. It returns up to
* three possible snippet frame sizes (small, medium, large) plus the
* original image size.
*
* @param {number} width
* @param {number} height
* @returns {Object}
* @returns { {width: number, height: number} } return.small
* @returns { {width: number, height: number} } return.medium
* @returns { {width: number, height: number} } return.large
* @returns { {width: number, height: number} } return.original
*/
EP.getPossibleImageSizesForHtml = function ( width, height ) {
var i, bucketName,
currentGuess, dimensions,
bucketWidth, bucketHeight,
buckets = {
'small': { width: 220, height: 145 },
'medium': { width: 640, height: 480 },
'large': { width: 1200, height: 900 }
},
sizes = {},
bucketNames = Object.keys( buckets ),
widthToHeight = height / width,
heightToWidth = width / height;
for ( i = 0; i < bucketNames.length; i++ ) {
bucketName = bucketNames[i];
dimensions = buckets[bucketName];
bucketWidth = dimensions.width;
bucketHeight = dimensions.height;
if ( width > bucketWidth ) {
// Width fits in the current bucket
currentGuess = bucketWidth;
if ( currentGuess * widthToHeight > bucketHeight ) {
// Constrain in height, resize width accordingly
sizes[bucketName] = {
width: Math.round( bucketHeight * heightToWidth ),
height: bucketHeight
};
} else {
sizes[bucketName] = {
width: currentGuess,
height: Math.round( currentGuess * widthToHeight )
};
}
} else if ( height > bucketHeight ) {
// Height fits in the current bucket, resize width accordingly
sizes[bucketName] = {
width: Math.round( bucketHeight * heightToWidth ),
height: bucketHeight
};
}
}
sizes.original = { width: width, height: height };
return sizes;
};
/**
* Calculates possible image sizes for wikitext snippets. It returns up to
* three possible snippet frame sizes (small, medium, large).
*
* @param {number} width
* @param {number} height
* @returns {Object}
* @returns { {width: number, height: number} } return.small
* @returns { {width: number, height: number} } return.medium
* @returns { {width: number, height: number} } return.large
*/
EP.getPossibleImageSizesForWikitext = function ( width, height ) {
var i, bucketName,
bucketWidth,
buckets = {
'small': 300,
'medium': 400,
'large': 500
},
sizes = {},
bucketNames = Object.keys( buckets ),
widthToHeight = height / width;
for ( i = 0; i < bucketNames.length; i++ ) {
bucketName = bucketNames[i];
bucketWidth = buckets[bucketName];
if ( width > bucketWidth ) {
sizes[bucketName] = {
width: bucketWidth,
height: Math.round( bucketWidth * widthToHeight )
};
}
}
return sizes;
};
/**
* Gets size options for html and wikitext snippets.
*
* @param {number} width
* @param {number} height
* @returns {Object}
* @returns {Object} return.html Collection of possible image sizes for html snippets
* @returns {Object} return.wikitext Collection of possible image sizes for wikitext snippets
*/
EP.getSizeOptions = function ( width, height ) {
var sizes = {};
sizes.html = this.getPossibleImageSizesForHtml( width, height );
sizes.wikitext = this.getPossibleImageSizesForWikitext( width, height );
return sizes;
};
/**
* Sets the data on the element.
*
* @param {mw.mmv.model.Image} image
* @param {mw.mmv.model.Repo} repo
* @param {string} caption
*/
EP.set = function ( image, repo, caption ) {
var embed = this,
sizes = this.getSizeOptions( image.width, image.height );
this.embedFileInfo = new mw.mmv.model.EmbedFileInfo( image, repo, caption );
this.updateMenuOptions( sizes.html,
this.embedSizeSwitchHtml.getMenu().getItems() );
this.updateMenuOptions( sizes.wikitext,
this.embedSizeSwitchWikitext.getMenu().getItems() );
this.currentSizeMenu.selectItem( this.currentSizeMenu.getSelectedItem() );
this.getThumbnailUrlPromise().done( function ( thumbnail ) {
embed.updateEmbedHtml( thumbnail );
embed.select();
} );
};
/**
* @private
*
* Gets a promise for the large thumbnail URL. This is needed because thumbnail URLs cannot
* be reliably guessed, even if we know the full size of the image - most of the time replacing
* the size in another thumbnail URL works (as long as the new size is not larger than the full
* size), but if the file name is very long and with the larger size the URL length would
* exceed a certain threshold, a different schema is used instead.
*
* FIXME document this better - why is this only needed for the large thumbnail?
*
* @return {jQuery.Promise.