Esanders has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/324232

Change subject: Use upstream media search widget from core
......................................................................

Use upstream media search widget from core

Bug: T140166
Depends-On: If53ef7f4b62c7a5d4da565c14dd2a353778694e5
Change-Id: I28845e9c143e78d3a377f067606c62da212e3bf1
---
M .jsduck/categories.json
M .jsduck/external.js
M .jsduck/mw-categories.json
M extension.json
D modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js
D modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js
D modules/ve-mw/dm/models/ve.dm.MWMediaSearchProvider.js
D modules/ve-mw/dm/models/ve.dm.MWMediaSearchQueue.js
M modules/ve-mw/tests/browser/features/support/pages/visual_editor_page.rb
M modules/ve-mw/ui/dialogs/ve.ui.MWGalleryDialog.js
M modules/ve-mw/ui/dialogs/ve.ui.MWMediaDialog.js
M modules/ve-mw/ui/styles/dialogs/ve.ui.MWMediaDialog.css
D modules/ve-mw/ui/styles/widgets/ve.ui.MWMediaResultWidget.css
D modules/ve-mw/ui/styles/widgets/ve.ui.MWMediaSearchWidget.css
D modules/ve-mw/ui/widgets/ve.ui.MWMediaResultWidget.js
D modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js
16 files changed, 23 insertions(+), 1,390 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor 
refs/changes/32/324232/1

diff --git a/.jsduck/categories.json b/.jsduck/categories.json
index 9655985..8e6d6a5 100644
--- a/.jsduck/categories.json
+++ b/.jsduck/categories.json
@@ -26,9 +26,7 @@
                                "name": "Data Model",
                                "classes": [
                                        "ve.dm.MW*Surface",
-                                       "ve.dm.MW*SurfaceFragment",
-                                       "ve.dm.MW*Provider",
-                                       "ve.dm.MW*Queue"
+                                       "ve.dm.MW*SurfaceFragment"
                                ]
                        },
                        {
@@ -60,7 +58,7 @@
                                "name": "MediaWiki",
                                "classes": [
                                        "mw.Title",
-                                       "mw.widgets.TitleInputWidget"
+                                       "mw.widgets.*Widget"
                                ]
                        }
                ]
diff --git a/.jsduck/external.js b/.jsduck/external.js
index e1286e2..a85d96b 100644
--- a/.jsduck/external.js
+++ b/.jsduck/external.js
@@ -7,3 +7,13 @@
  * @class mw.widgets.TitleInputWidget
  * @source 
<https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.widgets.TitleInputWidget>
  */
+
+/**
+ * @class mw.widgets.MediaSearchWidget
+ * @source 
<https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.widgets.MediaSearchWidget>
+ */
+
+/**
+ * @class mw.widgets.MediaResultWidget
+ * @source 
<https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.widgets.MediaResultWidget>
+ */
diff --git a/.jsduck/mw-categories.json b/.jsduck/mw-categories.json
index 734d4da..c3a3d52 100644
--- a/.jsduck/mw-categories.json
+++ b/.jsduck/mw-categories.json
@@ -26,9 +26,7 @@
                                "name": "Data Model",
                                "classes": [
                                        "ve.dm.MW*Surface",
-                                       "ve.dm.MW*SurfaceFragment",
-                                       "ve.dm.MW*Provider",
-                                       "ve.dm.MW*Queue"
+                                       "ve.dm.MW*SurfaceFragment"
                                ]
                        },
                        {
@@ -64,7 +62,7 @@
                                "name": "MediaWiki",
                                "classes": [
                                        "mw.Title",
-                                       "mw.widgets.TitleInputWidget"
+                                       "mw.widgets.*Widget"
                                ]
                        }
                ]
diff --git a/extension.json b/extension.json
index 2b65295..4dca6fd 100644
--- a/extension.json
+++ b/extension.json
@@ -1328,32 +1328,6 @@
                                "mobile"
                        ]
                },
-               "ext.visualEditor.mwmediasearch": {
-                       "scripts": [
-                               
"modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js",
-                               
"modules/ve-mw/dm/models/ve.dm.MWMediaSearchProvider.js",
-                               
"modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js",
-                               
"modules/ve-mw/dm/models/ve.dm.MWMediaSearchQueue.js",
-                               
"modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js",
-                               
"modules/ve-mw/ui/widgets/ve.ui.MWMediaResultWidget.js"
-                       ],
-                       "styles": [
-                               
"modules/ve-mw/ui/styles/widgets/ve.ui.MWMediaSearchWidget.css",
-                               
"modules/ve-mw/ui/styles/widgets/ve.ui.MWMediaResultWidget.css"
-                       ],
-                       "dependencies": [
-                               "ext.visualEditor.mwcore",
-                               "mediawiki.ForeignApi"
-                       ],
-                       "messages": [
-                               "visualeditor-dialog-media-noresults",
-                               "visualeditor-media-input-placeholder"
-                       ],
-                       "targets": [
-                               "desktop",
-                               "mobile"
-                       ]
-               },
                "ext.visualEditor.mwimage": {
                        "scripts": [
                                "modules/ve-mw/dm/models/ve.dm.MWImageModel.js",
@@ -1368,7 +1342,7 @@
                        ],
                        "dependencies": [
                                "ext.visualEditor.mwimage.core",
-                               "ext.visualEditor.mwmediasearch",
+                               "mediawiki.widgets.MediaSearch",
                                "oojs-ui.styles.icons-interactions",
                                "oojs-ui.styles.icons-media",
                                "oojs-ui.styles.icons-wikimedia",
@@ -1795,7 +1769,7 @@
                        ],
                        "dependencies": [
                                "ext.visualEditor.mwcore",
-                               "ext.visualEditor.mwmediasearch",
+                               "mediawiki.widgets.MediaSearch",
                                "oojs-ui.styles.icons-media",
                                "mediawiki.page.gallery.styles"
                        ],
diff --git a/modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js 
b/modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js
deleted file mode 100644
index 7f37105..0000000
--- a/modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js
+++ /dev/null
@@ -1,320 +0,0 @@
-/*!
- * VisualEditor DataModel MWMediaResourceProvider class.
- *
- * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-/**
- * MediaWiki media resource provider.
- *
- * @class
- * @extends ve.dm.APIResultsProvider
- *
- * @constructor
- * @param {string} apiurl The API url
- * @param {Object} [config] Configuration options
- * @cfg {string} [scriptDirUrl] The url of the API script
- */
-ve.dm.MWMediaResourceProvider = function VeDmMWMediaResourceProvider( apiurl, 
config ) {
-       config = config || {};
-
-       // Parent constructor
-       ve.dm.MWMediaResourceProvider.super.call( this, apiurl, config );
-
-       // Fetching configuration
-       this.scriptDirUrl = config.scriptDirUrl;
-       this.isLocal = config.local !== undefined;
-
-       if ( this.isLocal ) {
-               this.setAPIurl( mw.util.wikiScript( 'api' ) );
-       } else {
-               // If 'apiurl' is set, use that. Otherwise, build the url
-               // from scriptDirUrl and /api.php suffix
-               this.setAPIurl( this.getAPIurl() || ( this.scriptDirUrl + 
'/api.php' ) );
-       }
-
-       this.siteInfoPromise = null;
-       this.thumbSizes = [];
-       this.imageSizes = [];
-};
-
-/* Inheritance */
-OO.inheritClass( ve.dm.MWMediaResourceProvider, ve.dm.APIResultsProvider );
-
-/* Methods */
-
-/**
- * @inheritdoc
- */
-ve.dm.MWMediaResourceProvider.prototype.getStaticParams = function () {
-       return $.extend(
-               {},
-               // Parent method
-               
ve.dm.MWMediaResourceProvider.super.prototype.getStaticParams.call( this ),
-               {
-                       action: 'query',
-                       iiprop: 
'dimensions|url|mediatype|extmetadata|timestamp|user',
-                       iiextmetadatalanguage: this.getLang(),
-                       prop: 'imageinfo'
-               }
-       );
-};
-
-/**
- * Initialize the source and get the site info.
- *
- * Connect to the api url and retrieve the siteinfo parameters
- * that are required for fetching results.
- *
- * @return {jQuery.Promise} Promise that resolves when the class
- * properties are set.
- */
-ve.dm.MWMediaResourceProvider.prototype.loadSiteInfo = function () {
-       var provider = this;
-
-       if ( !this.siteInfoPromise ) {
-               this.siteInfoPromise = new mw.Api().get( {
-                       action: 'query',
-                       meta: 'siteinfo'
-               } )
-                       .then( function ( data ) {
-                               provider.setImageSizes( 
data.query.general.imagelimits || [] );
-                               provider.setThumbSizes( 
data.query.general.thumblimits || [] );
-                               provider.setUserParams( {
-                                       // Standard width per resource
-                                       iiurlwidth: provider.getStandardWidth()
-                               } );
-                       } );
-       }
-       return this.siteInfoPromise;
-};
-
-/**
- * Override parent method and get results from the source
- *
- * @param {number} [howMany] The number of items to pull from the API
- * @return {jQuery.Promise} Promise that is resolved into an array
- * of available results, or is rejected if no results are available.
- */
-ve.dm.MWMediaResourceProvider.prototype.getResults = function ( howMany ) {
-       var xhr,
-               aborted = false,
-               provider = this;
-
-       return this.loadSiteInfo()
-               .then( function () {
-                       if ( aborted ) {
-                               return $.Deferred().reject();
-                       }
-                       xhr = provider.fetchAPIresults( howMany );
-                       return xhr;
-               } )
-               .then(
-                       function ( results ) {
-                               if ( !results || results.length === 0 ) {
-                                       provider.toggleDepleted( true );
-                                       return [];
-                               }
-                               return results;
-                       },
-                       // Process failed, return an empty promise
-                       function () {
-                               provider.toggleDepleted( true );
-                               return $.Deferred().resolve( [] );
-                       }
-               )
-               .promise( { abort: function () {
-                       aborted = true;
-                       if ( xhr ) {
-                               xhr.abort();
-                       }
-               } } );
-};
-
-/**
- * Get continuation API data
- *
- * @param {number} howMany The number of results to retrieve
- * @return {Object} API request data
- */
-ve.dm.MWMediaResourceProvider.prototype.getContinueData = function () {
-       return {};
-};
-
-/**
- * Set continuation data for the next page
- *
- * @param {Object} continueData Continuation data
- */
-ve.dm.MWMediaResourceProvider.prototype.setContinue = function () {
-};
-
-/**
- * Sort the results
- *
- * @param {Object[]} results API results
- * @return {Object[]} Sorted results
- */
-ve.dm.MWMediaResourceProvider.prototype.sort = function ( results ) {
-       return results;
-};
-
-/**
- * Call the API for search results.
- *
- * @param {number} howMany The number of results to retrieve
- * @return {jQuery.Promise} Promise that resolves with an array of objects 
that contain
- *  the fetched data.
- */
-ve.dm.MWMediaResourceProvider.prototype.fetchAPIresults = function ( howMany ) 
{
-       var xhr, api,
-               provider = this;
-
-       if ( !this.isValid() ) {
-               return $.Deferred().reject().promise( { abort: $.noop } );
-       }
-
-       api = this.isLocal ? new mw.Api() : new mw.ForeignApi( 
this.getAPIurl(), { anonymous: true } );
-       xhr = api.get( $.extend( {}, this.getStaticParams(), 
this.getUserParams(), this.getContinueData( howMany ) ) );
-       return xhr
-               .then( function ( data ) {
-                       var page, newObj, raw,
-                               results = [];
-
-                       if ( data.error ) {
-                               provider.toggleDepleted( true );
-                               return [];
-                       }
-
-                       if ( data.continue ) {
-                               // Update the offset for next time
-                               provider.setContinue( data.continue );
-                       } else {
-                               // This is the last available set of results. 
Mark as depleted!
-                               provider.toggleDepleted( true );
-                       }
-
-                       // If the source returned no results, it will not have a
-                       // query property
-                       if ( data.query ) {
-                               raw = data.query.pages;
-                               if ( raw ) {
-                                       // Strip away the page ids
-                                       for ( page in raw ) {
-                                               if ( !raw[ page ].imageinfo ) {
-                                                       // The search may give 
us pages that belong to the File:
-                                                       // namespace but have 
no files in them, either because
-                                                       // they were deleted or 
imported wrongly, or just started
-                                                       // as pages. In that 
case, the response will not include
-                                                       // imageinfo. Skip 
those files.
-                                                       continue;
-                                               }
-                                               newObj = raw[ page ].imageinfo[ 
0 ];
-                                               newObj.title = raw[ page 
].title;
-                                               newObj.index = raw[ page 
].index;
-                                               results.push( newObj );
-                                       }
-                               }
-                       }
-                       return provider.sort( results );
-               } )
-               .promise( { abort: xhr.abort } );
-};
-
-/**
- * Set name
- *
- * @param {string} name
- */
-ve.dm.MWMediaResourceProvider.prototype.setName = function ( name ) {
-       this.name = name;
-};
-
-/**
- * Get name
- *
- * @return {string} name
- */
-ve.dm.MWMediaResourceProvider.prototype.getName = function () {
-       return this.name;
-};
-
-/**
- * Get standard width, based on the provider source's thumb sizes.
- *
- * @return {number|undefined} fetchWidth
- */
-ve.dm.MWMediaResourceProvider.prototype.getStandardWidth = function () {
-       return ( this.thumbSizes && this.thumbSizes[ this.thumbSizes.length - 1 
] ) ||
-               ( this.imageSizes && this.imageSizes[ 0 ] ) ||
-               // Fall back on a number
-               300;
-};
-
-/**
- * Get prop
- *
- * @return {string} prop
- */
-ve.dm.MWMediaResourceProvider.prototype.getFetchProp = function () {
-       return this.fetchProp;
-};
-
-/**
- * Set prop
- *
- * @param {string} prop
- */
-ve.dm.MWMediaResourceProvider.prototype.setFetchProp = function ( prop ) {
-       this.fetchProp = prop;
-};
-
-/**
- * Set thumb sizes
- *
- * @param {number[]} sizes Available thumbnail sizes
- */
-ve.dm.MWMediaResourceProvider.prototype.setThumbSizes = function ( sizes ) {
-       this.thumbSizes = sizes;
-};
-
-/**
- * Set image sizes
- *
- * @param {number[]} sizes Available image sizes
- */
-ve.dm.MWMediaResourceProvider.prototype.setImageSizes = function ( sizes ) {
-       this.imageSizes = sizes;
-};
-
-/**
- * Get thumb sizes
- *
- * @return {number[]} sizes Available thumbnail sizes
- */
-ve.dm.MWMediaResourceProvider.prototype.getThumbSizes = function () {
-       return this.thumbSizes;
-};
-
-/**
- * Get image sizes
- *
- * @return {number[]} sizes Available image sizes
- */
-ve.dm.MWMediaResourceProvider.prototype.getImageSizes = function () {
-       return this.imageSizes;
-};
-
-/**
- * Check if this source is valid.
- *
- * @return {boolean} Source is valid
- */
-ve.dm.MWMediaResourceProvider.prototype.isValid = function () {
-       return this.isLocal ||
-               // If we don't have either 'apiurl' or 'scriptDirUrl'
-               // the source is invalid, and we will skip it
-               this.apiurl !== undefined ||
-               this.scriptDirUrl !== undefined;
-};
diff --git a/modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js 
b/modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js
deleted file mode 100644
index 51b9761..0000000
--- a/modules/ve-mw/dm/models/ve.dm.MWMediaResourceQueue.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*!
- * VisualEditor DataModel MWMediaResourceQueue class.
- *
- * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-/**
- * MediaWiki media resource queue.
- *
- * @class
- * @extends ve.dm.APIResultsQueue
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {number} maxHeight The maximum height of the media, used in the
- *  search call to the API.
- */
-ve.dm.MWMediaResourceQueue = function VeDmMWMediaResourceQueue( config ) {
-       config = config || {};
-
-       // Parent constructor
-       ve.dm.MWMediaResourceQueue.super.call( this, config );
-
-       this.maxHeight = config.maxHeight || 200;
-};
-
-/* Inheritance */
-OO.inheritClass( ve.dm.MWMediaResourceQueue, ve.dm.APIResultsQueue );
-
-/**
- * Fetch the file repos.
- *
- * @return {jQuery.Promise} Promise that resolves when the resources are set up
- */
-ve.dm.MWMediaResourceQueue.prototype.getFileRepos = function () {
-       var defaultSource = [ {
-               url: mw.util.wikiScript( 'api' ),
-               local: ''
-       } ];
-
-       if ( !this.fileRepoPromise ) {
-               this.fileRepoPromise = new mw.Api().get( {
-                       action: 'query',
-                       meta: 'filerepoinfo'
-               } ).then(
-                       function ( resp ) {
-                               return resp.query && resp.query.repos || 
defaultSource;
-                       },
-                       function () {
-                               return $.Deferred().resolve( defaultSource );
-                       }
-               );
-       }
-
-       return this.fileRepoPromise;
-};
-
-/**
- * Get image maximum height
- *
- * @return {string} Image max height
- */
-ve.dm.MWMediaResourceQueue.prototype.getMaxHeight = function () {
-       return this.maxHeight;
-};
diff --git a/modules/ve-mw/dm/models/ve.dm.MWMediaSearchProvider.js 
b/modules/ve-mw/dm/models/ve.dm.MWMediaSearchProvider.js
deleted file mode 100644
index 822bfac..0000000
--- a/modules/ve-mw/dm/models/ve.dm.MWMediaSearchProvider.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/*!
- * VisualEditor DataModel MWMediaSearchProvider class.
- *
- * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-/**
- * MediaWiki media search provider.
- *
- * @class
- * @extends ve.dm.MWMediaResourceProvider
- *
- * @constructor
- * @param {string} apiurl The API url
- * @param {Object} [config] Configuration options
- */
-ve.dm.MWMediaSearchProvider = function VeDmMWMediaSearchProvider( apiurl, 
config ) {
-       config = config || {};
-
-       config.staticParams = $.extend( {
-               generator: 'search',
-               gsrnamespace: mw.config.get( 'wgNamespaceIds' ).file
-       }, config.staticParams );
-
-       // Parent constructor
-       ve.dm.MWMediaSearchProvider.super.call( this, apiurl, config );
-};
-
-/* Inheritance */
-OO.inheritClass( ve.dm.MWMediaSearchProvider, ve.dm.MWMediaResourceProvider );
-
-/* Methods */
-
-/**
- * @inheritdoc
- */
-ve.dm.MWMediaSearchProvider.prototype.getContinueData = function ( howMany ) {
-       return {
-               gsroffset: this.getOffset(),
-               gsrlimit: howMany || this.getDefaultFetchLimit()
-       };
-};
-
-/**
- * @inheritdoc
- */
-ve.dm.MWMediaSearchProvider.prototype.setContinue = function ( continueData ) {
-       // Update the offset for next time
-       this.setOffset( continueData.gsroffset );
-};
-
-/**
- * @inheritdoc
- */
-ve.dm.MWMediaSearchProvider.prototype.sort = function ( results ) {
-       return results.sort( function ( a, b ) {
-               return a.index - b.index;
-       } );
-};
-
-/**
- * @inheritdoc
- */
-ve.dm.MWMediaSearchProvider.prototype.isValid = function () {
-       return this.getUserParams().gsrsearch && 
ve.dm.MWMediaSearchProvider.super.prototype.isValid.call( this );
-};
diff --git a/modules/ve-mw/dm/models/ve.dm.MWMediaSearchQueue.js 
b/modules/ve-mw/dm/models/ve.dm.MWMediaSearchQueue.js
deleted file mode 100644
index 992312c..0000000
--- a/modules/ve-mw/dm/models/ve.dm.MWMediaSearchQueue.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/*!
- * VisualEditor DataModel MWMediaSearchQueue class.
- *
- * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-/**
- * MediaWiki media resource queue.
- *
- * @class
- * @extends ve.dm.MWMediaResourceQueue
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {number} maxHeight The maximum height of the media, used in the
- *  search call to the API.
- */
-ve.dm.MWMediaSearchQueue = function VeDmMWMediaSearchQueue( config ) {
-       config = config || {};
-
-       // Parent constructor
-       ve.dm.MWMediaSearchQueue.super.call( this, config );
-
-       this.searchQuery = '';
-};
-
-/* Inheritance */
-OO.inheritClass( ve.dm.MWMediaSearchQueue, ve.dm.MWMediaResourceQueue );
-
-/**
- * Override parent method to set up the providers according to
- * the file repos
- *
- * @return {jQuery.Promise} Promise that resolves when the resources are set up
- */
-ve.dm.MWMediaSearchQueue.prototype.setup = function () {
-       var i, len,
-               queue = this;
-
-       return this.getFileRepos().then( function ( sources ) {
-               if ( queue.providers.length === 0 ) {
-                       // Set up the providers
-                       for ( i = 0, len = sources.length; i < len; i++ ) {
-                               queue.providers.push( new 
ve.dm.MWMediaSearchProvider(
-                                       sources[ i ].apiurl,
-                                       {
-                                               name: sources[ i ].name,
-                                               local: sources[ i ].local,
-                                               scriptDirUrl: sources[ i 
].scriptDirUrl,
-                                               userParams: {
-                                                       gsrsearch: 
queue.getSearchQuery()
-                                               },
-                                               staticParams: {
-                                                       iiurlheight: 
queue.getMaxHeight()
-                                               }
-                                       } )
-                               );
-                       }
-               }
-       } );
-};
-
-/**
- * Set the search query
- *
- * @param {string} searchQuery API search query
- */
-ve.dm.MWMediaSearchQueue.prototype.setSearchQuery = function ( searchQuery ) {
-       this.setParams( { gsrsearch: searchQuery } );
-};
-
-/**
- * Get the search query
- *
- * @return {string} API search query
- */
-ve.dm.MWMediaSearchQueue.prototype.getSearchQuery = function () {
-       return this.getParams().gsrsearch;
-};
diff --git 
a/modules/ve-mw/tests/browser/features/support/pages/visual_editor_page.rb 
b/modules/ve-mw/tests/browser/features/support/pages/visual_editor_page.rb
index 7e0b4cc..a1a6912 100644
--- a/modules/ve-mw/tests/browser/features/support/pages/visual_editor_page.rb
+++ b/modules/ve-mw/tests/browser/features/support/pages/visual_editor_page.rb
@@ -102,7 +102,7 @@
   figure(:media_image, index: 0)
   span(:media_insert_menu, class: 'oo-ui-tool-name-media')
   text_field(:media_search, css: 'div.oo-ui-textInputWidget > input')
-  div(:media_select, class: 've-ui-mwMediaResultWidget-overlay')
+  div(:media_select, class: 'mw-widget-mediaResultWidget-overlay')
   div(:medium_dialog, class: 'oo-ui-window oo-ui-dialog oo-ui-dialog-open 
oo-ui-dialog-medium')
   span(:use_image_button, css: '.oo-ui-processDialog-actions-primary > 
div:nth-child(1) > a:nth-child(1) > span:nth-child(2)')
   span(:media_insert_button, css: '.oo-ui-flaggedElement-constructive > 
a:nth-child(1) > span:nth-child(2)')
diff --git a/modules/ve-mw/ui/dialogs/ve.ui.MWGalleryDialog.js 
b/modules/ve-mw/ui/dialogs/ve.ui.MWGalleryDialog.js
index ddb0506..cc52850 100644
--- a/modules/ve-mw/ui/dialogs/ve.ui.MWGalleryDialog.js
+++ b/modules/ve-mw/ui/dialogs/ve.ui.MWGalleryDialog.js
@@ -122,7 +122,7 @@
        } );
 
        // Search panel
-       this.searchWidget = new ve.ui.MWMediaSearchWidget();
+       this.searchWidget = new mw.widgets.MediaSearchWidget();
 
        // Options card
 
@@ -477,7 +477,7 @@
 /**
  * Handle search results choose event.
  *
- * @param {ve.ui.MWMediaResultWidget} item Chosen item
+ * @param {mw.widgets.MediaResultWidget} item Chosen item
  */
 ve.ui.MWGalleryDialog.prototype.onSearchResultsChoose = function ( item ) {
        var title = mw.Title.newFromText( item.getData().title 
).getPrefixedText();
diff --git a/modules/ve-mw/ui/dialogs/ve.ui.MWMediaDialog.js 
b/modules/ve-mw/ui/dialogs/ve.ui.MWMediaDialog.js
index 23272f5..a7e4ba1 100644
--- a/modules/ve-mw/ui/dialogs/ve.ui.MWMediaDialog.js
+++ b/modules/ve-mw/ui/dialogs/ve.ui.MWMediaDialog.js
@@ -227,7 +227,7 @@
                } )
        ] );
 
-       this.search = new ve.ui.MWMediaSearchWidget();
+       this.search = new mw.widgets.MediaSearchWidget();
 
        // Define fieldsets for image settings
 
@@ -817,7 +817,7 @@
 /**
  * Handle search results choose event.
  *
- * @param {ve.ui.MWMediaResultWidget} item Chosen item
+ * @param {mw.widgets.MediaResultWidget} item Chosen item
  */
 ve.ui.MWMediaDialog.prototype.onSearchResultsChoose = function ( item ) {
        this.chooseImageInfo( item.getData() );
@@ -840,7 +840,7 @@
 /**
  * Handle new image being chosen.
  *
- * @param {ve.ui.MWMediaResultWidget|null} item Selected item
+ * @param {mw.widgets.MediaResultWidget|null} item Selected item
  */
 ve.ui.MWMediaDialog.prototype.confirmSelectedImage = function () {
        var title, imageTitleText,
diff --git a/modules/ve-mw/ui/styles/dialogs/ve.ui.MWMediaDialog.css 
b/modules/ve-mw/ui/styles/dialogs/ve.ui.MWMediaDialog.css
index c5b8e24..39019db 100644
--- a/modules/ve-mw/ui/styles/dialogs/ve.ui.MWMediaDialog.css
+++ b/modules/ve-mw/ui/styles/dialogs/ve.ui.MWMediaDialog.css
@@ -64,7 +64,7 @@
        width: auto;
 }
 
-.ve-ui-mwMediaSearchWidget .oo-ui-searchWidget-results {
+.mw-widget-mediaSearchWidget .oo-ui-searchWidget-results {
        padding-right: 0;
 }
 
diff --git a/modules/ve-mw/ui/styles/widgets/ve.ui.MWMediaResultWidget.css 
b/modules/ve-mw/ui/styles/widgets/ve.ui.MWMediaResultWidget.css
deleted file mode 100644
index befce46..0000000
--- a/modules/ve-mw/ui/styles/widgets/ve.ui.MWMediaResultWidget.css
+++ /dev/null
@@ -1,97 +0,0 @@
-/*!
- * VisualEditor MediaWiki UserInterface MWMediaResultWidget styles.
- *
- * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-.ve-ui-mwMediaResultWidget {
-       display: inline-block;
-       position: relative;
-       padding: 0;
-       margin: 2px;
-       overflow: hidden;
-       box-sizing: border-box;
-       text-align: center;
-}
-
-.ve-ui.mwMediaResultWidget-eol:after {
-       content: '.';
-       display: block;
-       height: 0;
-       clear: both;
-       visibility: hidden;
-}
-
-.ve-ui-mwMediaResultWidget-error {
-       background-color: #f3f3f3;
-}
-
-.ve-ui-mwMediaResultWidget-thumbnail {
-       opacity: 0;
-       display: inline-block;
-       /* stylelint-disable no-unsupported-browser-features */
-       -webkit-transition: opacity 400ms;
-       -moz-transition: opacity 400ms;
-       transition: opacity 400ms;
-       /* stylelint-enable no-unsupported-browser-features */
-}
-
-.ve-ui-mwMediaResultWidget-done .ve-ui-mwMediaResultWidget-thumbnail,
-.ve-ui-mwMediaResultWidget-error .ve-ui-mwMediaResultWidget-thumbnail {
-       opacity: 1;
-}
-
-.ve-ui-mwMediaResultWidget-crop {
-       background-size: cover; /* stylelint-disable-line 
no-unsupported-browser-features */
-       background-position: center center;
-}
-
-.ve-ui-mwMediaResultWidget-overlay {
-       position: absolute;
-       top: 0;
-       bottom: 0;
-       left: 0;
-       right: 0;
-       box-shadow: inset 0 0 0 1px #ccc;
-}
-
-.ve-ui-mwMediaResultWidget.oo-ui-optionWidget-highlighted,
-.ve-ui-mwMediaResultWidget.oo-ui-optionWidget-selected {
-       box-shadow: 0 0 0.3em #a7dcff, 0 0 0 #fff;
-}
-
-.ve-ui-mwMediaResultWidget-error .ve-ui-mwMediaResultWidget-thumbnail {
-       /* @embed */
-       background-image: url( images/broken-image.png );
-       background-size: auto; /* stylelint-disable-line 
no-unsupported-browser-features */
-       background-position: center center;
-       background-repeat: no-repeat;
-}
-
-.ve-ui-mwMediaResultWidget .ve-ui-mwMediaResultWidget-nameLabel {
-       position: absolute;
-       bottom: 0;
-       left: 0;
-       right: 0;
-       overflow: hidden;
-       padding: 0.5em;
-       color: #fff;
-       text-shadow: 1px 1px #000; /* stylelint-disable-line 
no-unsupported-browser-features */
-       line-height: 1.125em;
-       background-color: rgba( 0, 0, 0, 0.5 );
-       text-overflow: ellipsis;
-       text-align: left;
-}
-
-.ve-ui-mwMediaResultWidget.oo-ui-optionWidget-highlighted 
.ve-ui-mwMediaResultWidget-nameLabel {
-       background-color: rgba( 0, 0, 0, 0.75 );
-}
-
-.ve-ui-mwMediaResultWidget.oo-ui-optionWidget-selected 
.ve-ui-mwMediaResultWidget-nameLabel {
-       background-color: #000;
-}
-
-.ve-ui-mwMediaSearchWidget-noresults {
-       padding-top: 1em;
-}
diff --git a/modules/ve-mw/ui/styles/widgets/ve.ui.MWMediaSearchWidget.css 
b/modules/ve-mw/ui/styles/widgets/ve.ui.MWMediaSearchWidget.css
deleted file mode 100644
index 4998615..0000000
--- a/modules/ve-mw/ui/styles/widgets/ve.ui.MWMediaSearchWidget.css
+++ /dev/null
@@ -1,10 +0,0 @@
-/*!
- * VisualEditor MediaWiki UserInterface MWMediaDialog styles.
- *
- * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-.ve-ui-mwMediaSearchWidget .oo-ui-searchWidget-query .oo-ui-inputWidget {
-       max-width: 100em;
-}
diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWMediaResultWidget.js 
b/modules/ve-mw/ui/widgets/ve.ui.MWMediaResultWidget.js
deleted file mode 100644
index c3424f7..0000000
--- a/modules/ve-mw/ui/widgets/ve.ui.MWMediaResultWidget.js
+++ /dev/null
@@ -1,252 +0,0 @@
-/*!
- * VisualEditor UserInterface MWMediaResultWidget class.
- *
- * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-/**
- * Creates an ve.ui.MWMediaResultWidget object.
- *
- * @class
- * @extends OO.ui.OptionWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {number} [rowHeight] Height of the row this result is part of
- * @cfg {number} [maxRowWidth] A limit for the width of the row this
- *  result is a part of.
- * @cfg {number} [minWidth] Minimum width for the result
- * @cfg {number} [maxWidth] Maximum width for the result
- */
-ve.ui.MWMediaResultWidget = function VeUiMWMediaResultWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       ve.ui.MWMediaResultWidget.super.call( this, config );
-
-       // Properties
-       this.setRowHeight( config.rowHeight || 150 );
-       this.maxRowWidth = config.maxRowWidth || 500;
-       this.minWidth = config.minWidth || this.maxRowWidth / 5;
-       this.maxWidth = config.maxWidth || this.maxRowWidth * 2 / 3;
-
-       this.imageDimensions = {};
-
-       this.isAudio = this.data.mediatype === 'AUDIO';
-
-       // Store the thumbnail url
-       this.thumbUrl = this.data.thumburl;
-       this.src = null;
-       this.row = null;
-
-       this.$thumb = $( '<img>' )
-               .addClass( 've-ui-mwMediaResultWidget-thumbnail' )
-               .on( {
-                       load: this.onThumbnailLoad.bind( this ),
-                       error: this.onThumbnailError.bind( this )
-               } );
-       this.$overlay = $( '<div>' )
-               .addClass( 've-ui-mwMediaResultWidget-overlay' );
-
-       this.calculateSizing( this.data );
-
-       // Get wiki default thumbnail size
-       this.defaultThumbSize = mw.config.get( 'wgVisualEditorConfig' )
-               .defaultUserOptions.defaultthumbsize;
-
-       // Initialization
-       this.setLabel( new mw.Title( this.data.title ).getNameText() );
-       this.$label.addClass( 've-ui-mwMediaResultWidget-nameLabel' );
-
-       this.$element
-               .addClass( 've-ui-mwMediaResultWidget ve-ui-texture-pending' )
-               .prepend( this.$thumb, this.$overlay );
-};
-
-/* Inheritance */
-
-OO.inheritClass( ve.ui.MWMediaResultWidget, OO.ui.OptionWidget );
-
-/* Methods */
-/** */
-ve.ui.MWMediaResultWidget.prototype.onThumbnailLoad = function () {
-       this.$thumb.first().addClass( 've-ui-texture-transparency' );
-       this.$element
-               .addClass( 've-ui-mwMediaResultWidget-done' )
-               .removeClass( 've-ui-texture-pending' );
-};
-
-/** */
-ve.ui.MWMediaResultWidget.prototype.onThumbnailError = function () {
-       this.$thumb.last()
-               .css( 'background-image', '' )
-               .addClass( 've-ui-texture-alert' );
-       this.$element
-               .addClass( 've-ui-mwMediaResultWidget-error' )
-               .removeClass( 've-ui-texture-pending' );
-};
-
-/**
- * Resize the thumbnail and wrapper according to row height and bounding 
boxes, if given.
- *
- * @param {Object} originalDimensions Original image dimensions with width and 
height values
- * @param {Object} [boundingBox] Specific bounding box, if supplied
- */
-ve.ui.MWMediaResultWidget.prototype.calculateSizing = function ( 
originalDimensions, boundingBox ) {
-       var wrapperPadding,
-               imageDimensions = {};
-
-       boundingBox = boundingBox || {};
-
-       if ( this.isAudio ) {
-               // HACK: We are getting the wrong information from the
-               // API about audio files. Set their thumbnail to square 120px
-               imageDimensions = {
-                       width: 120,
-                       height: 120
-               };
-       } else {
-               // Get the image within the bounding box
-               imageDimensions = ve.dm.MWImageNode.static.resizeToBoundingBox(
-                       // Image original dimensions
-                       {
-                               width: originalDimensions.width || 
originalDimensions.thumbwidth,
-                               height: originalDimensions.height || 
originalDimensions.thumbwidth
-                       },
-                       // Bounding box
-                       {
-                               width: boundingBox.width || 
this.getImageMaxWidth(),
-                               height: boundingBox.height || 
this.getRowHeight()
-                       }
-               );
-       }
-       this.imageDimensions = imageDimensions;
-       // Set the thumbnail size
-       this.$thumb.css( this.imageDimensions );
-
-       // Set the box size
-       wrapperPadding = this.calculateWrapperPadding( this.imageDimensions );
-       this.$element.css( wrapperPadding );
-};
-
-/**
- * Replace the empty .src attribute of the image with the
- * actual src.
- */
-ve.ui.MWMediaResultWidget.prototype.lazyLoad = function () {
-       if ( !this.hasSrc() ) {
-               this.src = this.thumbUrl;
-               this.$thumb.attr( 'src', this.thumbUrl );
-       }
-};
-
-/**
- * Retrieve the store dimensions object
- *
- * @return {Object} Thumb dimensions
- */
-ve.ui.MWMediaResultWidget.prototype.getDimensions = function () {
-       return this.dimensions;
-};
-
-/**
- * Resize thumbnail and element according to the resize factor
- *
- * @param {number} resizeFactor The resizing factor for the image
- */
-ve.ui.MWMediaResultWidget.prototype.resizeThumb = function ( resizeFactor ) {
-       var boundingBox,
-               imageOriginalWidth = this.imageDimensions.width,
-               wrapperWidth = this.$element.width();
-       // Set the new row height
-       this.setRowHeight( Math.ceil( this.getRowHeight() * resizeFactor ) );
-
-       boundingBox = {
-               width: Math.ceil( this.imageDimensions.width * resizeFactor ),
-               height: this.getRowHeight()
-       };
-
-       this.calculateSizing( this.data, boundingBox );
-
-       // We need to adjust the wrapper this time to fit the "perfect"
-       // dimensions, regardless of how small the image is
-       if ( imageOriginalWidth < wrapperWidth ) {
-               boundingBox.width = wrapperWidth * resizeFactor;
-       }
-       this.$element.css( this.calculateWrapperPadding( boundingBox ) );
-};
-
-/**
- * Adjust the wrapper padding for small images
- *
- * @param {Object} thumbDimensions Thumbnail dimensions
- * @return {Object} Css styling for the wrapper
- */
-ve.ui.MWMediaResultWidget.prototype.calculateWrapperPadding = function ( 
thumbDimensions ) {
-       var css = {
-               height: this.rowHeight,
-               width: thumbDimensions.width,
-               lineHeight: this.getRowHeight() + 'px'
-       };
-
-       // Check if the image is too thin so we can make a bit of space around 
it
-       if ( thumbDimensions.width < this.minWidth ) {
-               css.width = this.minWidth;
-       }
-
-       return css;
-};
-
-/**
- * Set the row height for all size calculations
- *
- * @return {number} rowHeight Row height
- */
-ve.ui.MWMediaResultWidget.prototype.getRowHeight = function () {
-       return this.rowHeight;
-};
-
-/**
- * Set the row height for all size calculations
- *
- * @param {number} rowHeight Row height
- */
-ve.ui.MWMediaResultWidget.prototype.setRowHeight = function ( rowHeight ) {
-       this.rowHeight = rowHeight;
-};
-
-ve.ui.MWMediaResultWidget.prototype.setImageMaxWidth = function ( width ) {
-       this.maxWidth = width;
-};
-ve.ui.MWMediaResultWidget.prototype.getImageMaxWidth = function () {
-       return this.maxWidth;
-};
-
-/**
- * Set the row this result is in.
- *
- * @param {number} row Row number
- */
-ve.ui.MWMediaResultWidget.prototype.setRow = function ( row ) {
-       this.row = row;
-};
-
-/**
- * Get the row this result is in.
- *
- * @return {number} row Row number
- */
-ve.ui.MWMediaResultWidget.prototype.getRow = function () {
-       return this.row;
-};
-
-/**
- * Check if the image has a src attribute already
- *
- * @return {boolean} Thumbnail has its source attribute set
- */
-ve.ui.MWMediaResultWidget.prototype.hasSrc = function () {
-       return !!this.src;
-};
diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js 
b/modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js
deleted file mode 100644
index b59f6f1..0000000
--- a/modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js
+++ /dev/null
@@ -1,455 +0,0 @@
-/*!
- * VisualEditor UserInterface MWMediaSearchWidget class.
- *
- * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-/**
- * Creates an ve.ui.MWMediaSearchWidget object.
- *
- * @class
- * @extends OO.ui.SearchWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @param {number} [size] Vertical size of thumbnails
- */
-ve.ui.MWMediaSearchWidget = function VeUiMWMediaSearchWidget( config ) {
-       // Configuration initialization
-       config = ve.extendObject( {
-               placeholder: ve.msg( 'visualeditor-media-input-placeholder' )
-       }, config );
-
-       // Parent constructor
-       ve.ui.MWMediaSearchWidget.super.call( this, config );
-
-       // Properties
-       this.providers = {};
-       this.lastQueryValue = '';
-       this.searchQueue = new ve.dm.MWMediaSearchQueue( {
-               limit: this.constructor.static.limit,
-               threshold: this.constructor.static.threshold
-       } );
-
-       this.queryTimeout = null;
-       this.itemCache = {};
-       this.promises = [];
-       this.lang = config.lang || 'en';
-       this.$panels = config.$panels;
-
-       // Masonry fit properties
-       this.rows = [];
-       this.rowHeight = config.rowHeight || 200;
-       this.layoutQueue = [];
-       this.numItems = 0;
-       this.currentItemCache = [];
-
-       this.resultsSize = {};
-
-       this.selected = null;
-
-       this.noItemsMessage = new OO.ui.LabelWidget( {
-               label: ve.msg( 'visualeditor-dialog-media-noresults' ),
-               classes: [ 've-ui-mwMediaSearchWidget-noresults' ]
-       } );
-       this.noItemsMessage.toggle( false );
-
-       // Events
-       this.$results.on( 'scroll', this.onResultsScroll.bind( this ) );
-       this.$query.append( this.noItemsMessage.$element );
-       this.results.connect( this, {
-               add: 'onResultsAdd',
-               remove: 'onResultsRemove'
-       } );
-
-       this.resizeHandler = ve.debounce( this.afterResultsResize.bind( this ), 
500 );
-
-       // Initialization
-       this.$element.addClass( 've-ui-mwMediaSearchWidget' );
-};
-
-/* Inheritance */
-
-OO.inheritClass( ve.ui.MWMediaSearchWidget, OO.ui.SearchWidget );
-
-/* Static properties */
-
-ve.ui.MWMediaSearchWidget.static.limit = 10;
-
-ve.ui.MWMediaSearchWidget.static.threshold = 5;
-
-/* Methods */
-
-/**
- * Respond to window resize and check if the result display should
- * be updated.
- */
-ve.ui.MWMediaSearchWidget.prototype.afterResultsResize = function () {
-       var items = this.currentItemCache;
-
-       if (
-               items.length > 0 &&
-               (
-                       this.resultsSize.width !== this.$results.width() ||
-                       this.resultsSize.height !== this.$results.height()
-               )
-       ) {
-               this.resetRows();
-               this.itemCache = {};
-               this.processQueueResults( items );
-               if ( this.results.getItems().length > 0 ) {
-                       this.lazyLoadResults();
-               }
-
-               // Cache the size
-               this.resultsSize = {
-                       width: this.$results.width(),
-                       height: this.$results.height()
-               };
-       }
-};
-
-/**
- * Teardown the widget; disconnect the window resize event.
- */
-ve.ui.MWMediaSearchWidget.prototype.teardown = function () {
-       $( window ).off( 'resize', this.resizeHandler );
-};
-
-/**
- * Setup the widget; activate the resize event.
- */
-ve.ui.MWMediaSearchWidget.prototype.setup = function () {
-       $( window ).on( 'resize', this.resizeHandler );
-};
-
-/**
- * Query all sources for media.
- *
- * @method
- */
-ve.ui.MWMediaSearchWidget.prototype.queryMediaQueue = function () {
-       var search = this,
-               value = this.getQueryValue();
-
-       if ( value === '' ) {
-               return;
-       }
-
-       this.query.pushPending();
-       search.noItemsMessage.toggle( false );
-
-       this.searchQueue.setSearchQuery( value );
-       this.searchQueue.get( this.constructor.static.limit )
-               .then( function ( items ) {
-                       if ( items.length > 0 ) {
-                               search.processQueueResults( items );
-                               search.currentItemCache = 
search.currentItemCache.concat( items );
-                       }
-
-                       search.query.popPending();
-                       search.noItemsMessage.toggle( 
search.results.getItems().length === 0 );
-                       if ( search.results.getItems().length > 0 ) {
-                               search.lazyLoadResults();
-                       }
-
-               } );
-};
-
-/**
- * Process the media queue giving more items
- *
- * @method
- * @param {Object[]} items Given items by the media queue
- */
-ve.ui.MWMediaSearchWidget.prototype.processQueueResults = function ( items ) {
-       var i, len, title,
-               resultWidgets = [],
-               inputSearchQuery = this.getQueryValue(),
-               queueSearchQuery = this.searchQueue.getSearchQuery();
-
-       if ( inputSearchQuery === '' || queueSearchQuery !== inputSearchQuery ) 
{
-               return;
-       }
-
-       for ( i = 0, len = items.length; i < len; i++ ) {
-               title = new mw.Title( items[ i ].title ).getMainText();
-               // Do not insert duplicates
-               if ( !Object.prototype.hasOwnProperty.call( this.itemCache, 
title ) ) {
-                       this.itemCache[ title ] = true;
-                       resultWidgets.push(
-                               new ve.ui.MWMediaResultWidget( {
-                                       data: items[ i ],
-                                       rowHeight: this.rowHeight,
-                                       maxWidth: this.results.$element.width() 
/ 3,
-                                       minWidth: 30,
-                                       rowWidth: this.results.$element.width()
-                               } )
-                       );
-               }
-       }
-       this.results.addItems( resultWidgets );
-
-};
-
-/**
- * Get the sanitized query value from the input
- *
- * @return {string} Query value
- */
-ve.ui.MWMediaSearchWidget.prototype.getQueryValue = function () {
-       var queryValue = this.query.getValue().trim();
-
-       if ( queryValue.match( 
ve.init.platform.getExternalLinkUrlProtocolsRegExp() ) ) {
-               queryValue = queryValue.match( /.+\/([^\/]+)/ )[ 1 ];
-       }
-       return queryValue;
-};
-
-/**
- * Handle search value change
- *
- * @param {string} value New value
- */
-ve.ui.MWMediaSearchWidget.prototype.onQueryChange = function () {
-       // Get the sanitized query value
-       var queryValue = this.getQueryValue();
-
-       if ( queryValue === this.lastQueryValue ) {
-               return;
-       }
-
-       // Parent method
-       ve.ui.MWMediaSearchWidget.super.prototype.onQueryChange.apply( this, 
arguments );
-
-       // Reset
-       this.itemCache = {};
-       this.currentItemCache = [];
-       this.resetRows();
-
-       // Empty the results queue
-       this.layoutQueue = [];
-
-       // Change resource queue query
-       this.searchQueue.setSearchQuery( queryValue );
-       this.lastQueryValue = queryValue;
-
-       // Queue
-       clearTimeout( this.queryTimeout );
-       this.queryTimeout = setTimeout( this.queryMediaQueue.bind( this ), 350 
);
-};
-
-/**
- * Handle results scroll events.
- *
- * @param {jQuery.Event} e Scroll event
- */
-ve.ui.MWMediaSearchWidget.prototype.onResultsScroll = function () {
-       var position = this.$results.scrollTop() + this.$results.outerHeight(),
-               threshold = this.results.$element.outerHeight() - 
this.rowHeight * 3;
-
-       // Check if we need to ask for more results
-       if ( !this.query.isPending() && position > threshold ) {
-               this.queryMediaQueue();
-       }
-
-       this.lazyLoadResults();
-};
-
-/**
- * Lazy-load the images that are visible.
- */
-ve.ui.MWMediaSearchWidget.prototype.lazyLoadResults = function () {
-       var i, elementTop,
-               items = this.results.getItems(),
-               resultsScrollTop = this.$results.scrollTop(),
-               position = resultsScrollTop + this.$results.outerHeight();
-
-       // Lazy-load results
-       for ( i = 0; i < items.length; i++ ) {
-               elementTop = items[ i ].$element.position().top;
-               if ( elementTop <= position && !items[ i ].hasSrc() ) {
-                       // Load the image
-                       items[ i ].lazyLoad();
-               }
-       }
-};
-
-/**
- * Reset all the rows; destroy the jQuery elements and reset
- * the rows array.
- */
-ve.ui.MWMediaSearchWidget.prototype.resetRows = function () {
-       var i, len;
-
-       for ( i = 0, len = this.rows.length; i < len; i++ ) {
-               this.rows[ i ].$element.remove();
-       }
-
-       this.rows = [];
-       this.itemCache = {};
-};
-
-/**
- * Find an available row at the end. Either we will need to create a new
- * row or use the last available row if it isn't full.
- *
- * @return {number} Row index
- */
-ve.ui.MWMediaSearchWidget.prototype.getAvailableRow = function () {
-       var row;
-
-       if ( this.rows.length === 0 ) {
-               row = 0;
-       } else {
-               row = this.rows.length - 1;
-       }
-
-       if ( !this.rows[ row ] ) {
-               // Create new row
-               this.rows[ row ] = {
-                       isFull: false,
-                       width: 0,
-                       items: [],
-                       $element: $( '<div>' )
-                               .addClass( 've-ui-mwMediaResultWidget-row' )
-                               .css( {
-                                       overflow: 'hidden'
-                               } )
-                               .data( 'row', row )
-                               .attr( 'data-full', false )
-               };
-               // Append to results
-               this.results.$element.append( this.rows[ row ].$element );
-       } else if ( this.rows[ row ].isFull ) {
-               row++;
-               // Create new row
-               this.rows[ row ] = {
-                       isFull: false,
-                       width: 0,
-                       items: [],
-                       $element: $( '<div>' )
-                               .addClass( 've-ui-mwMediaResultWidget-row' )
-                               .css( {
-                                       overflow: 'hidden'
-                               } )
-                               .data( 'row', row )
-                               .attr( 'data-full', false )
-               };
-               // Append to results
-               this.results.$element.append( this.rows[ row ].$element );
-       }
-
-       return row;
-};
-
-/**
- * Respond to add results event in the results widget.
- * Override the way SelectWidget and GroupElement append the items
- * into the group so we can append them in groups of rows.
- *
- * @param {ve.ui.MWMediaResultWidget[]} items An array of item elements
- */
-ve.ui.MWMediaSearchWidget.prototype.onResultsAdd = function ( items ) {
-       var search = this;
-
-       // Add method to a queue; this queue will only run when the widget
-       // is visible
-       this.layoutQueue.push( function () {
-               var i, j, ilen, jlen, itemWidth, row, effectiveWidth,
-                       resizeFactor,
-                       maxRowWidth = search.results.$element.width() - 15;
-
-               // Go over the added items
-               row = search.getAvailableRow();
-               for ( i = 0, ilen = items.length; i < ilen; i++ ) {
-                       itemWidth = items[ i ].$element.outerWidth( true );
-
-                       // Add items to row until it is full
-                       if ( search.rows[ row ].width + itemWidth >= 
maxRowWidth ) {
-                               // Mark this row as full
-                               search.rows[ row ].isFull = true;
-                               search.rows[ row ].$element.attr( 'data-full', 
true );
-
-                               // Find the resize factor
-                               effectiveWidth = search.rows[ row ].width;
-                               resizeFactor = maxRowWidth / effectiveWidth;
-
-                               search.rows[ row ].$element.attr( 
'data-effectiveWidth', effectiveWidth );
-                               search.rows[ row ].$element.attr( 
'data-resizeFactor', resizeFactor );
-                               search.rows[ row ].$element.attr( 'data-row', 
row );
-
-                               // Resize all images in the row to fit the width
-                               for ( j = 0, jlen = search.rows[ row 
].items.length; j < jlen; j++ ) {
-                                       search.rows[ row ].items[ j 
].resizeThumb( resizeFactor );
-                               }
-
-                               // find another row
-                               row = search.getAvailableRow();
-                       }
-
-                       // Add the cumulative
-                       search.rows[ row ].width += itemWidth;
-
-                       // Store reference to the item and to the row
-                       search.rows[ row ].items.push( items[ i ] );
-                       items[ i ].setRow( row );
-
-                       // Append the item
-                       search.rows[ row ].$element.append( items[ i ].$element 
);
-               }
-
-               // If we have less than 4 rows, call for more images
-               if ( search.rows.length < 4 ) {
-                       search.queryMediaQueue();
-               }
-       } );
-       this.runLayoutQueue();
-};
-
-/**
- * Run layout methods from the queue only if the element is visible.
- */
-ve.ui.MWMediaSearchWidget.prototype.runLayoutQueue = function () {
-       var i, len;
-
-       if ( this.$element.is( ':visible' ) ) {
-               for ( i = 0, len = this.layoutQueue.length; i < len; i++ ) {
-                       this.layoutQueue.pop()();
-               }
-       }
-};
-
-/**
- * Respond to removing results event in the results widget.
- * Clear the relevant rows.
- *
- * @param {OO.ui.OptionWidget[]} items Removed items
- */
-ve.ui.MWMediaSearchWidget.prototype.onResultsRemove = function ( items ) {
-       if ( items.length > 0 ) {
-               // In the case of the media search widget, if any items are 
removed
-               // all are removed (new search)
-               this.resetRows();
-               this.currentItemCache = [];
-       }
-};
-
-/**
- * Set language for the search results.
- *
- * @param {string} lang Language
- */
-ve.ui.MWMediaSearchWidget.prototype.setLang = function ( lang ) {
-       this.lang = lang;
-};
-
-/**
- * Get language for the search results.
- *
- * @return {string} lang Language
- */
-ve.ui.MWMediaSearchWidget.prototype.getLang = function () {
-       return this.lang;
-};

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I28845e9c143e78d3a377f067606c62da212e3bf1
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <[email protected]>

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

Reply via email to