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

Change subject: Image insertion
......................................................................


Image insertion

Objective:

* Allow inserting images from local wiki and commons

Changes:

ve.init.mw.ViewPageTarget.js
* Add media insert button to toolbar

ve.init.mw.Platform.js
* Add getMediaSources method - defaults to local wiki and commons

ve.ui.MWMediaInsertDialog.js
* New dialog for inserting media
* Uses a media select widget and inserts block images

ve.ui.Dialog.css
* Added styling for media select widget in media insert dialog

ve.ui.Widget.css
* Added styles for media select widget and media select item widget

ve.ui.MWMediaInsertButtonTool.js
* New tool for inserting media

ve.ui.MediaSelectItemWidget.js
* New item widget for media select widgets

ve.ui.MediaSelectWidget.js
* New widget for searching for and selecting media items

ve.ui.TextInputWidget.js
* Added isPending method

VisualEditor.i18n.php
* New messages for media insert dialog

VisualEditor.php
* Added links to new files and messages

PhantomJS--

Change-Id: Ia803ff3ef518782ce76802d2dab7559686a1bb0a
---
M VisualEditor.i18n.php
M VisualEditor.php
M modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
M modules/ve/init/mw/ve.init.mw.Platform.js
A modules/ve/ui/dialogs/ve.ui.MWMediaInsertDialog.js
M modules/ve/ui/styles/ve.ui.Dialog.css
M modules/ve/ui/styles/ve.ui.Widget.css
A modules/ve/ui/tools/buttons/ve.ui.MWMediaInsertButtonTool.js
A modules/ve/ui/widgets/ve.ui.MWMediaSelectItemWidget.js
A modules/ve/ui/widgets/ve.ui.MWMediaSelectWidget.js
M modules/ve/ui/widgets/ve.ui.TextInputWidget.js
11 files changed, 599 insertions(+), 2 deletions(-)

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



diff --git a/VisualEditor.i18n.php b/VisualEditor.i18n.php
index a841979..015b015 100644
--- a/VisualEditor.i18n.php
+++ b/VisualEditor.i18n.php
@@ -30,6 +30,9 @@
        'visualeditor-dialog-media-title' => 'Media settings',
        'visualeditor-dialog-reference-title' => 'Reference',
        'visualeditor-dialog-template-title' => 'Template',
+       'visualeditor-dialog-media-insert-title' => 'Insert media',
+       'visualeditor-dialog-media-insert-button' => 'Insert media',
+       'visualeditor-media-input-placeholder' => 'Search for media',
        'visualeditor-dialog-action-apply' => 'Apply changes',
        'visualeditor-dialog-action-cancel' => 'Cancel',
        'visualeditor-dialog-action-close' => 'Close',
@@ -169,6 +172,8 @@
 
 See also:
 * {{msg-mw|Visualeditor-dialog-action-close}}',
+       'visualeditor-dialog-media-insert-title' => 'Media insert dialog title 
text',
+       'visualeditor-media-input-placeholder' => 'Place holder text for media 
search input',
        'visualeditor-dialog-action-cancel' => 'Used as button text.
 {{Identical|Cancel}}',
        'visualeditor-dialog-action-close' => 'Used as tooltip for the "Close" 
button.
diff --git a/VisualEditor.php b/VisualEditor.php
index 7b821f8..1ba644c 100644
--- a/VisualEditor.php
+++ b/VisualEditor.php
@@ -142,7 +142,6 @@
                        'mediawiki.Title',
                        'mediawiki.Uri',
                        'mediawiki.user',
-                       'mediawiki.util',
                        'mediawiki.notify',
                        'mediawiki.feedback',
                        'user.options',
@@ -209,6 +208,7 @@
                'dependencies' => array(
                        'oojs',
                        'unicodejs.wordbreak',
+                       'mediawiki.util',
                ),
        ),
        'ext.visualEditor.mediawiki' => $wgVisualEditorResourceTemplate + array(
@@ -564,8 +564,12 @@
                        've/ce/nodes/ve.ce.MWReferenceListNode.js',
                        've/ce/nodes/ve.ce.MWReferenceNode.js',
 
+                       've/ui/dialogs/ve.ui.MWMediaInsertDialog.js',
+                       've/ui/widgets/ve.ui.MWMediaSelectWidget.js',
+                       've/ui/widgets/ve.ui.MWMediaSelectItemWidget.js',
                        've/ui/tools/buttons/ve.ui.MWReferenceButtonTool.js',
                        've/ui/tools/buttons/ve.ui.MWTemplateButtonTool.js',
+                       've/ui/tools/buttons/ve.ui.MWMediaInsertButtonTool.js',
                        've/ui/dialogs/ve.ui.MWReferenceDialog.js',
                        've/ui/dialogs/ve.ui.MWTemplateDialog.js',
                ),
@@ -577,6 +581,11 @@
                        'visualeditor-dialogbutton-reference-tooltip',
                        'visualeditor-dialog-template-title',
                        'visualeditor-dialogbutton-template-tooltip',
+                       'visualeditor-dialog-meta-title',
+                       'visualeditor-dialogbutton-meta-tooltip',
+                       'visualeditor-dialog-media-insert-title',
+                       'visualeditor-dialog-media-insert-button',
+                       'visualeditor-media-input-placeholder',
                ),
        ),
        'ext.visualEditor.icons-raster' => $wgVisualEditorResourceTemplate + 
array(
diff --git a/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js 
b/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
index e16fce3..cab71c5 100644
--- a/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
+++ b/modules/ve/init/mw/targets/ve.init.mw.ViewPageTarget.js
@@ -169,7 +169,7 @@
 ve.init.mw.ViewPageTarget.static.toolbarTools = [
        { 'items': ['undo', 'redo'] },
        { 'items': ['mwFormat'] },
-       { 'items': ['bold', 'italic', 'mwLink', 'clear'] },
+       { 'items': ['bold', 'italic', 'mwLink', 'clear', 'mwMediaInsert'] },
        { 'items': ['number', 'bullet', 'outdent', 'indent'] }
 ];
 
diff --git a/modules/ve/init/mw/ve.init.mw.Platform.js 
b/modules/ve/init/mw/ve.init.mw.Platform.js
index 03f53aa..9ce63f2 100644
--- a/modules/ve/init/mw/ve.init.mw.Platform.js
+++ b/modules/ve/init/mw/ve.init.mw.Platform.js
@@ -23,6 +23,10 @@
        this.externalLinkUrlProtocolsRegExp = new RegExp( '^' + mw.config.get( 
'wgUrlProtocols' ) );
        this.modulesUrl = mw.config.get( 'wgExtensionAssetsPath' ) + 
'/VisualEditor/modules';
        this.parsedMessages = {};
+       this.mediaSources = [
+               { 'url': mw.util.wikiScript( 'api' ) },
+               { 'url': '//commons.wikimedia.org/w/api.php' }
+       ];
 };
 
 /* Inheritance */
@@ -130,6 +134,16 @@
        return mw.config.get( 'wgUserLanguage' );
 };
 
+/**
+ * Get a list of URLs to MediaWiki API entry points where media can be found.
+ *
+ * @method
+ * @returns {string[]} API URLs
+ */
+ve.init.mw.Platform.prototype.getMediaSources = function () {
+       return this.mediaSources;
+};
+
 /* Initialization */
 
 ve.init.platform = new ve.init.mw.Platform();
diff --git a/modules/ve/ui/dialogs/ve.ui.MWMediaInsertDialog.js 
b/modules/ve/ui/dialogs/ve.ui.MWMediaInsertDialog.js
new file mode 100644
index 0000000..5c25aad
--- /dev/null
+++ b/modules/ve/ui/dialogs/ve.ui.MWMediaInsertDialog.js
@@ -0,0 +1,103 @@
+/*!
+ * VisualEditor user interface MediaInsertDialog class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/*global mw */
+
+/**
+ * Document dialog.
+ *
+ * @class
+ * @abstract
+ * @extends ve.ui.Dialog
+ *
+ * @constructor
+ * @param {ve.ui.Surface} surface
+ * @param {Object} [config] Config options
+ */
+ve.ui.MWMediaInsertDialog = function VeUiMWMediaInsertDialog( surface, config 
) {
+       // Parent constructor
+       ve.ui.Dialog.call( this, surface, config );
+
+       // Properties
+       this.item = null;
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.MWMediaInsertDialog, ve.ui.Dialog );
+
+/* Static Properties */
+
+ve.ui.MWMediaInsertDialog.static.titleMessage = 
'visualeditor-dialog-media-insert-title';
+
+ve.ui.MWMediaInsertDialog.static.icon = 'picture';
+
+/* Methods */
+
+/**
+ * Handle media select events.
+ *
+ * @method
+ * @param {string} item Selected item
+ */
+ve.ui.MWMediaInsertDialog.prototype.onSelect = function ( item ) {
+       this.item = item;
+       this.applyButton.setDisabled( item === null );
+};
+
+/**
+ * Handle apply button click events.
+ *
+ * @method
+ */
+ve.ui.MWMediaInsertDialog.prototype.onApplyButtonClick = function () {
+       var info = this.item.imageinfo[0];
+
+       this.surface.getModel().getFragment().insertContent( [
+               {
+                       'type': 'mwBlockImage',
+                       'attributes': {
+                               'align': 'right',
+                               'href': info.descriptionurl,
+                               'src': info.thumburl,
+                               'width': info.thumbwidth,
+                               'height': info.thumbheight
+                       }
+               },
+               { 'type': '/mwBlockImage' }
+       ] );
+
+       // Parent method
+       ve.ui.Dialog.prototype.onApplyButtonClick.call( this );
+};
+
+/**
+ * Initialize frame contents.
+ *
+ * @method
+ */
+ve.ui.MWMediaInsertDialog.prototype.initialize = function () {
+       // Parent method
+       ve.ui.Dialog.prototype.initialize.call( this );
+
+       // Properties
+       this.media = new ve.ui.MWMediaSelectWidget( { '$$': this.frame.$$ } );
+
+       // Events
+       this.media.connect( this, { 'select': 'onSelect' } );
+
+       // Initialization
+       this.applyButton.setDisabled( true ).setLabel(
+               mw.msg( 'visualeditor-dialog-media-insert-button' )
+       );
+       this.media.$.addClass( 've-ui-mwMediaInsertDialog-select' );
+       this.$body.append( this.media.$ );
+};
+
+/* Registration */
+
+ve.ui.dialogFactory.register( 'mwMediaInsert', ve.ui.MWMediaInsertDialog );
diff --git a/modules/ve/ui/styles/ve.ui.Dialog.css 
b/modules/ve/ui/styles/ve.ui.Dialog.css
index 9cf17a6..244b8ae 100644
--- a/modules/ve/ui/styles/ve.ui.Dialog.css
+++ b/modules/ve/ui/styles/ve.ui.Dialog.css
@@ -186,3 +186,13 @@
 .ve-ui-dialog-meta-languages-table tr:nth-child(even) td {
        background: #F8F8F8;
 }
+
+/* ve.ui.MWMediaInsertDialog */
+
+.ve-ui-mwMediaInsertDialog-select {
+       position: absolute;
+       top: 0;
+       bottom: 0;
+       left: 0;
+       right: 0;
+}
diff --git a/modules/ve/ui/styles/ve.ui.Widget.css 
b/modules/ve/ui/styles/ve.ui.Widget.css
index 8a1e0db..75e787d 100644
--- a/modules/ve/ui/styles/ve.ui.Widget.css
+++ b/modules/ve/ui/styles/ve.ui.Widget.css
@@ -525,6 +525,104 @@
        padding: 0 0.125em 0.5em 0.125em;
 }
 
+/* ve.ui.MWMediaSelectWidget */
+
+.ve-ui-mwMediaSelectWidget-query {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+       height: 4em;
+       padding: 0 1em;
+       box-shadow: 0 0 0.5em rgba(0,0,0,0.2);
+}
+
+.ve-ui-mwMediaSelectWidget-query .ve-ui-textInputWidget {
+       width: 100%;
+       margin: 0.75em 0;
+}
+
+.ve-ui-mwMediaSelectWidget-results {
+       position: absolute;
+       top: 4em;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       padding: 1em;
+       overflow-x: hidden;
+       overflow-y: auto;
+       line-height: 0;
+}
+
+/* ve.ui.mwMediaSelectItemWidget */
+
+.ve-ui-mwMediaSelectItemWidget {
+       display: inline-block;
+       position: relative;
+       padding: 0;
+       margin: 2px;
+       overflow: hidden;
+}
+
+.ve-ui-mwMediaSelectItemWidget-error {
+       background-color: #f3f3f3;
+}
+
+.ve-ui-mwMediaSelectItemWidget-thumbnail {
+       position: absolute;
+       opacity: 0;
+       -webkit-transition: opacity 400ms;
+       -moz-transition: opacity 400ms;
+       -ms-transition: opacity 400ms;
+       -o-transition: opacity 400ms;
+       transition: opacity 400ms;
+}
+
+.ve-ui-mwMediaSelectItemWidget-done .ve-ui-mwMediaSelectItemWidget-thumbnail,
+.ve-ui-mwMediaSelectItemWidget-error .ve-ui-mwMediaSelectItemWidget-thumbnail {
+       opacity: 1;
+}
+
+.ve-ui-mwMediaSelectItemWidget-crop {
+       background-size: cover;
+       background-position: center center;
+}
+
+.ve-ui-mwMediaSelectItemWidget-overlay {
+       position: absolute;
+       top: 0;
+       bottom: 0;
+       left: 0;
+       right: 0;
+}
+
+.ve-ui-optionWidget-selected .ve-ui-mwMediaSelectItemWidget-overlay {
+       box-shadow: inset 0 0 0 1px rgba(0,0,0,0.5), inset 0 0 0 2px 
rgba(255,255,255,0.5);
+}
+
+.ve-ui-mwMediaSelectItemWidget-error .ve-ui-mwMediaSelectItemWidget-thumbnail {
+       /* @embed */
+       background-image: url(images/broken-image.png);
+       background-size: auto;
+       background-position: center center;
+       background-repeat: no-repeat;
+}
+
+.ve-ui-mwMediaSelectItemWidget .ve-ui-labeledElement-label {
+       position: absolute;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       overflow: hidden;
+       padding: 0.5em;
+       color: #fff;
+       text-shadow: 1px 1px #000;
+       font-size: 0.8em;
+       line-height: 1.125em;
+       background-color: rgba(0,0,0,0.5);
+       text-overflow: ellipsis;
+}
+
 /* RTL Definitions */
 
 /* @noflip */
diff --git a/modules/ve/ui/tools/buttons/ve.ui.MWMediaInsertButtonTool.js 
b/modules/ve/ui/tools/buttons/ve.ui.MWMediaInsertButtonTool.js
new file mode 100644
index 0000000..ed35287
--- /dev/null
+++ b/modules/ve/ui/tools/buttons/ve.ui.MWMediaInsertButtonTool.js
@@ -0,0 +1,39 @@
+/*!
+ * VisualEditor UserInterface MWMediaButtonTool class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * MediaWiki media insert button tool.
+ *
+ * @class
+ * @extends ve.ui.DialogButtonTool
+ *
+ * @constructor
+ * @param {ve.ui.Toolbar} toolbar
+ * @param {Object} [config] Config options
+ */
+ve.ui.MWMediaInsertButtonTool = function VeUiMWMediaButtonTool( toolbar, 
config ) {
+       // Parent constructor
+       ve.ui.DialogButtonTool.call( this, toolbar, config );
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.MWMediaInsertButtonTool, ve.ui.DialogButtonTool );
+
+/* Static Properties */
+
+ve.ui.MWMediaInsertButtonTool.static.name = 'mwMediaInsert';
+
+ve.ui.MWMediaInsertButtonTool.static.icon = 'picture';
+
+ve.ui.MWMediaInsertButtonTool.static.titleMessage = 
'visualeditor-dialogbutton-media-tooltip';
+
+ve.ui.MWMediaInsertButtonTool.static.dialog = 'mwMediaInsert';
+
+/* Registration */
+
+ve.ui.toolFactory.register( 'mwMediaInsert', ve.ui.MWMediaInsertButtonTool );
diff --git a/modules/ve/ui/widgets/ve.ui.MWMediaSelectItemWidget.js 
b/modules/ve/ui/widgets/ve.ui.MWMediaSelectItemWidget.js
new file mode 100755
index 0000000..f8ba9cc
--- /dev/null
+++ b/modules/ve/ui/widgets/ve.ui.MWMediaSelectItemWidget.js
@@ -0,0 +1,105 @@
+/*!
+ * VisualEditor UserInterface MWMediaSelectItemWidget class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/*global mw */
+
+/**
+ * Creates an ve.ui.MWMediaSelectItemWidget object.
+ *
+ * @class
+ * @extends ve.ui.OptionWidget
+ *
+ * @constructor
+ * @param {Mixed} data Item data
+ * @param {Object} [config] Config options
+ * @cfg {number} [size] Media thumbnail size
+ */
+ve.ui.MWMediaSelectItemWidget = function VeUiMWMediaSelectItemWidget( data, 
config ) {
+       // Configuration intialization
+       config = config || {};
+
+       // Parent constructor
+       ve.ui.OptionWidget.call( this, data, config );
+
+       // Properties
+       this.size = config.size || 150;
+       this.$thumb = this.buildThumbnail();
+       this.$overlay = this.$$( '<div>' );
+
+       // Initialization
+       this.setLabel( new mw.Title( this.data.title ).getNameText() );
+       this.$overlay.addClass( 've-ui-mwMediaSelectItemWidget-overlay' );
+       this.$
+               .addClass( 've-ui-mwMediaSelectItemWidget 
ve-ui-texture-pending' )
+               .css( { 'width': this.size, 'height': this.size } )
+               .prepend( this.$thumb, this.$overlay );
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.MWMediaSelectItemWidget, ve.ui.OptionWidget );
+
+/* Static Properties */
+
+ve.ui.MWMediaSelectItemWidget.static.highlightable = false;
+
+/* Methods */
+
+ve.ui.MWMediaSelectItemWidget.prototype.onThumbnailLoad = function () {
+       this.$thumb.first().addClass( 've-ui-texture-transparency' );
+       this.$
+               .addClass( 've-ui-mwMediaSelectItemWidget-done' )
+               .removeClass( 've-ui-texture-pending' );
+};
+
+ve.ui.MWMediaSelectItemWidget.prototype.onThumbnailError = function () {
+       this.$thumb.last()
+               .css( 'background-image', '' )
+               .addClass( 've-ui-texture-alert' );
+       this.$
+               .addClass( 've-ui-mwMediaSelectItemWidget-error' )
+               .removeClass( 've-ui-texture-pending' );
+};
+
+/**
+ * Build a thumbnail.
+ *
+ * @method
+ * @returns {jQuery} Thumbnail element
+ */
+ve.ui.MWMediaSelectItemWidget.prototype.buildThumbnail = function () {
+       var info = this.data.imageinfo[0],
+               image = new Image(),
+               $image = this.$$( image ),
+               $back = this.$$( '<div>' ),
+               $front = this.$$( '<div>' ),
+               $thumb = $back.add( $front );
+
+       // Preload image
+       $image
+               .load( ve.bind( this.onThumbnailLoad, this ) )
+               .error( ve.bind( this.onThumbnailError, this ) );
+       image.src = info.thumburl;
+
+       $thumb.addClass( 've-ui-mwMediaSelectItemWidget-thumbnail' );
+       $thumb.last().css( 'background-image', 'url(' + info.thumburl + ')' );
+       if ( info.width >= this.size && info.height >= this.size ) {
+               $front.addClass( 've-ui-mwMediaSelectItemWidget-crop' );
+               $thumb.css( { 'width': '100%', 'height': '100%' } );
+       } else {
+               $thumb.css( {
+                       'width': info.thumbwidth,
+                       'height': info.thumbheight,
+                       'left': '50%',
+                       'top': '50%',
+                       'margin-left': Math.round( -info.thumbwidth / 2 ),
+                       'margin-top': Math.round( -info.thumbheight / 2 )
+               } );
+       }
+
+       return $thumb;
+};
diff --git a/modules/ve/ui/widgets/ve.ui.MWMediaSelectWidget.js 
b/modules/ve/ui/widgets/ve.ui.MWMediaSelectWidget.js
new file mode 100755
index 0000000..422e19a
--- /dev/null
+++ b/modules/ve/ui/widgets/ve.ui.MWMediaSelectWidget.js
@@ -0,0 +1,204 @@
+/*!
+ * VisualEditor UserInterface MWMediaSelectWidget class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/*global mw*/
+
+/**
+ * Creates an ve.ui.MWMediaSelectWidget object.
+ *
+ * @class
+ * @extends ve.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Config options
+ * @param {number} [size] Vertical size of thumbnails
+ */
+ve.ui.MWMediaSelectWidget = function VeUiMWMediaSelectWidget( config ) {
+       // Configuration intialization
+       config = config || {};
+
+       // Parent constructor
+       ve.ui.Widget.call( this, config );
+
+       // Properties
+       this.input = new ve.ui.TextInputWidget( {
+               '$$': this.$$,
+               'placeholder': ve.msg( 'visualeditor-media-input-placeholder' ),
+               'value': mw.config.get( 'wgTitle' ),
+               'icon': 'search'
+       } );
+       this.select = new ve.ui.SelectWidget( { '$$': this.$$ } );
+       this.$query = this.$$( '<div>' );
+       this.$results = this.$$( '<div>' );
+
+       this.sources = ve.copyArray( ve.init.platform.getMediaSources() );
+       this.size = config.size || 150;
+       this.inputTimeout = null;
+       this.titles = {};
+       this.queryMediaSourcesCallback = ve.bind( this.queryMediaSources, this 
);
+
+       // Events
+       this.input.connect( this, { 'change': 'onInputChange' } );
+       this.select.connect( this, { 'select': 'onSelectSelect' } );
+       this.$results.on( 'scroll', ve.bind( this.onResultsScroll, this ) );
+
+       // Initialization
+       this.$query
+               .addClass( 've-ui-mwMediaSelectWidget-query' )
+               .append( this.input.$ );
+       this.$results
+               .addClass( 've-ui-mwMediaSelectWidget-results' )
+               .append( this.select.$ );
+       this.$
+               .addClass( 've-ui-mwMediaSelectWidget' )
+               .append( this.$results, this.$query );
+       this.queryMediaSources();
+};
+
+/* Inheritance */
+
+ve.inheritClass( ve.ui.MWMediaSelectWidget, ve.ui.Widget );
+
+/* Events */
+
+/**
+ * @event select
+ * @param {Object} item Media item info
+ */
+
+/* Methods */
+
+/**
+ * Handle select widget select events.
+ *
+ * @param {string} value New value
+ */
+ve.ui.MWMediaSelectWidget.prototype.onInputChange = function () {
+       var i, len;
+
+       if ( this.loading || this.input.getValue() === '' ) {
+               return;
+       }
+
+       // Reset
+       this.select.clearItems();
+       for ( i = 0, len = this.sources.length; i < len; i++ ) {
+               delete this.sources[i].gsroffset;
+       }
+
+       // Queue
+       clearTimeout( this.inputTimeout );
+       this.inputTimeout = setTimeout( this.queryMediaSourcesCallback, 100 );
+};
+
+/**
+ * Handle select widget select events.
+ *
+ * @param {ve.ui.MWMediaSelectItemWidget} item Selected item
+ * @emits select
+ */
+ve.ui.MWMediaSelectWidget.prototype.onSelectSelect = function ( item ) {
+       this.emit( 'select', item ? item.getData() : null );
+};
+
+/**
+ * Handle results scroll events.
+ *
+ * @param {jQuery.Event} e Scroll event
+ */
+ve.ui.MWMediaSelectWidget.prototype.onResultsScroll = function () {
+       var position = this.$results.scrollTop() + this.$results.outerHeight(),
+               threshold = this.select.$.outerHeight() - this.size;
+       if ( !this.input.isPending() && position > threshold ) {
+               this.queryMediaSources();
+       }
+};
+
+/**
+ * Query all sources for media.
+ *
+ * @method
+ */
+ve.ui.MWMediaSelectWidget.prototype.queryMediaSources = function () {
+       var i, len, source;
+
+       for ( i = 0, len = this.sources.length; i < len; i++ ) {
+               source = this.sources[i];
+               if ( source.request ) {
+                       source.request.abort();
+               }
+               if ( !source.gsroffset ) {
+                       source.gsroffset = 0;
+               }
+               this.input.pushPending();
+               source.request = $.ajax( {
+                       'url': source.url,
+                       'data': {
+                               'format': 'json',
+                               'action': 'query',
+                               'generator': 'search',
+                               'gsrsearch': this.input.getValue(),
+                               'gsrnamespace': 6,
+                               'gsrlimit': 15,
+                               'gsroffset': source.gsroffset,
+                               'prop': 'imageinfo',
+                               'iiprop': 'dimensions|url',
+                               'iiurlheight': this.size
+                       },
+                       'dataType': 'jsonp'
+               } )
+                       .always( ve.bind( this.onMediaQueryAlways, this, source 
) )
+                       .done( ve.bind( this.onMediaQueryDone, this, source ) );
+       }
+};
+
+/**
+ * Handle media query response events.
+ *
+ * @method
+ * @param {Object} source Media query source
+ */
+ve.ui.MWMediaSelectWidget.prototype.onMediaQueryAlways = function ( source ) {
+       source.request = null;
+       this.input.popPending();
+};
+
+/**
+ * Handle media query load events.
+ *
+ * @method
+ * @param {Object} source Media query source
+ * @param {Object} data Media query response
+ */
+ve.ui.MWMediaSelectWidget.prototype.onMediaQueryDone = function ( source, data 
) {
+       if ( !data.query || !data.query.pages ) {
+               return;
+       }
+
+       var     page, title,
+               items = [],
+               pages = data.query.pages;
+
+       if ( data['query-continue'] && data['query-continue'].search ) {
+               source.gsroffset = data['query-continue'].search.gsroffset;
+       }
+
+       for ( page in pages ) {
+               title = pages[page].title;
+               if ( !( title in this.titles ) ) {
+                       this.titles[title] = true;
+                       items.push(
+                               new ve.ui.MWMediaSelectItemWidget(
+                                       pages[page],
+                                       { '$$': this.$$, 'size': this.size }
+                               )
+                       );
+               }
+       }
+
+       this.select.addItems( items );
+};
diff --git a/modules/ve/ui/widgets/ve.ui.TextInputWidget.js 
b/modules/ve/ui/widgets/ve.ui.TextInputWidget.js
index 4aa4263..904ede3 100644
--- a/modules/ve/ui/widgets/ve.ui.TextInputWidget.js
+++ b/modules/ve/ui/widgets/ve.ui.TextInputWidget.js
@@ -61,6 +61,16 @@
 /* Methods */
 
 /**
+ * Checks if input is pending.
+ *
+ * @method
+ * @returns {boolean} Input is pending
+ */
+ve.ui.TextInputWidget.prototype.isPending = function () {
+       return !!this.pending;
+};
+
+/**
  * Increases the pending stack.
  *
  * @method

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Ia803ff3ef518782ce76802d2dab7559686a1bb0a
Gerrit-PatchSet: 9
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Trevor Parscal <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: Trevor Parscal <[email protected]>
Gerrit-Reviewer: jenkins-bot

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

Reply via email to