jenkins-bot has submitted this change and it was merged.

Change subject: Add embed tab to reuse dialog
......................................................................


Add embed tab to reuse dialog

Implements the wikitext part of the mingle card

Mingle: https://wikimedia.mingle.thoughtworks.com/projects/multimedia/cards/148
Change-Id: I5bcd8e2171f45c70736b7a7cfc695134269ed12d
---
M MultimediaViewer.php
M MultimediaViewerHooks.php
A resources/mmv/mmv.EmbedFileFormatter.js
A resources/mmv/model/mmv.model.EmbedFileInfo.js
M resources/mmv/ui/mmv.ui.metadataPanel.js
M resources/mmv/ui/mmv.ui.reuse.dialog.js
A resources/mmv/ui/mmv.ui.reuse.embed.js
A resources/mmv/ui/mmv.ui.reuse.embed.less
A tests/qunit/mmv/mmv.EmbedFileFormatter.test.js
M tests/qunit/mmv/model/mmv.model.test.js
M tests/qunit/mmv/ui/mmv.ui.reuse.dialog.test.js
A tests/qunit/mmv/ui/mmv.ui.reuse.embed.test.js
12 files changed, 1,041 insertions(+), 12 deletions(-)

Approvals:
  Gilles: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/MultimediaViewer.php b/MultimediaViewer.php
index d18d364..1d76e19 100644
--- a/MultimediaViewer.php
+++ b/MultimediaViewer.php
@@ -96,6 +96,16 @@
                ),
        ), $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 @@
                ),
        ), $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 @@
                        'oojs',
                        'oojs-ui',
                        'mmv.ui.reuse.share',
+                       'mmv.ui.reuse.embed',
                ),
 
                'messages' => array(
@@ -412,6 +446,40 @@
                ),
        ), $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 94f270e..412b668 100644
--- a/MultimediaViewerHooks.php
+++ b/MultimediaViewerHooks.php
@@ -125,6 +125,7 @@
                                '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 @@
                                
'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 0000000..30e403c
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+( 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 0000000..ba6eb72
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+( 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: $( '<div>' + html + '</div>' ).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 33a6285..e477c89 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 da2aca6..0cdee12 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 0000000..f099e0d
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+( 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
+               } );
+
+               $( '<p>' )
+                       .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 );
+
+               $( '<p>' )
+                       .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 0000000..f840978
--- /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 0000000..f65e0e3
--- /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 ac7f25f..50759f4 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 <http://www.gnu.org/licenses/>.
  */
 
-( 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 = $( '<a class="mw-mlb-license empty" 
href="/wiki/File:License.txt">Public License</a>' ),
+                       $author = $( '<span class="mw-mlb-author">Homer</span>' 
),
+                       $source = $( '<span class="mw-mlb-source">Iliad</span>' 
),
+                       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: '<b>HTML imgae caption</b>'
+                       },
+                       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 4297116..0577fc0 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 0000000..9e9e43f
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+ ( 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 ) );

-- 
To view, visit https://gerrit.wikimedia.org/r/119434
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I5bcd8e2171f45c70736b7a7cfc695134269ed12d
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/extensions/MultimediaViewer
Gerrit-Branch: master
Gerrit-Owner: GergÅ‘ Tisza <[email protected]>
Gerrit-Reviewer: GergÅ‘ Tisza <[email protected]>
Gerrit-Reviewer: Gilles <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to