diff --git a/MultimediaViewer.php b/MultimediaViewer.php
index d18d364e0..1d76e1929 100644
--- a/MultimediaViewer.php
+++ b/MultimediaViewer.php
@@ -96,6 +96,16 @@ call_user_func( function() {
),
), $moduleInfo( 'mmv/model' ) );
+ $wgResourceModules['mmv.model.EmbedFileInfo'] = array_merge( array(
+ 'scripts' => array(
+ 'mmv.model.EmbedFileInfo.js',
+ ),
+
+ 'dependencies' => array(
+ 'mmv.model',
+ ),
+ ), $moduleInfo( 'mmv/model' ) );
+
$wgResourceModules['mmv.model.FileUsage'] = array_merge( array(
'scripts' => array(
'mmv.model.FileUsage.js',
@@ -353,6 +363,29 @@ call_user_func( function() {
),
), $moduleInfo( 'mmv/ui' ) );
+ $wgResourceModules['mmv.embedFileFormatter'] = array_merge( array(
+ 'scripts' => array(
+ 'mmv.EmbedFileFormatter.js',
+ ),
+
+ 'dependencies' => array(
+ 'mmv.base',
+ 'oojs',
+ ),
+
+ 'messages' => array(
+ 'multimediaviewer-credit',
+
+ 'multimediaviewer-html-embed-credit-text-tbls',
+ 'multimediaviewer-html-embed-credit-text-tls',
+ 'multimediaviewer-html-embed-credit-text-tbs',
+ 'multimediaviewer-html-embed-credit-text-tbl',
+ 'multimediaviewer-html-embed-credit-text-tb',
+ 'multimediaviewer-html-embed-credit-text-ts',
+ 'multimediaviewer-html-embed-credit-text-tl',
+ ),
+ ), $moduleInfo( 'mmv' ) );
+
$wgResourceModules['mmv.ui.reuse.dialog'] = array_merge( array(
'scripts' => array(
'mmv.ui.reuse.dialog.js',
@@ -367,6 +400,7 @@ call_user_func( function() {
'oojs',
'oojs-ui',
'mmv.ui.reuse.share',
+ 'mmv.ui.reuse.embed',
),
'messages' => array(
@@ -412,6 +446,40 @@ call_user_func( function() {
),
), $moduleInfo( 'mmv/ui' ) );
+ $wgResourceModules['mmv.ui.reuse.embed'] = array_merge( array(
+ 'scripts' => array(
+ 'mmv.ui.reuse.embed.js',
+ ),
+
+ 'styles' => array(
+ 'mmv.ui.reuse.embed.less',
+ ),
+
+ 'dependencies' => array(
+ 'mmv.ui.reuse.tab',
+ 'oojs',
+ 'oojs-ui',
+ 'mmv.model.EmbedFileInfo',
+ 'mmv.embedFileFormatter',
+ ),
+
+ 'messages' => array(
+ 'multimediaviewer-embed-tab',
+ 'multimediaviewer-embed-html',
+ 'multimediaviewer-embed-wt',
+
+ 'multimediaviewer-embed-byline',
+ 'multimediaviewer-embed-license',
+ 'multimediaviewer-embed-via',
+
+ 'multimediaviewer-default-embed-size',
+ 'multimediaviewer-original-embed-size',
+ 'multimediaviewer-large-embed-size',
+ 'multimediaviewer-medium-embed-size',
+ 'multimediaviewer-small-embed-size',
+ ),
+ ), $moduleInfo( 'mmv/ui' ) );
+
$wgResourceModules['mmv.ui.buttons'] = array_merge( array(
'scripts' => array(
'mmv.ui.buttons.js',
diff --git a/MultimediaViewerHooks.php b/MultimediaViewerHooks.php
index 94f270e6e..412b66817 100644
--- a/MultimediaViewerHooks.php
+++ b/MultimediaViewerHooks.php
@@ -125,6 +125,7 @@ class MultimediaViewerHooks {
'tests/qunit/mmv/mmv.lightboxinterface.test.js',
'tests/qunit/mmv/mmv.lightboximage.test.js',
'tests/qunit/mmv/mmv.ThumbnailWidthCalculator.test.js',
+ 'tests/qunit/mmv/mmv.EmbedFileFormatter.test.js',
'tests/qunit/mmv/mmv.performance.test.js',
'tests/qunit/mmv/mmv.logger.test.js',
'tests/qunit/mmv/model/mmv.model.test.js',
@@ -146,6 +147,7 @@ class MultimediaViewerHooks {
'tests/qunit/mmv/ui/mmv.ui.metadataPanel.test.js',
'tests/qunit/mmv/ui/mmv.ui.permission.test.js',
'tests/qunit/mmv/ui/mmv.ui.reuse.dialog.test.js',
+ 'tests/qunit/mmv/ui/mmv.ui.reuse.embed.test.js',
'tests/qunit/mmv/ui/mmv.ui.reuse.share.test.js',
'tests/qunit/mmv/ui/mmv.ui.reuse.tab.test.js',
'tests/qunit/mmv/mmv.testhelpers.js',
diff --git a/resources/mmv/mmv.EmbedFileFormatter.js b/resources/mmv/mmv.EmbedFileFormatter.js
new file mode 100644
index 000000000..30e403c52
--- /dev/null
+++ b/resources/mmv/mmv.EmbedFileFormatter.js
@@ -0,0 +1,60 @@
+/*
+ * 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 .
+ */
+
+( function( mw ) {
+ var AFP;
+
+ /**
+ * Converts data in various formats needed by the Embed sub-dialog
+ * @class mw.mmv.EmbedFileFormatter
+ * @constructor
+ */
+ function EmbedFileFormatter() {}
+ AFP = EmbedFileFormatter.prototype;
+
+ /**
+ * Helper function to generate thumbnail wikicode
+ * @param {mw.Title} title
+ * @param {number} [width]
+ * @param {string} [caption]
+ * @return {string}
+ */
+ AFP.getThumbnailWikitext = function ( title, width, caption ) {
+ var widthSection, captionSection;
+
+ widthSection = width ? '|' + width + 'px' : '';
+ captionSection = caption ? '|' + caption : '';
+
+ return '[[File:' + title.getMain() + widthSection + '|thumb' + captionSection + ']]';
+ };
+
+ /**
+ * Helper function to generate thumbnail wikicode
+ * @param {mw.mmv.model.EmbedFileInfo} info
+ * @param {number} [width]
+ * @return {string}
+ */
+ AFP.getThumbnailWikitextFromEmbedFileInfo = function ( info, width ) {
+ var title = info.title,
+ caption = info.caption;
+
+ return this.getThumbnailWikitext( info.title, width,
+ caption ? caption.plain : title.getNameText() );
+ };
+
+ mw.mmv.EmbedFileFormatter = EmbedFileFormatter;
+}( mediaWiki ) );
diff --git a/resources/mmv/model/mmv.model.EmbedFileInfo.js b/resources/mmv/model/mmv.model.EmbedFileInfo.js
new file mode 100644
index 000000000..ba6eb724d
--- /dev/null
+++ b/resources/mmv/model/mmv.model.EmbedFileInfo.js
@@ -0,0 +1,165 @@
+/*
+ * 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 .
+ */
+
+( function ( mw, $ ) {
+ /**
+ * Contains information needed to embed and share files.
+ * @class mw.mmv.model.EmbedFileInfo
+ * @constructor
+ * @param {mw.Title} title
+ * @param {string} src
+ * @param {string} url
+ * @param {string} [siteName]
+ * @param {Object} [license]
+ * @param {string} [license.plain]
+ * @param {string} [license.html]
+ * @param {Object} [author]
+ * @param {string} [author.plain]
+ * @param {string} [author.html]
+ * @param {Object} [source]
+ * @param {string} [source.plain]
+ * @param {string} [source.html]
+ * @param {Object} [caption]
+ * @param {string} [caption.plain]
+ * @param {string} [caption.html]
+ */
+ function EmbedFileInfo(
+ title,
+ src,
+ url,
+ siteName,
+ license,
+ author,
+ source,
+ caption
+ ) {
+ if ( !title || !src || !url ) {
+ throw 'title, src and url are required and must have a value';
+ }
+
+ /** @property {mw.Title} title The title of the file */
+ this.title = title;
+
+ /** @property {string} src The URL to the original file */
+ this.src = src;
+
+ /** @property {string} url The URL to the file description page */
+ this.url = url;
+
+ /** @property {string} [siteName] Human-readable name of the site the file is on */
+ this.siteName = siteName;
+
+ /** @property {Object} [license] Description of the license of the file - with links */
+ this.license = license;
+
+ /** @property {Object} [author] Author of the file - with links */
+ this.author = author;
+
+ /** @property {Object} [source] Source for the file - with links */
+ this.source = source;
+
+ /** @property {Object} [caption] Image caption, if any */
+ this.caption = caption;
+ }
+
+ /**
+ * Helper function to turn HTML to plaintext
+ * @private
+ * @param {string} html
+ * @return {{plain: string, html: string}|null}
+ */
+ function htmlToObject ( html ) {
+ if ( html && html.length ) {
+ return {
+ plain: $( '
' + html + '
' ).text(),
+ html: html
+ };
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Factory method for creating an info object from html
+ * @param {mw.Title} title
+ * @param {string} src
+ * @param {string} url
+ * @param {string} [siteName]
+ * @param {string} [licenseHtml]
+ * @param {string} [authorHtml]
+ * @param {string} [sourceHtml]
+ * @param {string} [captionHtml]
+ * @return {mw.mmv.model.EmbedFileInfo}
+ */
+ EmbedFileInfo.fromHtml = function (
+ title,
+ src,
+ url,
+ siteName,
+ licenseHtml,
+ authorHtml,
+ sourceHtml,
+ captionHtml
+ ) {
+ return new EmbedFileInfo(
+ title,
+ src,
+ url,
+ siteName,
+ htmlToObject( licenseHtml ),
+ htmlToObject( authorHtml ),
+ htmlToObject( sourceHtml ),
+ htmlToObject( captionHtml )
+ );
+ };
+
+ /**
+ * Turns image info into EmbedFileInfo
+ * @param {mw.mmv.model.Image} imageInfo
+ * @param {string} [siteName]
+ * @param {string} [caption]
+ * @return {mw.mmv.model.EmbedFileInfo}
+ */
+ EmbedFileInfo.fromImageInfo = function ( imageInfo, siteName, caption ) {
+ var title = imageInfo.title,
+ src = imageInfo.url,
+ url = imageInfo.descriptionUrl,
+ license = imageInfo.license,
+ author = imageInfo.author,
+ source = imageInfo.source;
+
+ return EmbedFileInfo.fromHtml( title, src, url, siteName, license, author, source, caption );
+ };
+
+ /**
+ * Turns a jQuery object into a plaintext/HTML pair
+ * @param $jq
+ * @return {{plain: string, html:string}|null}
+ */
+ EmbedFileInfo.jqueryToObject = function ( $jq ) {
+ if ( $jq && $jq.length ) {
+ return {
+ plain: $jq.text(),
+ html: $jq.get( 0 ).outerHTML
+ };
+ } else {
+ return null;
+ }
+ };
+
+ mw.mmv.model.EmbedFileInfo = EmbedFileInfo;
+}( mediaWiki, jQuery ) );
diff --git a/resources/mmv/ui/mmv.ui.metadataPanel.js b/resources/mmv/ui/mmv.ui.metadataPanel.js
index 33a6285c9..e477c8972 100644
--- a/resources/mmv/ui/mmv.ui.metadataPanel.js
+++ b/resources/mmv/ui/mmv.ui.metadataPanel.js
@@ -446,9 +446,12 @@
/**
* Sets up the file reuse data in the DOM
* @param {mw.mmv.model.Image} image
+ * @param {string} siteName
+ * @param {string} caption
*/
- MPP.setFileReuseData = function ( image ) {
- this.fileReuse.set( image );
+ MPP.setFileReuseData = function ( image, siteName, caption ) {
+ this.fileReuse.set( image,
+ mw.mmv.model.EmbedFileInfo.fromImageInfo( image, siteName, caption ) );
};
/**
diff --git a/resources/mmv/ui/mmv.ui.reuse.dialog.js b/resources/mmv/ui/mmv.ui.reuse.dialog.js
index da2aca613..0cdee122d 100644
--- a/resources/mmv/ui/mmv.ui.reuse.dialog.js
+++ b/resources/mmv/ui/mmv.ui.reuse.dialog.js
@@ -68,17 +68,21 @@
// FIXME this should happen outside the dialog and the tabs, but we need to improve
DP.initTabs = function () {
- var shareTab;
+ var shareTab, embedTab;
this.tabs = {
- share: new mw.mmv.ui.reuse.Share( this.$reuseDialog )
+ share: new mw.mmv.ui.reuse.Share( this.$reuseDialog ),
+ embed: new mw.mmv.ui.reuse.Embed( this.$reuseDialog )
};
shareTab = new oo.ui.MenuItemWidget(
'share', { label: mw.message( 'multimediaviewer-share-tab' ).text() } );
+ embedTab = new oo.ui.MenuItemWidget(
+ 'embed', { label: mw.message( 'multimediaviewer-embed-tab' ).text() } );
this.reuseTabs.addItems( [
- shareTab
+ shareTab,
+ embedTab
] );
// Default to 'share' tab
@@ -154,9 +158,11 @@
/**
* Sets data needed by contaned tabs and makes dialog launch link visible.
* @param {mw.mmv.model.Image} image
+ * @param {mw.mmv.model.EmbedFileInfo} info
*/
- DP.set = function ( image ) {
+ DP.set = function ( image, info ) {
this.tabs.share.set( image );
+ this.tabs.embed.set( image, info );
this.$reuseLink.removeClass( 'empty' );
};
diff --git a/resources/mmv/ui/mmv.ui.reuse.embed.js b/resources/mmv/ui/mmv.ui.reuse.embed.js
new file mode 100644
index 000000000..f099e0d38
--- /dev/null
+++ b/resources/mmv/ui/mmv.ui.reuse.embed.js
@@ -0,0 +1,340 @@
+/*
+ * 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 .
+ */
+
+( function ( mw, $, oo ) {
+ // Shortcut for prototype later
+ var EP;
+
+ /**
+ * UI component that provides the user html/wikitext snippets needed to share
+ * and/or embed a media asset.
+ *
+ * @class mw.mmv.ui.reuse.Embed
+ * @extends mw.mmv.ui.reuse.Tab
+ * @constructor
+ * @param {jQuery} $container
+ */
+ function Embed( $container ) {
+ Embed.super.call( this, $container );
+
+ /**
+ * Formatter converting image data into formats needed for output
+ * @property {mw.mmv.EmbedFileFormatter}
+ */
+ this.formatter = new mw.mmv.EmbedFileFormatter();
+
+ this.$pane.addClass( 'mw-mlb-embed-pane' );
+
+ this.$pane.appendTo( this.$container );
+
+ this.createSnippetTextAreas( this.$pane );
+ this.createSizePulldownMenus( this.$pane );
+
+ /**
+ * Currently selected embed snippet, defaults to wikitext.
+ * @property {jQuery}
+ */
+ this.$currentMainEmbedText = this.embedTextWikitext.$element;
+
+ /**
+ * Currently selected size menu.
+ * @property {OO.ui.MenuWidget}
+ */
+ this.currentSizeMenu = this.embedWtSizeSwitch.getMenu();
+ }
+ oo.inheritClass( Embed, mw.mmv.ui.reuse.Tab );
+ EP = Embed.prototype;
+
+
+ /**
+ * Creates text areas for html and wikitext snippets.
+ *
+ * @param {jQuery} $container
+ */
+ EP.createSnippetTextAreas = function( $container ) {
+ this.embedTextWikitext = new oo.ui.TextInputWidget( {
+ classes: [ 'mw-mlb-embed-text-wt', 'active' ],
+ multiline: true,
+ readOnly: true
+ } );
+
+ $( '' )
+ .append(
+ this.embedTextWikitext.$element
+ )
+ .appendTo( $container );
+ };
+
+ /**
+ * Creates pulldown menus to select file sizes.
+ *
+ * @param {jQuery} $container
+ */
+ EP.createSizePulldownMenus = function( $container ) {
+ // Wikitext sizes pulldown menu
+ this.embedWtSizeSwitch = new oo.ui.InlineMenuWidget( {
+ classes: [ 'mw-mlb-embed-size', 'active' ]
+ } );
+
+ this.embedWtSizeChoices = {};
+
+ this.embedWtSizeSwitch.getMenu().addItems( [
+ this.embedWtSizeChoices.default = new oo.ui.MenuItemWidget( { name: 'default' }, {
+ label: mw.message( 'multimediaviewer-default-embed-size' ).text()
+ } ),
+
+ this.embedWtSizeChoices.small = new oo.ui.MenuItemWidget( {
+ name: 'small',
+ height: null,
+ width: null
+ },
+ {
+ label: mw.message( 'multimediaviewer-small-embed-size', 0, 0 ).text(),
+ selected: true
+ } ),
+
+ this.embedWtSizeChoices.medium = new oo.ui.MenuItemWidget( {
+ name: 'medium',
+ height: null,
+ width: null
+ },
+ {
+ label: mw.message( 'multimediaviewer-medium-embed-size', 0, 0 ).text()
+ } ),
+
+ this.embedWtSizeChoices.large = new oo.ui.MenuItemWidget( {
+ name: 'large',
+ height: null,
+ width: null
+ },
+ {
+ label: mw.message( 'multimediaviewer-large-embed-size', 0, 0 ).text()
+ } )
+ ] );
+
+ this.embedWtSizeSwitch.getMenu().selectItem( this.embedWtSizeChoices.default );
+
+ $( '
' )
+ .append(
+ this.embedWtSizeSwitch.$element
+ )
+ .appendTo( $container );
+ };
+
+ /**
+ * Registers listeners.
+ */
+ EP.attach = function() {
+ var embed = this,
+ $wikitextTextarea = this.embedTextWikitext.$element.find( 'textarea' );
+
+ // Select all text once element gets focus
+ this.embedTextWikitext.onDOMEvent( 'focus', $.proxy( this.selectAllOnEvent, $wikitextTextarea ) );
+ this.embedTextWikitext.onDOMEvent( 'mousedown click', $.proxy( this.onlyFocus, $wikitextTextarea ) );
+
+ // Register handlers for switching between file sizes
+ this.embedWtSizeSwitch.getMenu().on( 'select', $.proxy( embed.handleSizeSwitch, embed ) );
+ };
+
+ /**
+ * Clears listeners.
+ */
+ EP.unattach = function() {
+ this.constructor.super.prototype.unattach.call( this );
+
+ this.embedTextWikitext.offDOMEvent( 'focus mousedown click' );
+ this.embedWtSizeSwitch.getMenu().off( 'select' );
+ };
+
+ /**
+ * Handles size menu change events.
+ */
+ EP.handleSizeSwitch = function ( item ) {
+ var value = item.getData();
+
+ this.changeSize( value.width, value.height );
+ };
+
+ /**
+ * Changes the size, takes different actions based on which sort of
+ * embed is currently chosen.
+ *
+ * @param {number} width New width to set
+ */
+ EP.changeSize = function ( width ) {
+ this.updateWtEmbedText( width );
+ this.select();
+ };
+
+ /**
+ * Updates the wikitext embed text with a new value for width.
+ *
+ * Assumes that the set method has already been called.
+ * @param {number} width
+ */
+ EP.updateWtEmbedText = function ( width ) {
+ if ( !this.embedFileInfo ) {
+ return;
+ }
+
+ var title = this.embedFileInfo.title,
+ caption = this.embedFileInfo.caption;
+
+ this.embedTextWikitext.setValue( this.formatter.getThumbnailWikitext(
+ title, width, caption ? caption.plain : title.getNameText() ) );
+ };
+
+ /**
+ * Shows the pane.
+ */
+ EP.show = function () {
+ this.constructor.super.prototype.show.call( this );
+ this.select();
+ };
+
+ /**
+ * 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.wikitext = this.getPossibleImageSizesForWikitext( width, height );
+
+ return sizes;
+ };
+
+ /**
+ * Sets the data on the element.
+ *
+ * @param {mw.mmv.model.Image} image
+ * @param {mw.mmv.model.EmbedFileInfo} embedFileInfo
+ */
+ EP.set = function ( image, embedFileInfo ) {
+ var wtSizeSwitch = this.embedWtSizeSwitch.getMenu(),
+ wtSizeOptions = wtSizeSwitch.getItems(),
+ sizes = this.getSizeOptions( image.width, image.height );
+
+ this.embedFileInfo = embedFileInfo;
+
+ this.updateMenuOptions( sizes.wikitext, wtSizeOptions );
+
+ this.currentSizeMenu.selectItem( this.currentSizeMenu.getSelectedItem() );
+ };
+
+ /**
+ * @private
+ *
+ * Updates the menu options based on calculated sizes.
+ *
+ * @param {Object} sizes
+ * @param {OO.ui.MenuItemWidget[]} options
+ */
+ EP.updateMenuOptions = function ( sizes, options ) {
+ var i, option, data;
+
+ for ( i = 0; i < options.length; i++ ) {
+ option = options[i];
+ data = option.getData();
+
+ if ( sizes[data.name] ) {
+ option.setDisabled( false );
+
+ // These values are later used in the else if case below as flags
+ // to disable an option that is no longer pertinent. Ex: User went
+ // from a large image from which we have options(small, med, large) to
+ // a small image where the only pertinent option is small.
+ data.width = sizes[data.name].width;
+ data.height = sizes[data.name].height;
+
+ option.setLabel(
+ mw.message(
+ 'multimediaviewer-' + data.name + '-embed-size',
+ data.width,
+ data.height
+ ).text()
+ );
+ } else if ( data.width && data.height ) {
+ option.setDisabled( true );
+
+ data.width = null;
+ data.height = null;
+ }
+ }
+ };
+
+ /**
+ * @inheritdoc
+ */
+ EP.empty = function () {
+ this.embedTextWikitext.setValue( '' );
+
+ this.embedWtSizeSwitch.getMenu().hide();
+ };
+
+ /**
+ * Selects the text in the current text box.
+ */
+ EP.select = function () {
+ this.$currentMainEmbedText.focus();
+ };
+
+ mw.mmv.ui.reuse.Embed = Embed;
+}( mediaWiki, jQuery, OO ) );
diff --git a/resources/mmv/ui/mmv.ui.reuse.embed.less b/resources/mmv/ui/mmv.ui.reuse.embed.less
new file mode 100644
index 000000000..f840978e5
--- /dev/null
+++ b/resources/mmv/ui/mmv.ui.reuse.embed.less
@@ -0,0 +1,39 @@
+@switch-color: #f2f2f2;
+@active-switch-color: #666666;
+
+.mw-mlb-embed-text-html,
+.mw-mlb-embed-text-wt {
+ display: none;
+ width: auto;
+
+ &.active {
+ display: block;
+ }
+}
+
+.mw-mlb-reuse-dialog .mw-mlb-embed-pane {
+ padding: 5px 27px;
+}
+
+.mw-mlb-embed-switch {
+ div {
+ padding: 1px 3px;
+ background-color: @switch-color;
+
+ &.active {
+ background-color: @active-switch-color;
+ }
+ }
+}
+
+.mw-mlb-embed-size {
+ display: none;
+
+ &.active {
+ display: block;
+ }
+
+ .oo-ui-widget-disabled {
+ display: none;
+ }
+}
diff --git a/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js b/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js
new file mode 100644
index 000000000..f65e0e3a5
--- /dev/null
+++ b/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js
@@ -0,0 +1,48 @@
+( function ( mw ) {
+ QUnit.module( 'mmv.EmbedFileFormatter', QUnit.newMwEnvironment() );
+
+ QUnit.test( 'EmbedFileFormatter constructor sanity check', 1, function ( assert ) {
+ var formatter = new mw.mmv.EmbedFileFormatter();
+ assert.ok( formatter, 'constructor with no argument works');
+ } );
+
+ QUnit.test( 'getThumbnailWikitext():', 3, function ( assert ) {
+ var formatter = new mw.mmv.EmbedFileFormatter(),
+ title = mw.Title.newFromText( 'File:Foobar.jpg' ),
+ imgUrl = 'https://upload.wikimedia.org/wikipedia/commons/3/3a/Foobar.jpg',
+ filePageUrl = 'https://commons.wikimedia.org/wiki/File:Foobar.jpg',
+ caption = 'Foobar caption.',
+ width = 700,
+ info,
+ wikitext;
+
+ // Title, width and caption
+ info = new mw.mmv.model.EmbedFileInfo.fromHtml( title, imgUrl, filePageUrl, undefined,
+ undefined, undefined, undefined, caption );
+ wikitext = formatter.getThumbnailWikitextFromEmbedFileInfo( info, width );
+
+ assert.strictEqual(
+ wikitext,
+ '[[File:Foobar.jpg|700px|thumb|Foobar caption.]]',
+ 'Wikitext generated correctly.' );
+
+ // Title, width and no caption
+ info = new mw.mmv.model.EmbedFileInfo.fromHtml( title, imgUrl, filePageUrl );
+ wikitext = formatter.getThumbnailWikitextFromEmbedFileInfo( info , width );
+
+ assert.strictEqual(
+ wikitext,
+ '[[File:Foobar.jpg|700px|thumb|Foobar]]',
+ 'Wikitext generated correctly.' );
+
+ // Title, no width and no caption
+ info = new mw.mmv.model.EmbedFileInfo.fromHtml( title, imgUrl, filePageUrl );
+ wikitext = formatter.getThumbnailWikitextFromEmbedFileInfo( info );
+
+ assert.strictEqual(
+ wikitext,
+ '[[File:Foobar.jpg|thumb|Foobar]]',
+ 'Wikitext generated correctly.' );
+ } );
+
+}( mediaWiki ) );
diff --git a/tests/qunit/mmv/model/mmv.model.test.js b/tests/qunit/mmv/model/mmv.model.test.js
index ac7f25f2c..50759f499 100644
--- a/tests/qunit/mmv/model/mmv.model.test.js
+++ b/tests/qunit/mmv/model/mmv.model.test.js
@@ -15,7 +15,7 @@
* along with MultimediaViewer. If not, see .
*/
-( function ( mw ) {
+( function ( mw, $ ) {
QUnit.module( 'mmv.model', QUnit.newMwEnvironment() );
QUnit.test( 'Image model constructor sanity check', 21, function ( assert ) {
@@ -183,4 +183,47 @@
}
} );
-}( mediaWiki ) );
+ QUnit.test( 'EmbedFileInfo constructor sanity check', 9, function ( assert ) {
+ var title = mw.Title.newFromText( 'File:Foobar.jpg' ),
+ src = 'https://upload.wikimedia.org/wikipedia/commons/3/3a/Foobar.jpg',
+ url = 'https://commons.wikimedia.org/wiki/File:Foobar.jpg',
+ siteName = 'Name of the web site',
+ $license = $( 'Public License' ),
+ $author = $( 'Homer' ),
+ $source = $( 'Iliad' ),
+ license = {
+ plain: $license && $license.text(),
+ html: $license && $license.html()
+ },
+ author = {
+ plain: $author && $author.text(),
+ html: $author && $author.get( 0 ).outerHTML
+ },
+ source = {
+ plain: $source && $source.text(),
+ html: $source && $source.get( 0 ).outerHTML
+ },
+ caption = {
+ plain: 'Plain image caption',
+ html: 'HTML imgae caption'
+ },
+ embedFileInfo = new mw.mmv.model.EmbedFileInfo(
+ title, src, url, siteName, license, author, source, caption );
+
+ assert.strictEqual( embedFileInfo.title, title, 'Title is set correctly' );
+ assert.strictEqual( embedFileInfo.src, src, 'Src is set correctly' );
+ assert.strictEqual( embedFileInfo.url, url, 'Url is set correctly' );
+ assert.strictEqual( embedFileInfo.siteName, siteName, 'Site name is set correctly' );
+ assert.strictEqual( embedFileInfo.license, license, 'License is set correctly' );
+ assert.strictEqual( embedFileInfo.author, author, 'Author is set correctly' );
+ assert.strictEqual( embedFileInfo.source, source, 'Source is set correctly' );
+ assert.strictEqual( embedFileInfo.caption, caption, 'Caption is set correctly' );
+
+ try {
+ embedFileInfo = new mw.mmv.model.EmbedFileInfo( title );
+ } catch (e) {
+ assert.ok( e, 'Exception is thrown when parameters are missing' );
+ }
+ } );
+
+}( mediaWiki, jQuery ) );
diff --git a/tests/qunit/mmv/ui/mmv.ui.reuse.dialog.test.js b/tests/qunit/mmv/ui/mmv.ui.reuse.dialog.test.js
index 4297116c5..0577fc054 100644
--- a/tests/qunit/mmv/ui/mmv.ui.reuse.dialog.test.js
+++ b/tests/qunit/mmv/ui/mmv.ui.reuse.dialog.test.js
@@ -57,6 +57,30 @@
reuseDialog.handleOpenCloseClick();
} );
+ QUnit.test( 'handleTabSelection():', 4, function ( assert ) {
+ var reuseDialog = makeReuseDialog();
+
+ reuseDialog.tabs.share.show = function () {
+ assert.ok( true, 'Share tab shown.' );
+ };
+ reuseDialog.tabs.embed.hide = function () {
+ assert.ok( true, 'Embed tab hidden.' );
+ };
+
+ // Share pane is selected
+ reuseDialog.handleTabSelection( { getData: function () { return 'share'; } } );
+
+ reuseDialog.tabs.share.hide = function () {
+ assert.ok( true, 'Share tab hidden.' );
+ };
+ reuseDialog.tabs.embed.show = function () {
+ assert.ok( true, 'Embed tab shown.' );
+ };
+
+ // Embed pane is selected
+ reuseDialog.handleTabSelection( { getData: function () { return 'embed'; } } );
+ } );
+
QUnit.test( 'attach()/unattach():', 2, function ( assert ) {
var reuseDialog = makeReuseDialog();
@@ -154,11 +178,12 @@
descriptionUrl: url,
width: 100,
height: 80
- };
+ },
+ embedFileInfo = new mw.mmv.model.EmbedFileInfo( title, src, url );
assert.ok( reuseDialog.$reuseLink.hasClass( 'empty' ), 'Dialog launch link is empty by default.' );
- reuseDialog.set( image );
+ reuseDialog.set( image, embedFileInfo );
assert.ok( ! reuseDialog.$reuseLink.hasClass( 'empty' ), 'Dialog launch link is not empty after set().' );
@@ -178,9 +203,10 @@
descriptionUrl: url,
width: 100,
height: 80
- };
+ },
+ embedFileInfo = new mw.mmv.model.EmbedFileInfo( title, src, url );
- reuseDialog.set( image );
+ reuseDialog.set( image, embedFileInfo );
assert.ok( ! reuseDialog.isOpen, 'Dialog closed by default.' );
diff --git a/tests/qunit/mmv/ui/mmv.ui.reuse.embed.test.js b/tests/qunit/mmv/ui/mmv.ui.reuse.embed.test.js
new file mode 100644
index 000000000..9e9e43f95
--- /dev/null
+++ b/tests/qunit/mmv/ui/mmv.ui.reuse.embed.test.js
@@ -0,0 +1,229 @@
+/*
+ * 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 .
+ */
+
+ ( function ( mw, $ ) {
+ var $qf = $( '#qunit-fixture' );
+
+ QUnit.module( 'mmv.ui.reuse.Embed', QUnit.newMwEnvironment() );
+
+ QUnit.test( 'Sanity test, object creation and UI construction', 6, function ( assert ) {
+ var embed = new mw.mmv.ui.reuse.Embed( $qf );
+
+ assert.ok( embed, 'Embed UI element is created.' );
+ assert.strictEqual( embed.$pane.length, 1, 'Pane div is created.' );
+ assert.ok( embed.embedTextWikitext, 'Wikitext snipped text area created.' );
+ assert.ok( embed.embedWtSizeSwitch, 'Size selection menu for wikitext created.' );
+ assert.strictEqual( embed.$currentMainEmbedText.length, 1, 'Size selection menu for html created.' );
+ assert.ok( embed.currentSizeMenu, 'Size selection menu for html created.' );
+ } );
+
+ QUnit.test( 'changeSize(): Wikitext size menu item selected.', 2, function ( assert ) {
+ var embed = new mw.mmv.ui.reuse.Embed( $qf ),
+ width = 10,
+ height = 20;
+
+ embed.updateWtEmbedText = function( w ) {
+ assert.strictEqual( w, width, 'Correct width passed.' );
+ };
+ embed.select = function( ) {
+ assert.ok( true, 'Item selected after update.' );
+ };
+
+ embed.changeSize( width, height );
+ } );
+
+ QUnit.test( 'updateWtEmbedText(): Do nothing if set() not called before.', 0, function ( assert ) {
+ var embed = new mw.mmv.ui.reuse.Embed( $qf ),
+ width = 10;
+
+ embed.formatter.getThumbnailWikitext = function() {
+ assert.ok( false, 'formatter.getThumbnailWikitext() should not have been called.');
+ };
+ embed.updateWtEmbedText( width );
+ } );
+
+ QUnit.test( 'updateWtEmbedText():', 3, function ( assert ) {
+ var embed = new mw.mmv.ui.reuse.Embed( $qf ),
+ title = mw.Title.newFromText( 'File:Foobar.jpg' ),
+ src = 'https://upload.wikimedia.org/wikipedia/commons/3/3a/Foobar.jpg',
+ url = 'https://commons.wikimedia.org/wiki/File:Foobar.jpg',
+ embedFileInfo = new mw.mmv.model.EmbedFileInfo( title, src, url ),
+ width = 10,
+ height = 20;
+
+ embed.set( { width: width, height: height }, embedFileInfo );
+
+ embed.formatter.getThumbnailWikitext = function( t, w, c ) {
+ assert.strictEqual( t, title, 'Title passed correctly.');
+ assert.strictEqual( w, width, 'Width passed correctly.');
+ assert.strictEqual( c, title.getNameText(), 'Caption passed correctly.');
+ };
+ embed.updateWtEmbedText( width );
+ } );
+
+ QUnit.test( 'Size options are correct', 3, function ( assert ) {
+ var embed = new mw.mmv.ui.reuse.Embed( $qf ),
+ exampleSizes = [
+ // Big wide image
+ {
+ width: 2048, height: 1536,
+ expected: {
+ wikitext: {
+ small: { width: 300, height: 225 },
+ medium: { width: 400, height: 300 },
+ large: { width: 500, height: 375 }
+ }
+ }
+ },
+
+ // Big tall image
+ {
+ width: 201, height: 1536,
+ expected: {
+ wikitext: {}
+ }
+ },
+
+ // Very small image
+ {
+ width: 15, height: 20,
+ expected: {
+ wikitext: {}
+ }
+ }
+ ],
+ i, cursize, opts;
+ for ( i = 0; i < exampleSizes.length; i++ ) {
+ cursize = exampleSizes[i];
+ opts = embed.getSizeOptions( cursize.width, cursize.height );
+ assert.deepEqual( opts, cursize.expected, 'We got the expected results out of the size calculation function.' );
+ }
+ } );
+
+ QUnit.test( 'set():', 3, function ( assert ) {
+ var embed = new mw.mmv.ui.reuse.Embed( $qf ),
+ title = mw.Title.newFromText( 'File:Foobar.jpg' ),
+ src = 'https://upload.wikimedia.org/wikipedia/commons/3/3a/Foobar.jpg',
+ url = 'https://commons.wikimedia.org/wiki/File:Foobar.jpg',
+ embedFileInfo = new mw.mmv.model.EmbedFileInfo( title, src, url ),
+ width = 15,
+ height = 20;
+
+ embed.updateMenuOptions = function( sizes, options ) {
+ assert.strictEqual( options.length, 4, 'Options passed correctly.' );
+ };
+
+ assert.ok( !embed.embedFileInfo, 'embedFileInfo not set yet.' );
+
+ embed.set( { width: width, height: height }, embedFileInfo );
+
+ assert.ok( embed.embedFileInfo, 'embedFileInfo correctly set.' );
+ } );
+
+ QUnit.test( 'updateMenuOptions():', 3, function ( assert ) {
+ var embed = new mw.mmv.ui.reuse.Embed( $qf ),
+ options = embed.embedWtSizeSwitch.getMenu().getItems(),
+ width = 700,
+ height = 500,
+ sizes = embed.getSizeOptions( width, height ),
+ oldMessage = mw.message;
+
+ mw.message = function( messageKey ) {
+ assert.ok( messageKey.match(/^multimediaviewer-(small|medium|large)/), 'messageKey passed correctly.' );
+
+ return { text: $.noop };
+ };
+
+ embed.updateMenuOptions( sizes.wikitext, options );
+
+ mw.message = oldMessage;
+ } );
+
+ QUnit.test( 'empty():', 3, function ( assert ) {
+ var embed = new mw.mmv.ui.reuse.Embed( $qf ),
+ title = mw.Title.newFromText( 'File:Foobar.jpg' ),
+ src = 'https://upload.wikimedia.org/wikipedia/commons/3/3a/Foobar.jpg',
+ url = 'https://commons.wikimedia.org/wiki/File:Foobar.jpg',
+ embedFileInfo = new mw.mmv.model.EmbedFileInfo( title, src, url ),
+ width = 15,
+ height = 20;
+
+ embed.set( { width: width, height: height }, embedFileInfo );
+ embed.updateWtEmbedText( width );
+
+ assert.notStrictEqual( embed.embedTextWikitext.getValue(), '', 'embedTextWikitext is not empty.' );
+
+ embed.empty();
+
+ assert.strictEqual( embed.embedTextWikitext.getValue(), '', 'embedTextWikitext is empty.' );
+ assert.ok( ! embed.embedWtSizeSwitch.getMenu().isVisible(), 'Wikitext size menu should be hidden.' );
+ } );
+
+ QUnit.test( 'attach()/unattach():', 2, function ( assert ) {
+ var embed = new mw.mmv.ui.reuse.Embed( $qf ),
+ title = mw.Title.newFromText( 'File:Foobar.jpg' ),
+ src = 'https://upload.wikimedia.org/wikipedia/commons/3/3a/Foobar.jpg',
+ url = 'https://commons.wikimedia.org/wiki/File:Foobar.jpg',
+ embedFileInfo = new mw.mmv.model.EmbedFileInfo( title, src, url ),
+ width = 15,
+ height = 20;
+
+ embed.set( { width: width, height: height }, embedFileInfo );
+
+ embed.selectAllOnEvent = function() {
+ assert.ok( false, 'selectAllOnEvent should not have been called.' );
+ };
+ embed.handleSizeSwitch = function() {
+ assert.ok( false, 'handleTypeSwitch should not have been called.' );
+ };
+
+ // Triggering action events before attaching should do nothing
+ embed.embedTextWikitext.$element.focus();
+ embed.embedWtSizeSwitch.getMenu().emit(
+ 'select', embed.embedWtSizeSwitch.getMenu().getSelectedItem() );
+
+ embed.selectAllOnEvent = function() {
+ assert.ok( true, 'selectAllOnEvent was called.' );
+ };
+ embed.handleSizeSwitch = function() {
+ assert.ok( true, 'handleTypeSwitch was called.' );
+ };
+
+ embed.attach();
+
+ // Action events should be handled now
+ embed.embedTextWikitext.$element.focus();
+ embed.embedWtSizeSwitch.getMenu().emit(
+ 'select', embed.embedWtSizeSwitch.getMenu().getSelectedItem() );
+
+ // Test the unattach part
+ embed.selectAllOnEvent = function() {
+ assert.ok( false, 'selectAllOnEvent should not have been called.' );
+ };
+ embed.handleSizeSwitch = function() {
+ assert.ok( false, 'handleTypeSwitch should not have been called.' );
+ };
+
+ embed.unattach();
+
+ // Triggering action events now that we are unattached should do nothing
+ embed.embedTextWikitext.$element.focus();
+ embed.embedWtSizeSwitch.getMenu().emit(
+ 'select', embed.embedWtSizeSwitch.getMenu().getSelectedItem() );
+ } );
+
+}( mediaWiki, jQuery ) );